"Fossies" - the Fresh Open Source Software Archive

Member "libgcgi.a-0.9.5/src/mime.c" (22 Jun 2002, 18021 Bytes) of package /linux/www/old/gcgi-0.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mime.c" see the Fossies "Dox" file reference documentation.

    1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
    2 /*
    3  * GCGI Library, implementing NCSA'a Common Gateway Interface and RFC2338.
    4  * Copyright (C) 2001-2002 Julian Catchen, julian@catchen.org
    5  *
    6  * This library is free software; you can redistribute it and/or
    7  * modify it under the terms of the GNU Lesser General Public
    8  * License as published by the Free Software Foundation; either
    9  * version 2.1 of the License, or (at your option) any later version.
   10  *
   11  * This library is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14  * Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with this library; if not, write to the Free Software
   18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   19  */
   20 
   21 #include "mime.h"
   22 
   23 static const char* const mimeStrTypes[] = {
   24     "text", 
   25     "image", 
   26     "audio", 
   27     "video", 
   28     "application", 
   29     "multipart", 
   30     "message", 
   31     "unknown"
   32 };
   33 
   34 static const char* const mimeStrEncodings[] = {
   35     "7bit", 
   36     "8bit", 
   37     "binary", 
   38     "quoted-printable", 
   39     "base64", 
   40     "unknown"
   41 };
   42 
   43 
   44 void 
   45 mimeCreateMimePart(MimePart **part)
   46 {
   47     *part = XMALLOC(MimePart, 1);
   48 
   49     (*part)->version     = NULL;
   50     (*part)->type        = unknown;
   51     (*part)->subtype     = NULL;
   52     (*part)->encoding    = unknown;
   53     (*part)->boundary    = NULL;
   54     (*part)->name        = NULL;
   55     (*part)->filename    = NULL;
   56     (*part)->charset     = NULL;
   57     (*part)->body        = NULL;
   58     (*part)->bodylen     = 0;
   59     (*part)->bodysize    = 0;
   60     (*part)->truncated   = 0;
   61     (*part)->next        = NULL;
   62     (*part)->description = NULL;
   63     (*part)->description = NULL;
   64 }
   65 
   66 
   67 void 
   68 mimeFreeMimePart(MimePart *part)
   69 {
   70     if (part->version     != NULL) XFREE(part->version);
   71     if (part->subtype     != NULL) XFREE(part->subtype);
   72     if (part->boundary    != NULL) XFREE(part->boundary);
   73     if (part->name        != NULL) XFREE(part->name);
   74     if (part->filename    != NULL) XFREE(part->filename);
   75     if (part->charset     != NULL) XFREE(part->charset);
   76     if (part->body        != NULL) XFREE(part->body);
   77     if (part->description != NULL) XFREE(part->description);
   78   
   79     XFREE(part);
   80 }
   81 
   82 
   83 MimePart *
   84 mimeParseMimeMessage(FILE *mimeIn, size_t mimeLimit, size_t mimePartLimit)
   85 {
   86     MimePart      *mime, *part, *cur;
   87     CRLFStringBuf *sbuf;
   88     char          *sboundary, *eboundary, *buffer;
   89     int            blen;
   90 
   91     mimeCreateMimePart(&mime);
   92 
   93     sbuf = crlfsbuf_new(3, mimeIn, mimePartLimit, mimeLimit);
   94 
   95     mimeParseHeadersFromEnv(mime);
   96     cur = mime;
   97 
   98     blen = strlen(mime->boundary);
   99     sboundary = XMALLOC(char, blen + 3);
  100     eboundary = XMALLOC(char, blen + 5);
  101     sprintf(sboundary, "--%s", mime->boundary);
  102     sprintf(eboundary, "--%s--", mime->boundary);
  103     
  104     buffer = crlfsbuf_read(sbuf);
  105     while ((strncmp(buffer,eboundary,strlen(eboundary)) != 0) && buffer != NULL) {
  106     if (! strncmp(buffer,sboundary,strlen(sboundary))) {
  107         mimeCreateMimePart(&part);
  108         mimeParseHeaders(sbuf, part);
  109 
  110         /* Forward to the MIME-part body. */
  111         do {
  112         buffer = crlfsbuf_read(sbuf);
  113         } while (strncmp(buffer, "\r\n", 2) != 0 && buffer != NULL);
  114 
  115         mimeParseMimeBody(sbuf, sboundary, eboundary, part, mimePartLimit, mimeLimit);
  116 
  117         cur->next = part;
  118         cur = cur->next;
  119     }
  120 
  121     /* Have we surpassed the MIME message limit? */
  122     if (mimeLimit > 0 && crlfsbuf_truncated(sbuf)) {
  123         mime->truncated++;
  124         break;
  125     }
  126 
  127     buffer = crlfsbuf_read(sbuf);
  128     }
  129 
  130     XFREE(sboundary);
  131     XFREE(eboundary);
  132 
  133     crlfsbuf_free(sbuf);
  134 
  135     return mime;
  136 }
  137 
  138 
  139 extern void      
  140 mimeFreeMimeMessage(MimePart *mime)
  141 {
  142     MimePart *m, *n;
  143   
  144     m = mime;
  145     n = m->next;
  146 
  147     while (n != NULL) {
  148     mimeFreeMimePart(m);
  149     m = n;
  150     n = n->next;
  151     }
  152     mimeFreeMimePart(m);
  153 }
  154 
  155 
  156 void 
  157 mimeParseHeaders(CRLFStringBuf *mimeIn, MimePart *part)
  158 {
  159     char      *type, *subtype, *encoding, *name, *boundary;
  160     char      *filename, *desc, *disp, *dname, *charset, *format;
  161     char      *buf;
  162     int        size, endOfHeaders;
  163     MimeHeader header;
  164 
  165     type = subtype = encoding = name = boundary = NULL;
  166     format = filename = desc = disp = dname = charset = NULL;
  167 
  168     do {
  169     endOfHeaders = mimeReturnHeader(mimeIn, &buf, &size);
  170     header = mimeHeaderName(buf);
  171     
  172     switch (header) {
  173     case contentType:
  174         mimeParseContentType(buf, &type, &subtype, &charset, &name, &boundary, &format);
  175         break;
  176     case contentEncoding:
  177         mimeParseContentTransferEncoding(buf, &encoding);
  178         break;
  179     case contentDisposition:
  180         mimeParseContentDisposition(buf, &disp, &filename, &dname);
  181         break;
  182     case contentDescription:
  183         mimeParseContentDescription(buf, &desc);
  184         break;
  185     default:
  186         break;
  187     } 
  188 
  189     XFREE(buf);
  190     size = 0;
  191 
  192     } while (endOfHeaders >= 0);
  193 
  194     mimeFillMimeStruct(part, type, subtype, encoding, name, boundary, filename, desc, 
  195                disp, dname, charset, format);
  196 }
  197 
  198 
  199 void
  200 mimeParseHeadersFromEnv(MimePart *part)
  201 {
  202     char *buf, *type, *subtype, *encoding, *name, *boundary, *env;
  203     char *filename, *desc, *disp, *dname, *charset, *format;
  204     int   len;
  205 
  206     type = subtype = encoding = name = boundary = NULL;
  207     format = filename = desc = disp = dname = charset = NULL;
  208 
  209     env = gcgiFetchEnvVar(gcgiContentType);
  210     len = strlen(env);
  211     buf = XMALLOC(char, len + 17);
  212     snprintf(buf, len+16, "Content-Type: %s\r\n", env);
  213     buf[len+16] = '\0';
  214 
  215     mimeParseContentType(buf, &type, &subtype, &charset, &name, &boundary, &format);
  216 
  217     mimeFillMimeStruct(part, type, subtype, encoding, name, boundary, filename, desc, 
  218                disp, dname, charset, format);
  219 
  220     XFREE(buf);
  221 }
  222 
  223 void
  224 mimeFillMimeStruct(MimePart *part, char *type, char *subtype, char *encoding, 
  225            char *name, char *boundary, char *filename, char *desc, 
  226            char *disp, char *dname, char *charset, char *format)
  227 {
  228     /* Assume ascii text when no "Content-Type" header present. */
  229     if (type == NULL) {
  230     part->type = text;
  231     subtype = XMALLOC(char, 6);
  232     strcpy(subtype,"plain");
  233     }
  234     else if (strncasecmp("text", type, strlen(type)) == 0) 
  235     part->type = text;
  236     else if (strncasecmp("image", type, strlen(type)) == 0)
  237     part->type = image;
  238     else if (strncasecmp("audio", type, strlen(type)) == 0)
  239     part->type = audio;  
  240     else if (strncasecmp("video", type, strlen(type)) == 0)
  241     part->type = video;  
  242     else if (strncasecmp("application", type, strlen(type)) == 0)
  243     part->type = application;  
  244     else if (strncasecmp("multipart", type, strlen(type)) == 0)
  245     part->type = multipart;  
  246     else if (strncasecmp("message", type, strlen(type)) == 0)
  247     part->type = message;  
  248     else
  249     part->type = unknown;
  250 
  251     /* Assume the format is fixed when no "Content-Type" header present. */
  252     if (format == NULL) 
  253     part->format = fixed;
  254     else if (strncasecmp("fixed", format, strlen(format)) == 0) 
  255     part->format = fixed;
  256     else if (strncasecmp("flowed", format, strlen(format)) == 0)
  257     part->format = flowed;
  258     else 
  259     part->format = fixed;
  260 
  261     /* Assume 7Bit encoding when no "Content-Transfer-Encoding" header present. */
  262     if (encoding == NULL) {
  263     type == text ? (part->encoding = sevenbit) : (part->encoding = binary);
  264     }
  265     else if (strncasecmp("7bit",encoding,strlen(encoding)) == 0) 
  266     part->encoding = sevenbit;
  267     else if (strncasecmp("8bit",encoding,strlen(encoding)) == 0)
  268     part->encoding = eightbit;
  269     else if (strncasecmp("binary",encoding,strlen(encoding)) == 0)
  270     part->encoding = binary;  
  271     else if (strncasecmp("quoted-printable",encoding,strlen(encoding)) == 0)
  272     part->encoding = quotedprintable;  
  273     else if (strncasecmp("base64",encoding,strlen(encoding)) == 0)
  274     part->encoding = basesixtyfour;
  275     else
  276     part->encoding = sevenbit;         
  277     
  278     if (disp == NULL) 
  279     part->disposition = attachment;
  280     else if (strncasecmp("inline", disp, strlen(disp)) == 0) 
  281     part->disposition = inlined;
  282     else if (strncasecmp("attachment", disp, strlen(disp)) == 0)
  283     part->disposition = attachment;
  284     else if (strncasecmp("form-data", disp, strlen(disp)) == 0)
  285     part->disposition = formdata;
  286     else
  287     part->disposition = attachment;        
  288 
  289     if (subtype != NULL)    part->subtype = subtype;
  290     if (filename != NULL)   part->filename = filename;
  291     if (name != NULL)       part->name = name;
  292     else if (dname != NULL) part->name = dname;
  293     if (desc != NULL)       part->description = desc;
  294     if (charset != NULL)    part->charset = charset;
  295     if (boundary != NULL)   part->boundary = boundary;
  296 
  297     if (type != NULL)       XFREE(type);
  298     if (format != NULL)     XFREE(format);
  299     if (disp != NULL)       XFREE(disp);
  300 }
  301 
  302 
  303 void 
  304 mimeParseContentType(char *text, char **type, char **subtype, 
  305              char **charset, char **name, char **boundary, char **format)
  306 {
  307     char  *p, *q;
  308     char **tokens;
  309     char  *property, *value;
  310     int    tlen;
  311     int    i;
  312 
  313     *type = *subtype = *charset = *name = *boundary = *format = NULL;
  314     text? (tlen  = strlen(text)) : (tlen = 0);
  315 
  316     tokenizeString(text, tlen, &tokens);
  317 
  318     parseToken(tokens[1], &property, &value);
  319 
  320     /* Figure out the type of message, example: Content-Type: text/html */
  321     for (p = q = value; *p != '/' && *p != '\0'; p++);
  322     *type = XMALLOC(char, p - q + 1);
  323     strncpy(*type, q, p - q);
  324     (*type)[p-q] = '\0';
  325     
  326     /* Figure out the subtype */
  327     for (q = p; *q != '\0'; q++);
  328     *subtype = XMALLOC(char, q - p + 1);
  329     strncpy(*subtype, p + 1, q - p);
  330     (*subtype)[q-p] = '\0';
  331 
  332     XFREE(value);
  333 
  334     for (i = 2; tokens[i] != '\0'; i++) {
  335     parseToken(tokens[i], &property, &value);
  336     
  337     if (property == NULL)
  338         XFREE(value);
  339 
  340     else if (!strncasecmp(property, "charset", 7))
  341         *charset = value;
  342 
  343     else if (!strncasecmp(property, "name", 4))
  344         *name = value;
  345 
  346     else if (!strncasecmp(property, "boundary", 8))
  347         *boundary = value;
  348 
  349     else if (!strncasecmp(property, "format", 6))
  350         *format = value;
  351 
  352     else
  353         XFREE(value);
  354 
  355     XFREE(property);
  356     }
  357 
  358     freeStringArray(tokens);
  359 }
  360 
  361 
  362 void 
  363 mimeParseContentTransferEncoding(char *text, char **encoding)
  364 {
  365     char **tokens;
  366     char  *property, *value;
  367     int    tlen;
  368 
  369     *encoding = NULL;
  370     text? (tlen = strlen(text)) : (tlen = 0);
  371 
  372     tokenizeString(text, tlen, &tokens);
  373 
  374     parseToken(tokens[1], &property, &value);
  375     (*encoding) = value;
  376 }
  377 
  378 
  379 void 
  380 mimeParseContentDisposition(char *text, char **disp, char **filename, char **name)
  381 {
  382     int   tlen;
  383     int   i;
  384     char  *property, *value, *p;
  385     char **tokens;
  386 
  387     *disp = *filename = *name = NULL;
  388     text? (tlen = strlen(text)) : (tlen = 0);
  389 
  390     tokenizeString(text, tlen, &tokens);
  391 
  392     parseToken(tokens[1], &property, &value);
  393     (*disp) = value;
  394 
  395     for (i = 2; tokens[i] != '\0'; i++) {
  396     parseToken(tokens[i], &property, &value);
  397 
  398     if (property == NULL)
  399         XFREE(value);
  400     
  401     if (!strncasecmp(property, "filename", 8))
  402         *filename = value;
  403 
  404     else if (!strncasecmp(property, "name", 4))
  405         *name = value;
  406 
  407     else
  408         XFREE(value);
  409 
  410     XFREE(property);
  411     }
  412 
  413     /* 
  414        Some browsers, like MS Internet Explorer send the entire path
  415        for the file.  The standard states that the only the filename should
  416        be included, so we check for the full path and remove it if present.
  417     */
  418     if (*filename && strlen(*filename) > 0) {
  419     /* Forward to the end of the string. */
  420     for (p = *filename; *p != '\0'; p++);
  421     /* Iterate backwards until a backslash or forward slash or the beginning of the string is found. */
  422     for (; *p != '\\' && *p != '/' && p > *filename; p--);
  423     if (*p == '\\' || *p == '/') {
  424         strcpy(*filename, p+1);
  425     }
  426     }
  427 
  428     freeStringArray(tokens);
  429 }
  430 
  431 
  432 void 
  433 mimeParseContentDescription(char *text, char **desc)
  434 {
  435     char *p, *q, *offset;
  436     int   tlen;
  437 
  438     *desc = NULL;
  439     text? (tlen = strlen(text)) : (tlen = 0);
  440     offset = text + tlen;
  441 
  442     for (q = text; *q != '\r' && q < offset; p++);
  443     
  444     for (p = text; isspace((int)*p); p++);  /* Remove whitespace from beggining of string. */
  445     *desc = XMALLOC(char, q - p + 1);
  446     strncpy(*desc, q, p-q);
  447     (*desc)[p-q] = 0;
  448 }
  449 
  450 
  451 size_t 
  452 mimeParseMimeBody(CRLFStringBuf *mimeIn, char *sboundary, char *eboundary, 
  453           MimePart *part, size_t mimePartLimit, size_t mimeLimit)
  454 {
  455     char  *line, *peek;
  456     int    i, endOfPart;
  457     int    llen, size;
  458 
  459     /* This message is MIME encoded, look for s/eboundary as end of Message. */
  460     endOfPart = 0;
  461     
  462     part->bodylen  = 0;
  463     part->bodysize = 256;
  464     part->body     = XMALLOC(char, part->bodysize);
  465     memset(part->body, 0, part->bodysize);
  466 
  467     do {
  468         crlfsbuf_readdata(mimeIn, &line, &size, &llen);
  469 
  470         /* Check if we have reached the end of the MIME body. */
  471     peek = crlfsbuf_peek(mimeIn);
  472         if ((strncmp(peek, eboundary, strlen(eboundary)) == 0) || 
  473         (strncmp(peek, sboundary, strlen(sboundary)) == 0))
  474             endOfPart++;
  475     
  476     /* If reading the previous line pushes us over the limit,  *
  477      * only copy enough into the MIME part to equal the limit. */
  478     if (mimePartLimit > 0 && (part->bodylen + llen) >= mimePartLimit) {
  479         llen -= (part->bodylen + llen) - mimePartLimit;
  480     }
  481 
  482     if (!part->truncated) {
  483         if (part->bodylen + llen <= part->bodysize - 1) {
  484         memcpy(part->body + part->bodylen, line, llen);
  485         part->bodylen += llen;
  486         }
  487         else {
  488         part->bodysize = (part->bodylen + llen) * 2;
  489         part->body     = XREALLOC(char, part->body, part->bodysize);
  490         memcpy(part->body + part->bodylen, line, llen);
  491         part->bodylen += llen;
  492         }
  493     }
  494 
  495     /* If the input was too long and truncated, read *
  496      * and discard the rest of the MIME part.        */
  497     if (mimePartLimit > 0 && part->bodylen >= mimePartLimit)
  498         part->truncated++;
  499 
  500     /* If the entire MIME input was greater than the limit, exit. */
  501     if (mimeLimit > 0 && crlfsbuf_truncated(mimeIn)) {
  502         part->truncated++;
  503         break;
  504     }
  505 
  506     } while (!endOfPart && line != NULL);
  507     
  508     /* If binary encoded, remove the final CRLF that seperates the MIME boundary. */
  509     if (part->encoding == binary || part->encoding == unknown) {
  510     if (part->body[part->bodylen - 1] == '\n' && part->body[part->bodylen - 2] == '\r') {
  511         part->body[part->bodylen - 1] = '\0';
  512         part->body[part->bodylen - 2] = '\0';
  513         part->bodylen -= 2;
  514     }
  515     }
  516     /* Else, if not text, but encoded, remove CRLF endings. */
  517     else if (part->encoding == basesixtyfour && part->encoding == quotedprintable) {
  518     i = removePat(part->body, "\r\n", part->bodylen);
  519     part->bodylen -= i*2;
  520     }
  521 
  522     return part->bodylen;
  523 }
  524 
  525 
  526 MimeHeader
  527 mimeHeaderName(char *text)
  528 {
  529     MimeHeader htype;
  530 
  531     htype = unknownHeader;
  532 
  533     switch (text[0]) {
  534     case 'c':
  535     case 'C':
  536     if (strncasecmp("Content-Type: ", text, 14) == 0)
  537         htype = contentType;
  538 
  539     else if (strncasecmp("Content-Transfer-Encoding: ", text, 27) == 0)
  540         htype = contentEncoding;
  541 
  542     else if (strncasecmp("Content-Disposition: ", text, 21) == 0)
  543         htype = contentDisposition;
  544 
  545     else if (strncasecmp("Content-Description: ", text, 21) == 0)
  546         htype = contentDescription;
  547     break;
  548     case 'm':
  549     case 'M':
  550     if (strncasecmp("Mime-Version: ", text, 14) == 0)
  551         htype = mimeVersion;
  552     break;
  553     default:
  554     htype = unknownHeader;
  555     }
  556 
  557     return htype;
  558 }
  559 
  560 
  561 /* Returns the next header from the incomming stream. */
  562 int 
  563 mimeReturnHeader(CRLFStringBuf *mimeIn, char **text, int *size)
  564 {
  565     int   lsize;
  566     int   llen, tlen;
  567     char  *line, *peek;
  568     int   endOfHeaders;
  569     
  570     llen  = 0;
  571     lsize = 0;
  572     
  573     tlen  = 0;
  574     *size = 256;
  575     *text = XMALLOC(char, *size);
  576     memset(*text, 0, *size);
  577 
  578     endOfHeaders = 0;
  579 
  580     do {
  581         line = crlfsbuf_read(mimeIn);
  582 
  583         /* Check if we have reached the end of the headers. */
  584     peek = crlfsbuf_peek(mimeIn);
  585         if (peek[0] == '\r' && peek[1] == '\n' && peek[2] == '\0')
  586             endOfHeaders++;
  587     
  588     llen = strlen(line);
  589     if (tlen + llen <= *size - 1) {
  590         strcat(*text, line);
  591         tlen += llen;
  592     }
  593     else {
  594         *size  = (*size+lsize) * 2;
  595         tlen  += llen;
  596         *text  = XREALLOC(char, *text, *size);
  597         strcat(*text, line);
  598     }
  599     } while (!endOfHeaders && isspace((int)peek[0]) && line != NULL);
  600 
  601     replacePat(text, "\r\n", " ", size);
  602 
  603     if (endOfHeaders || line == NULL)
  604         return -1;
  605     else 
  606         return 0;
  607 }
  608 
  609 int 
  610 mimeReturnHeaderByName(FILE *mimeIn, char *header, char **text, int *size)
  611 {
  612     off_t pos;
  613     int   hsize, lsize;
  614     int   llen, tlen;
  615     char  *line;
  616     int   endOfHeaders, foundHeader;
  617     
  618     pos   = ftell(mimeIn);
  619     header? (hsize = strlen(header)) : (hsize = 0);
  620 
  621     llen  = 0;
  622     lsize = 256;
  623     line  = XMALLOC(char, lsize);
  624     memset(line, 0, lsize);
  625 
  626     tlen  = 0;
  627     *size = 256;
  628     *text = XMALLOC(char, *size);
  629     memset(*text, 0, *size);
  630 
  631     endOfHeaders = 0;
  632     foundHeader  = 0;
  633 
  634     do {
  635         readCRLFLine(mimeIn, &line, &lsize);
  636 
  637         /* Check if we have reached the end of the headers. */
  638         if (line[0] == '\r' && line[1] == '\n' && line[2] == '\0')
  639             endOfHeaders++;
  640 
  641         /* Found the header. */
  642         if (strncasecmp(line, header, hsize) == 0 || foundHeader) {
  643 
  644             if (foundHeader && !isspace((int)line[0]))
  645                 break;
  646 
  647             foundHeader++;
  648 
  649             llen = strlen(line);
  650             if (tlen + llen <= *size - 1) {
  651                 strcat(*text, line);
  652                 tlen += llen;
  653             }
  654             else {
  655                 *size  = (*size+lsize) * 2;
  656                 tlen  += llen;
  657                 *text  = XREALLOC(char, *text, *size);
  658                 strcat(*text, line);
  659             }
  660         }
  661     } while (!endOfHeaders && !feof(mimeIn));
  662 
  663     replacePat(text, "\r\n", " ", size);
  664     replacePat(text, header, "", size);
  665 
  666     /* Put the file pointer back where we got it. */
  667     fseek(mimeIn, pos - ftell(mimeIn), SEEK_CUR);
  668 
  669     XFREE(line);
  670 
  671     if (endOfHeaders && !foundHeader) 
  672         return -1;
  673     else 
  674         return 0;
  675 }