2017-04-15 1 views
1

Je crée un formulaire de téléchargement via une interface CGI. Je l'écris en C et je ne veux pas utiliser de bibliothèques externes (ie cgic).Comment modifier le flux STDIN en binaire

Je pensais que le programme était complet, car les premiers fichiers de test téléchargés correctement. Mais ils étaient des fichiers ASCII. Quand j'ai testé avec un fichier binaire (JPG). Il semble que STDIN essaie de lire les données binaires en ASCII ce qui crée un problème pour les caractères comme \0 qui est présent à la fin d'un fichier ASCII, mais qui est un caractère commun dans les fichiers binaires. Les résultats du téléchargement d'un fichier de 1,9 Mo finissent avec un fichier de 38 Ko.

Lors de la recherche sur la façon de changer le flux STDIN en binaire, on m'a renvoyé à la commande freopen et on m'a dit d'utiliser NULL comme argument pour le fichier. example 1

Il dit:

Si le nom de fichier est un pointeur NULL, la fonction freopen() tente de changer le mode du flux à celui spécifié par le mode, comme si le nom du fichier actuellement associé au flux a été utilisé. Dans ce cas, le descripteur de fichier associé au flux ne doit pas être être fermé si l'appel à freopen() réussit. Il est défini par l'implémentation quels changements de mode sont autorisés (le cas échéant), et dans quelles circonstances.

Mais quand je vérifie la page de manuel sur mon système avec man 3 freopen, il ne dit pas de du tout. De plus, la lecture de la page de manuel, je trouve la l'option pour binaire (en ajoutant « b » au mode) est plus reconnu et existe seulement pour compliancy archaïque:

La chaîne de mode peut également inclure la lettre «b» en tant que dernier caractère ou en tant que caractère entre les caractères de l'une des chaînes de caractères à deux caractères décrites ci-dessus. Ceci est strictement pour la compatibilité avec C89 et n'a aucun effet; le 'b' est ignoré sur tous les systèmes conformes POSIX, y compris Linux.

Donc en ce moment je suis complètement perdu. Comment puis-je changer le flux STDIN pour lire l'entrée binaire?

Voici le 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

Ce n'est pas le flux, c'est votre code. Tous les handles de fichiers sous Linux et autres systèmes POSIXy sont toujours "binaires"; ils ne massent pas le contenu du flux. Vous devriez utiliser 'fread()' pour lire les données POST, pas 'fgets()' ou 'getline()' ou 'getdelim()', car les données POST sont intrinsèquement binaires, pas les lignes de texte. –

+0

@NominalAnimal J'utilise 'fread' pour lire STDIN. – Deanie

+0

Utilisez-vous également 'fwrite' pour écrire les données de type échappement-échappé (si application de type MIME/x-www-form-urlencoded) ou délimité (si de type MIME multipart/form-data) dans votre fichier de sortie? Aucune des implémentations CGI que j'ai utilisées sur les systèmes POSIXy n'a de problèmes avec des caractères spécifiques, voyez-vous. Le problème est définitivement dans votre code - que vous avez choisi de ne pas montrer -, pas dans une fonction de bibliothèque. –

Répondre

0

Ok, il semble que ce que @NominalAnimal disait était correct. Vous pouvez stocker des données binaires dans une chaîne, mais au moment où vous utilisez n'importe quelle fonction dans la bibliothèque string.h, cela modifie presque toujours ce qui est stocké dans cette chaîne (si les données sont binaires).

La solution facile consiste à créer une fonction séparée qui pointe sur les données binaires et effectue vos recherches de chaînes dans cette fonction, en renvoyant les informations pertinentes nécessaires. De cette façon, les données d'origine ne sont jamais modifiées.

-1

'stdin' est une macro de STDIN_FILENO, qui est egal à 0. Voir aussi 'unistd.h'. Vous ne montrez pas votre code, mais je pense que vous arrêtez quand vous rencontrez un '\ 0' ou un caractère non-ascii, puisque vous avez dit que vous utilisiez 'fread()'.

Vous devez vous arrêter lorsque la fonction fread() renvoie 0, ce qui signifie qu'elle s'est arrêtée pour lire: elle a rencontré EOF.

+0

Le nom 'stdin' n'est pas vraiment lié au nom POSIX STDIN_FILENO. Ils se réfèrent tous les deux à l'entrée standard, mais de manière très différente. –

+0

Je ne crois pas que ce soit le cas. Je ne teste jamais pour ''\ 0' ', tout est basé sur la valeur de retour de' fread' et si les drapeaux 'FEOF' et' FERROR' ont été définis. – Deanie