Andreas Bierhals: CGI-Skript in C/C++ - Ärger mit File-Upload (multipart/form-data)

Hallo liebe HTML'ler!

Ich versuchte seit längerem, einen File-Upload mit einem
in C++ geschriebenen CGI-Skript zum laufen zu bringen, was
mir jetzt ENDLICH gelungen ist ;-)
Damit andere nicht in die gleiche Falle laufen, poste ich
das Problem vorbeugend mal ins Forum...

Ich hatte ein Formular mittels

<form action="http://...." method="post"  enctype="multipart/form-data">

an mein CGI-prog geschickt, in dem sich u.a. auch ein
File-Button befindet, mit dem ein ASCII-File ge-uploadet
werden soll.
Mit folgendem C-Code Fragment wollte ich nun die geposteten
Daten aus der Standard-Eingabe stdin in den String 'post'
einlesen (um sie später weiterzuverarbeiten):

char *s, *post;
   int   postlen=0;
   s=getenv("CONTENT_LENGTH");

// die Anzahl der ge-post-eten Zeichen in postlen speichern...
   if (s!=NULL) postlen=atoi(s);

if (postlen>0) {
     post = new char[postlen+1];

// postlen Zeichen aus stdin in post einlesen
     fread(post, sizeof(char), postlen, stdin);  
   }

Funktionierte zunächst ohne Probleme, zumindest unter Win95 mit
dem Xitami-Server und dem OmniHTTPd.

Allerdings hat das Prog unter dem Personal Web Server und IIS von Microsoft
an dieser Stelle grundsätzlich gestreikt und verharrte in einer Art Endlosschleife.

Grund:
Die Funktion   "fread(post, sizeof(char), postlen, stdin); " liest eigentlich
"postlen" Elemente der Größe "sizeof(char)"  aus dem Eingabestrom "stdin"
in die Variable  "post" ein - sollte man meinen...

Allerdings hat diese Funktion den Haken, daß sie bei Zeilenumbrüchen,

  • bestehend aus Carriage-Return (CR) und Line-Feed (LF) - das Carriage-Return
    wegläßt, also an dieser Stelle statt 2 Bytes nur eines zurückgibt.

Will man nun aus stdin eine 1000 byte lange ASCII-Datei mit 50 Zeilenumbrüchen
lesen, so liefert fread(...) sämtliche Zeilen bereits nach 950 bytes
zurück (die fehlenden 50 bytes wurden ja durch die verkürzten Zeilenumbrüche
geschluckt).
Nun wurden aber 1000 Zeichen angefordert, aber "stdin" ist schon
nach 950 Zeichen mit seinem Latein am Ende!!

Als Resultat bleibt das Programm stehen und wartet endlos auf weitere Eingaben aus stdin,
bis der Web-Server sein Timeout erreicht und dem Programm den Gnadenstoß verpaßt.

Nachdem man dies weiß ($@§$%!!), kann man sich z.B. mit folgender Routine behelfen:

char *s, *post, *tmp;
   int   postlen=0;
   s=getenv("CONTENT_LENGTH");

// die Anzahl der ge-post-eten Zeichen in postlen speichern...
   if (s!=NULL) postlen=atoi(s);

// postlen Zeichen aus stdin in post einlesen
   if (postlen>0) {
          post = new char[postlen+1];
   tmp  = new char[postlen+1];
      strcpy(post, "");
   while(postlen>0) {
             strcpy(tmp, "");
   fgets(tmp, postlen+1, stdin);  
   postlen -= strlen(tmp)+1;      // man beachte die +1 !!!
   strcat(post, tmp);
   }
   }

die Funktion fgets(buffer, laenge, stream) liest dabei bis zum nächsten Zeilenumbruch
aber höchstens soviele Zeichen, wie in "laenge" angegeben wurde.

Viele Grüße!

Andreas

  1. Hallo Andreas,

    vielen Dank fuer Deine interessanten Hinweise!
    Ich glaube zwar, nur eine Minderheit der Forumsbesucher ist mit C/C++ vertraut, und bei mir selber leuchteten bei der Lektuere auch nur einige undeutliche Erinnerungslichter tief im Hintergrund auf, aber hilfreich sind die Infos allemal.

    Falls Du eine vollstaendige C-Routine zum File-Upload-Handling hast, kannst Du die ja gerne auch mal selber zum Download anbieten oder so...es sind immer viele Leute interessiert an so etwas, glaube ich.

    viele Gruesse
      Stefan Muenz

    1. Hallo Stefan, hallo liebe HTML'ler!

      Falls Du eine vollstaendige C-Routine zum File-Upload-Handling hast, kannst Du die ja gerne auch mal selber zum Download anbieten oder so...es sind immer viele Leute interessiert an so etwas, glaube ich.

      Ich hab zwar jetzt eine File-Upload Routine, die auch unter Microsoft-Servern
      zu funktionieren scheint. Allerdings weiß ich noch nicht, was jetzt
      unter anderen Servern/Betriebssystemen passiert, wenn die Zeilenumbrüche
      wieder anders gehandhabt werden.
      Vermutlich existieren dazu sowieso schon
      allgemeingültigere Routinen zum Download auf diversen Servern...
      Wenn aber nach mehreren Tests unter verschiedenen Servern/Konfigurationen
      alles zu funktionieren scheint, werde ich die Routine posten
      oder zum Download bereitlegen...

      Viele Grüße und frohe Festtage!

      Andreas