2017-04-15 1 views
1

Ich mache ein Upload-Formular über eine CGI-Schnittstelle zu ändern. Ich schreibe es in C und möchte keine externen Bibliotheken (zB .cgic) verwenden.Wie STDIN-Stream auf binäre

Ich dachte, das Programm beendet war, als die ersten Testdateien korrekt hochgeladen. Aber sie waren ASCII-Dateien. Wenn ich mit einer Binärdatei (JPG) getestet habe. Es scheint, dass STDIN versucht, die Binärdaten als ASCII zu lesen, die ein Problem für Charaktere wie \0 erzeugt, die am Ende einer ASCII-Datei vorhanden ist, aber es ist ein gemeinsames Zeichen in Binärdateien. Die Ergebnisse beim Hochladen einer 1,9 MB-Datei enden mit einer 38 KB-Datei.

Bei der Suche, wie der STDIN-Stream in binäre geändert wird, wurde ich auf den Befehl freopen verwiesen und aufgefordert, NULL als Argument für die Datei zu verwenden. example 1

Dort heißt es:

Wenn Dateiname ein Nullzeiger ist, die freopen() Funktion wird versuchen, den Modus des Stroms, dass Modus angegeben zu ändern, als ob der Name der Datei derzeit mit dem Stream verbunden war verwendet worden. In diesem Fall muss der Dateideskriptor mit dem Strom verbunden ist, nicht geschlossen werden, wenn der Aufruf erfolgreich freopen(). Es ist Implementierung definiert, welche Änderungen des Modus zulässig sind (falls vorhanden), und unter welchen Umständen.

Aber wenn ich den Mann Seite auf meinem System mit man 3 freopen überprüfen, es sagt nicht, jeder von bei all diesen. Darüber hinaus in der Manpage zu lesen, finde ich für binäre die die Option aus (Hinzufügen von dem Modus ‚b‘) nicht mehr erkannt und existiert nur für archaische compliancy:

Der String-Modus kann auch enthalten der Buchstabe "b" entweder als ein letztes Zeichen oder als ein Zeichen zwischen die Zeichen in irgendeiner der oben beschriebenen zwei Zeichenfolgen. Dies ist ausschließlich für die Kompatibilität mit C89 und hat keine Wirkung; Das 'b' wird auf allen POSIX-konformen Systemen, einschließlich Linux, ignoriert.

So im Moment bin ich völlig verloren. Wie kann ich den STDIN-Stream ändern, um Binäreingabe zu lesen? Hier

ist der Code:

#include <stdio.h> 
#include <stdlib.h> 
#include <libgen.h> 
#include <string.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <unistd.h> 

// Declare constants. 
#define BUF_SIZE     4096 
#define FILENAME_SIZE    500 
#define MARKER_SIZE    100 
#define RETURN_FAILURE    0 
#define RETURN_SUCCESS    1 
#define SEARCH_STRING_1    "filename=\"" 
#define SEARCH_STRING_2    "\r\n\r\n" 

// Declare global variables. 
char filename[FILENAME_SIZE + 1]; 
char *program_name; 

// Declare function prototype. 
void print_footer (void); 
void print_header (void); 
void process_input (char *data); 

int main (int argc, char *argv[]) 
{ 
// Declare variables. 
    long long ret; 
    char buf[BUF_SIZE + 1]; 

// Get program name for error reporting. 
    program_name = basename(argv[0]); 

// Prepare output for browser. 
    print_header(); 

// Protect variable against buffer overflow. 
    buf[BUF_SIZE] = '\0'; 

// Loop through all the file data. 
    while(1) 
    { 
// Read in the next block of data. 
     if((ret = (long long) fread(buf, 1, BUF_SIZE, stdin)) != BUF_SIZE) 
     { 
// Check for error. 
      if(ferror(stdin) != 0) 
      { 
       printf("%s: An error occurred while reading the input file.<br>\n", program_name); 
       process_input(NULL); 
       exit(EXIT_FAILURE); 
      } 
// Check for EOF. 
      else if(feof(stdin) != 0) 
       break; 
     } 

// Terminate and process uploaded data. 
     buf[ret] = '\0'; 
     process_input(buf); 
    } 

// Terminate and process uploaded data. 
    buf[ret] = '\0'; 
    process_input(buf); 

// Finish user output, close output file and exit. 
    print_footer(); 
    process_input(NULL); 
    exit(EXIT_SUCCESS); 
} 

void process_input (char *data) 
{ 
// Declare variables. 
    char *ptr1= NULL; 
    char *ptr2; 
    int x = 0; 
    static FILE *fp; 
    static int flag = 0; 
    static char marker[MARKER_SIZE + 1]; 

// If data is NULL, close output file. 
    if(data == NULL) 
    { 
     if(fclose(fp) == EOF) 
     { 
      printf("%s: process_input: close failed (%s)<br>\n", program_name, strerror(errno)); 
      exit(EXIT_FAILURE); 
     } 

     return; 
    } 

// Check if this is the first time through. 
    if(flag == 0) 
    { 
// Get marker. 
     if((ptr1 = strchr(data, '\n')) == NULL) 
     { 
      printf("%s: process_input: strchr(1) failed (\n)<br>\n", program_name); 
      exit(EXIT_FAILURE); 
     } 

     ptr1[0] = '\0'; 
     strcpy(marker, data); 
     ptr1[0] = '\n'; 

// Get filename. 
     if((ptr1 = strstr(data, SEARCH_STRING_1)) == NULL) 
     { 
      printf("%s: process_input: strstr(1) failed (%s)<br>\n", program_name, SEARCH_STRING_1); 
      exit(EXIT_FAILURE); 
     } 

// Advance pointer to start of filename. 
     ptr1 += 10; 

// Find end of filename. 
     if((ptr2 = strchr(ptr1, '"')) == NULL) 
     { 
      printf("%s: process_input: strchr(2) failed (\")<br>\n", program_name); 
      exit(EXIT_FAILURE); 
     } 

// Terminate and store filename. 
     ptr2[0] = '\0'; 
     strcpy(filename, ptr1); 
     ptr2[0] = '"'; 

// Remove spaces from filename. 
     while(filename[x] != '\0') 
     { 
      if(filename[x] == ' ') 
       filename[x] = '.'; 

      x++; 
     } 

// Open output file. 
     if((fp = fopen(filename, "wb")) == NULL) 
     { 
      printf("%s: process_input: fopen failed (%s) (%s)<br>\n", program_name, strerror(errno), filename); 
      exit(EXIT_FAILURE); 
     } 

// Find start of file data. 
     if((ptr1 = strstr(data, SEARCH_STRING_2)) == NULL) 
     { 
      printf("%s: process_input: strstr(2) failed (%s)<br>\n", program_name, SEARCH_STRING_2); 
      fclose(fp); 
      exit(EXIT_FAILURE); 
     } 

// Set flag. 
     flag++; 
// Advance pointer to start of file data. 
     ptr1 += 4; 

// Change STDIN stream to binary. 
     if(freopen(NULL, "rb", stdin) == NULL) 
     { 
      printf("%s: process_input: freopen failed (%s)<br>\n", program_name, strerror(errno)); 
      fclose(fp); 
      exit(EXIT_FAILURE); 
     } 
    } 
// Catch everything else. 
    else 
    { 
     ptr1 = data; 

     if((ptr2 = strstr(ptr1, marker)) != NULL) 
      ptr2[0 - 2] = '\0'; 
    } 

// Write file data. 
    if(fwrite(ptr1, 1, strlen(ptr1), fp) != strlen(ptr1)) 
    { 
     printf("%s: process_input: write failed (%s)<br>\n", program_name, strerror(errno)); 
     fclose(fp); 
     exit(EXIT_FAILURE); 
    } 
} 

void print_footer (void) 
{ 
    printf("\nMade it!\n"); 
} 

void print_header (void) 
{ 
    printf("Content-type: text/plain\r\n\r\n"); 
} 
+0

Es ist nicht der Stream, es ist dein Code. Alle Dateihandles in Linux und anderen POSIXy-Systemen sind immer "binär"; Sie massieren den Stream-Inhalt überhaupt nicht. Sie sollten 'fread()' verwenden, um die POST-Daten zu lesen, nicht 'fgets()' oder 'getline()' oder 'getdelim()', weil POST-Daten inhärent binär sind, keine Textzeilen. –

+0

@NominalAnimal Ich benutze 'fread', um STDIN zu lesen. – Deanie

+0

Verwenden Sie 'fwrite' auch, um die entschachtelten (if of MIME type application/x-www-form-urlencoded) oder begrenzten (if of MIME type multipart/form-data) Daten in Ihre Ausgabedatei zu schreiben? Keine der CGI-Implementierungen, die ich auf POSIXy-Systemen verwendet habe, hat irgendwelche Probleme mit irgendwelchen bestimmten Zeichen, sehen Sie. Das Problem liegt definitiv in Ihrem Code - den Sie nicht anzeigen möchten - und nicht in einer Bibliotheksfunktion. –

Antwort

0

Ok, scheint es, was @NominalAnimal sagte, war richtig. Sie können Binärdaten in einer Zeichenfolge speichern, aber sobald Sie eine Funktion in der Bibliothek string.h verwenden, ändert sich fast immer, was in dieser Zeichenfolge gespeichert ist (wenn die Daten binär sind).

Die einfache Lösung besteht darin, eine separate Funktion zu erstellen, die einen Zeiger auf die binären Daten nimmt und die Suche nach Zeichenketten in dieser Funktion ausführt und die relevanten Informationen zurückgibt. Auf diese Weise werden die Originaldaten niemals geändert.

-1

'stdin' ist ein Makro von STDIN_FILENO, die auf 0. Siehe auch egal ist 'unistd.h'. Sie zeigen Ihren Code nicht an, aber ich glaube, Sie hören auf, wenn Sie auf ein '\ 0' oder ein nicht-ASCII-Zeichen stoßen, da Sie sagten, Sie hätten 'fread()' benutzt.

Sie haben zu stoppen, wenn fread() Funktion 0 zurück, was bedeutet, es zu lesen gestoppt: es EOF angetroffen.

+0

Der Name 'stdin' ist nicht wirklich mit dem POSIX-Namen STDIN_FILENO verbunden. Beide beziehen sich auf die Standardeingabe, aber auf sehr unterschiedliche Weise. –

+0

Ich glaube nicht, dass dies der Fall ist. Ich teste nie nach ''\ 0'', es basiert alles auf dem Rückgabewert von' fread' und darauf, ob die 'FEOF' und' FERROR' Flags gesetzt wurden. – Deanie