Back to index

libcitadel  8.12
mime_parser.c
Go to the documentation of this file.
00001 /*
00002  * This is the MIME parser for Citadel.
00003  *
00004  * Copyright (c) 1998-2010 by the citadel.org development team.
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00019  */
00020 
00021 #include <stdlib.h>
00022 #include <unistd.h>
00023 #include <stdio.h>
00024 #include <signal.h>
00025 #include <sys/types.h>
00026 #include <ctype.h>
00027 #include <string.h>
00028 #include <sys/stat.h>
00029 #include <sys/types.h>
00030 #include <dirent.h>
00031 #include <errno.h>
00032 
00033 #include "xdgmime/xdgmime.h"
00034 #include "libcitadel.h"
00035 #include "libcitadellocal.h"
00036 
00037 const unsigned char FromHexTable [256] = {
00038        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //  0
00039        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 10
00040        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20
00041        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 30
00042        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, // 40
00043        0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, // 50
00044        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 60
00045        0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 70
00046        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 80
00047        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, // 90
00048        0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //100
00049        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //110
00050        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //120
00051        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //130
00052        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //140
00053        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //150
00054        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //160
00055        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //170
00056        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //180
00057        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //190
00058        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //200
00059        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //210
00060        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //220
00061        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //230
00062        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //240
00063        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF                          //250
00064 };
00065 
00066 
00067 long extract_key(char *target, char *source, long sourcelen, char *key, long keylen, char KeyEnd)
00068 {
00069        char *sptr, *ptr = NULL;
00070        int double_quotes = 0;
00071        long RealKeyLen = keylen;
00072 
00073        sptr = source;
00074 
00075        while (sptr != NULL)
00076        {
00077               ptr = bmstrcasestr_len(sptr, sourcelen - (sptr - source), 
00078                                    key, keylen);
00079               if(ptr != NULL)
00080               {
00081                      while (isspace(*(ptr + RealKeyLen)))
00082                             RealKeyLen ++;
00083                      if (*(ptr + RealKeyLen) == KeyEnd)
00084                      {
00085                             sptr = NULL;
00086                             RealKeyLen ++;                            
00087                      }
00088                      else
00089                      {
00090                             sptr = ptr + RealKeyLen + 1;
00091                      }
00092               }
00093               else 
00094                      sptr = ptr;
00095        }
00096        if (ptr == NULL) {
00097               *target = '\0';
00098               return 0;
00099        }
00100        strcpy(target, (ptr + RealKeyLen));
00101 
00102        for (ptr=target; (*ptr != 0); ptr++) {
00103 
00104               /* A semicolon means we've hit the end of the key, unless we're inside double quotes */
00105               if ( (double_quotes != 1) && (*ptr == ';')) {
00106                      *ptr = 0;
00107               }
00108 
00109               /* if we find double quotes, we've got a great set of string boundaries */
00110               if (*ptr == '\"') {
00111                      ++double_quotes;
00112                      if (double_quotes == 1) {
00113                             strcpy(ptr, ptr+1);
00114                      }
00115                      else {
00116                             *ptr = 0;
00117                      }
00118               }
00119        }
00120        *ptr = '\0';
00121        return ptr - target;
00122 }
00123 
00124 
00125 /*
00126  * For non-multipart messages, we need to generate a quickie partnum of "1"
00127  * to return to callback functions.  Some callbacks demand it.
00128  */
00129 char *fixed_partnum(char *supplied_partnum) {
00130        if (supplied_partnum == NULL) return "1";
00131        if (strlen(supplied_partnum)==0) return "1";
00132        return supplied_partnum;
00133 }
00134 
00135 
00136 static inline unsigned int _decode_hex(const char *Source)
00137 {
00138        int ret = '?';
00139        unsigned char LO_NIBBLE;
00140        unsigned char HI_NIBBLE;
00141 
00142        HI_NIBBLE = FromHexTable[(unsigned char) *Source];
00143        LO_NIBBLE = FromHexTable[(unsigned char) *(Source+1)];
00144        
00145        if ((LO_NIBBLE == 0xFF) || (LO_NIBBLE == 0xFF))
00146               return ret;
00147        ret = HI_NIBBLE;
00148        ret = ret << 4;
00149        ret = ret | LO_NIBBLE;
00150        return ret;
00151 }
00152 
00153 unsigned int decode_hex(char *Source) {return _decode_hex(Source);}
00154 
00155 /*
00156  * Convert "quoted-printable" to binary.  Returns number of bytes decoded.
00157  * according to RFC2045 section 6.7
00158  */
00159 int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen) {
00160        unsigned int ch;
00161        int decoded_length = 0;
00162        int pos = 0;
00163 
00164        while (pos < sourcelen)
00165        {
00166               if (*(encoded + pos) == '=')
00167               {
00168                      pos ++;
00169                      if (*(encoded + pos) == '\n')
00170                      {
00171                             pos ++;
00172                      }
00173                      else if (*(encoded + pos) == '\r')
00174                      {
00175                             pos ++;
00176                             if (*(encoded + pos) == '\n')
00177                                    pos++;
00178                      }
00179                      else
00180                      {
00181                             ch = _decode_hex(&encoded[pos]);
00182                             pos += 2;
00183                             decoded[decoded_length++] = ch;
00184                      }
00185               }
00186               else
00187               {
00188                      decoded[decoded_length++] = encoded[pos];
00189                      pos += 1;
00190               }
00191        }
00192        decoded[decoded_length] = 0;
00193        return(decoded_length);
00194 }
00195 
00196 
00197 /*
00198  * Given a message or message-part body and a length, handle any necessary
00199  * decoding and pass the request up the stack.
00200  */
00201 void mime_decode(char *partnum,
00202                char *part_start, size_t length,
00203                char *content_type, char *charset, char *encoding,
00204                char *disposition,
00205                char *id,
00206                char *name, char *filename,
00207                MimeParserCallBackType CallBack,
00208                MimeParserCallBackType PreMultiPartCallBack,
00209                MimeParserCallBackType PostMultiPartCallBack,
00210                void *userdata,
00211                int dont_decode)
00212 {
00213 
00214        char *decoded;
00215        size_t bytes_decoded = 0;
00216 
00217        /* Some encodings aren't really encodings */
00218        if (!strcasecmp(encoding, "7bit"))
00219               strcpy(encoding, "");
00220        if (!strcasecmp(encoding, "8bit"))
00221               strcpy(encoding, "");
00222        if (!strcasecmp(encoding, "binary"))
00223               strcpy(encoding, "");
00224        if (!strcasecmp(encoding, "ISO-8859-1"))
00225               strcpy(encoding, "");
00226 
00227        /* If this part is not encoded, send as-is */
00228        if ( (strlen(encoding) == 0) || (dont_decode)) {
00229               if (CallBack != NULL) {
00230                      CallBack(name, 
00231                              filename, 
00232                              fixed_partnum(partnum),
00233                              disposition, 
00234                              part_start,
00235                              content_type, 
00236                              charset, 
00237                              length, 
00238                              encoding, 
00239                              id,
00240                              userdata);
00241                      }
00242               return;
00243        }
00244        
00245        /* Fail silently if we hit an unknown encoding. */
00246        if ((strcasecmp(encoding, "base64"))
00247            && (strcasecmp(encoding, "quoted-printable"))) {
00248               return;
00249        }
00250 
00251        /*
00252         * Allocate a buffer for the decoded data.  The output buffer is slightly
00253         * larger than the input buffer; this assumes that the decoded data
00254         * will never be significantly larger than the encoded data.  This is a
00255         * safe assumption with base64, uuencode, and quoted-printable.
00256         */
00257        decoded = malloc(length + 32768);
00258        if (decoded == NULL) {
00259               return;
00260        }
00261 
00262        if (!strcasecmp(encoding, "base64")) {
00263               bytes_decoded = CtdlDecodeBase64(decoded, part_start, length);
00264        }
00265        else if (!strcasecmp(encoding, "quoted-printable")) {
00266               bytes_decoded = CtdlDecodeQuotedPrintable(decoded, part_start, length);
00267        }
00268 
00269        if (bytes_decoded > 0) if (CallBack != NULL) {
00270                      char encoding_buf[SIZ];
00271 
00272                      strcpy(encoding_buf, "binary");
00273                      CallBack(name, 
00274                              filename, 
00275                              fixed_partnum(partnum),
00276                              disposition, 
00277                              decoded,
00278                              content_type, 
00279                              charset, 
00280                              bytes_decoded, 
00281                              encoding_buf, 
00282                              id, 
00283                              userdata);
00284        }
00285 
00286        free(decoded);
00287 }
00288 
00289 /*
00290  * this is the extract of mime_decode which can be called if 'dont_decode' was set; 
00291  * to save the cpu intense process of decoding to the time when it realy wants the content. 
00292  * returns: 
00293  *   - > 0 we decoded something, its on *decoded, you need to free it.
00294  *   - = 0 no need to decode stuff. *decoded will be NULL.
00295  *   - < 0 an error occured, either an unknown encoding, or alloc failed. no need to free.
00296  */
00297 int mime_decode_now (char *part_start, 
00298                    size_t length,
00299                    char *encoding,
00300                    char **decoded,
00301                    size_t *bytes_decoded)
00302 {
00303        *bytes_decoded = 0;
00304        *decoded = NULL;
00305        /* Some encodings aren't really encodings */
00306        if (!strcasecmp(encoding, "7bit"))
00307               strcpy(encoding, "");
00308        if (!strcasecmp(encoding, "8bit"))
00309               strcpy(encoding, "");
00310        if (!strcasecmp(encoding, "binary"))
00311               strcpy(encoding, "");
00312 
00313        /* If this part is not encoded, send as-is */
00314        if (strlen(encoding) == 0) {
00315               return 0;
00316        }
00317        
00318 
00319        /* Fail if we hit an unknown encoding. */
00320        if ((strcasecmp(encoding, "base64"))
00321            && (strcasecmp(encoding, "quoted-printable"))) {
00322               return -1;
00323        }
00324 
00325        /*
00326         * Allocate a buffer for the decoded data.  The output buffer is slightly
00327         * larger than the input buffer; this assumes that the decoded data
00328         * will never be significantly larger than the encoded data.  This is a
00329         * safe assumption with base64, uuencode, and quoted-printable.
00330         */
00331        *decoded = malloc(length + 32768);
00332        if (decoded == NULL) {
00333               return -1;
00334        }
00335 
00336        if (!strcasecmp(encoding, "base64")) {
00337               *bytes_decoded = CtdlDecodeBase64(*decoded, part_start, length);
00338               return 1;
00339        }
00340        else if (!strcasecmp(encoding, "quoted-printable")) {
00341               *bytes_decoded = CtdlDecodeQuotedPrintable(*decoded, part_start, length);
00342               return 1;
00343        }
00344        return -1;
00345 }
00346 
00347 typedef enum _eIntMimeHdrs {
00348        boundary,
00349        startary,
00350        endary,
00351        content_type,
00352        charset,
00353        encoding,
00354        content_type_name,
00355        content_disposition_name,
00356        filename,
00357        disposition,
00358        id,
00359        eMax /* don't move ! */
00360 } eIntMimeHdrs;
00361 
00362 typedef struct _CBufStr {
00363        char Key[SIZ];
00364        long len;
00365 }CBufStr;
00366 
00367 typedef struct _interesting_mime_headers {
00368        CBufStr b[eMax];
00369        long content_length;
00370        long is_multipart;
00371 } interesting_mime_headers;
00372 
00373 
00374 static void FlushInterestingMimes(interesting_mime_headers *m)
00375 {
00376        int i;
00377        
00378        for (i = 0; i < eMax; i++) {
00379             m->b[i].Key[0] = '\0';
00380             m->b[i].len = 0;
00381        }
00382        m->content_length = -1;
00383 }
00384 static interesting_mime_headers *InitInterestingMimes(void)
00385 {
00386        interesting_mime_headers *m;
00387        m = (interesting_mime_headers*) malloc( sizeof(interesting_mime_headers));
00388 
00389        FlushInterestingMimes(m);
00390 
00391        return m;
00392 }
00393 
00394 
00395 static long parse_MimeHeaders(interesting_mime_headers *m, 
00396                            char** pcontent_start, 
00397                            char *content_end)
00398 {
00399        char buf[SIZ];
00400        char header[SIZ];
00401        long headerlen;
00402        char *ptr, *pch;
00403        int buflen = 0;
00404        int i;
00405 
00406        /* Learn interesting things from the headers */
00407        ptr = *pcontent_start;
00408        *header = '\0';
00409        headerlen = 0;
00410        do {
00411               ptr = memreadlinelen(ptr, buf, SIZ, &buflen);
00412 
00413               for (i = 0; i < buflen; ++i) {
00414                      if (isspace(buf[i])) {
00415                             buf[i] = ' ';
00416                      }
00417               }
00418 
00419               if (!isspace(buf[0]) && (headerlen > 0)) {
00420                      if (!strncasecmp(header, "Content-type:", 13)) {
00421                             memcpy (m->b[content_type].Key, &header[13], headerlen - 12);
00422                             m->b[content_type].Key[headerlen - 12] = '\0';
00423                             m->b[content_type].len = striplt (m->b[content_type].Key);
00424 
00425                             m->b[content_type_name].len = extract_key(m->b[content_type_name].Key, CKEY(m->b[content_type]), HKEY("name"), '=');
00426                             m->b[charset].len           = extract_key(m->b[charset].Key,           CKEY(m->b[content_type]), HKEY("charset"), '=');
00427                             m->b[boundary].len          = extract_key(m->b[boundary].Key,          header,       headerlen,  HKEY("boundary"), '=');
00428 
00429                             /* Deal with weird headers */
00430                             pch = strchr(m->b[content_type].Key, ' ');
00431                             if (pch != NULL) {
00432                                    *pch = '\0';
00433                                    m->b[content_type].len = m->b[content_type].Key - pch;
00434                             }
00435                             pch = strchr(m->b[content_type].Key, ';');
00436                             if (pch != NULL) {
00437                                    *pch = '\0';
00438                                    m->b[content_type].len = m->b[content_type].Key - pch;
00439                             }
00440                      }
00441                      else if (!strncasecmp(header, "Content-Disposition:", 20)) {
00442                             memcpy (m->b[disposition].Key, &header[20], headerlen - 19);
00443                             m->b[disposition].Key[headerlen - 19] = '\0';
00444                             m->b[disposition].len = striplt(m->b[disposition].Key);
00445 
00446                             m->b[content_disposition_name].len = extract_key(m->b[content_disposition_name].Key, CKEY(m->b[disposition]), HKEY("name"), '=');
00447                             m->b[filename].len                 = extract_key(m->b[filename].Key,                 CKEY(m->b[disposition]), HKEY("filename"), '=');
00448                             pch = strchr(m->b[disposition].Key, ';');
00449                             if (pch != NULL) *pch = '\0';
00450                             m->b[disposition].len = striplt(m->b[disposition].Key);
00451                      }
00452                      else if (!strncasecmp(header, "Content-ID:", 11)) {
00453                             memcpy(m->b[id].Key, &header[11], headerlen - 11);
00454                             m->b[id].Key[headerlen - 11] = '\0';
00455                             striplt(m->b[id].Key);
00456                             m->b[id].len = stripallbut(m->b[id].Key, '<', '>');
00457                      }
00458                      else if (!strncasecmp(header, "Content-length: ", 15)) {
00459                             char *clbuf;
00460                             clbuf = &header[15];
00461                             while (isspace(*clbuf))
00462                                    clbuf ++;
00463                             m->content_length = (size_t) atol(clbuf);
00464                      }
00465                      else if (!strncasecmp(header, "Content-transfer-encoding: ", 26)) {
00466                             memcpy(m->b[encoding].Key, &header[26], headerlen - 26);
00467                             m->b[encoding].Key[headerlen - 26] = '\0';
00468                             m->b[encoding].len = striplt(m->b[encoding].Key);
00469                      }
00470                      *header = '\0';
00471                      headerlen = 0;
00472               }
00473               if ((headerlen + buflen + 2) < SIZ) {
00474                      memcpy(&header[headerlen], buf, buflen);
00475                      headerlen += buflen;
00476                      header[headerlen] = '\0';
00477               }
00478               if (ptr >= content_end) {
00479                      return -1;
00480               }
00481        } while ((!IsEmptyStr(buf)) && (*ptr != 0));
00482 
00483        m->is_multipart = m->b[boundary].len != 0;
00484        *pcontent_start = ptr;
00485 
00486        return 0;
00487 }
00488 
00489 
00490 static int IsAsciiEncoding(interesting_mime_headers *m)
00491 {
00492 
00493        if ((m->b[encoding].len != 0) &&
00494            (strcasecmp(m->b[encoding].Key, "base64") == 0))
00495               return 1;
00496        if ((m->b[encoding].len != 0) &&
00497            (strcmp(m->b[encoding].Key, "quoted-printable") == 0))
00498               return 1;
00499 
00500        return 0;
00501 }
00502 
00503 static char *FindNextContent(char *ptr,
00504                           char *content_end,
00505                           interesting_mime_headers *SubMimeHeaders,
00506                           interesting_mime_headers *m)
00507 {
00508        char *next_boundary;
00509        char  tmp;
00510 
00511        if (IsAsciiEncoding(SubMimeHeaders)) {
00512               tmp = *content_end;
00513               *content_end = '\0';
00514 
00524               if ((SubMimeHeaders->content_length != -1) &&
00525                   (SubMimeHeaders->content_length > 10))
00526               {
00527                      char *pptr;
00528                      long lines;
00529                                    
00530                      lines = SubMimeHeaders->content_length / 80;
00531                      pptr = ptr + SubMimeHeaders->content_length - lines - 10;
00532                      if (pptr < content_end)
00533                             ptr = pptr;
00534               }
00535                      
00536               next_boundary = strstr(ptr, m->b[startary].Key);
00537               *content_end = tmp;
00538        }
00539        else {
00540               char *srch;
00548               if ((SubMimeHeaders->content_length != -1) &&
00549                   (SubMimeHeaders->content_length > 10))
00550               {
00551                      char *pptr;
00552                      pptr = ptr + SubMimeHeaders->content_length - 10;
00553                      if (pptr < content_end)
00554                             ptr = pptr;
00555               }
00556               
00557 
00558               srch = next_boundary = NULL;
00559               for (srch = memchr(ptr, '-',  content_end - ptr);
00560                    (srch != NULL) && (srch < content_end); 
00561                    srch = memchr(srch, '-',  content_end - srch)) 
00562               {
00563                      if (!memcmp(srch, 
00564                                 m->b[startary].Key, 
00565                                 m->b[startary].len)) 
00566                      {
00567                             next_boundary = srch;
00568                             srch = content_end;
00569                      }
00570                      else srch ++;
00571 
00572               }
00573 
00574        }
00575        return next_boundary;
00576 }
00577 
00578 /*
00579  * Break out the components of a multipart message
00580  * (This function expects to be fed HEADERS + CONTENT)
00581  * Note: NULL can be supplied as content_end; in this case, the message is
00582  * considered to have ended when the parser encounters a 0x00 byte.
00583  */
00584 static void recurseable_mime_parser(char *partnum,
00585                                 char *content_start, char *content_end,
00586                                 MimeParserCallBackType CallBack,
00587                                 MimeParserCallBackType PreMultiPartCallBack,
00588                                 MimeParserCallBackType PostMultiPartCallBack,
00589                                 void *userdata,
00590                                 int dont_decode, 
00591                                 interesting_mime_headers *m)
00592 {
00593        interesting_mime_headers *SubMimeHeaders;
00594        char     *ptr;
00595        char     *part_start;
00596        char     *part_end = NULL;
00597        char     *evaluate_crlf_ptr = NULL;
00598        char     *next_boundary;
00599        char      nested_partnum[256];
00600        int       crlf_in_use = 0;
00601        int       part_seq = 0;
00602        CBufStr  *chosen_name;
00603 
00604 
00605        /* If this is a multipart message, then recursively process it */
00606        ptr = content_start;
00607        part_start = NULL;
00608        if (m->is_multipart) {
00609 
00610               /* Tell the client about this message's multipartedness */
00611               if (PreMultiPartCallBack != NULL) {
00612                      PreMultiPartCallBack("", 
00613                                         "", 
00614                                         partnum, 
00615                                         "",
00616                                         NULL, 
00617                                         m->b[content_type].Key, 
00618                                         m->b[charset].Key,
00619                                         0, 
00620                                         m->b[encoding].Key, 
00621                                         m->b[id].Key, 
00622                                         userdata);
00623               }
00624 
00625               /* Figure out where the boundaries are */
00626               m->b[startary].len = snprintf(m->b[startary].Key, SIZ, "--%s", m->b[boundary].Key);
00627               SubMimeHeaders = InitInterestingMimes ();
00628 
00629               while ((*ptr == '\r') || (*ptr == '\n')) ptr ++;
00630 
00631               if (strncmp(ptr, m->b[startary].Key, m->b[startary].len) == 0)
00632                      ptr += m->b[startary].len;
00633 
00634               while ((*ptr == '\r') || (*ptr == '\n')) ptr ++;
00635 
00636               part_start = NULL;
00637               do {
00638                      char *optr;
00639 
00640                      optr = ptr;
00641                      if (parse_MimeHeaders(SubMimeHeaders, &ptr, content_end) != 0)
00642                             break;
00643                      if ((ptr - optr > 2) && 
00644                          (*(ptr - 2) == '\r'))
00645                             crlf_in_use = 1;
00646                      
00647                      part_start = ptr;
00648                      
00649                      next_boundary = FindNextContent(ptr,
00650                                                  content_end,
00651                                                  SubMimeHeaders,
00652                                                  m);
00653                      if ((next_boundary != NULL) && 
00654                          (next_boundary - part_start < 3)) {
00655                             FlushInterestingMimes(SubMimeHeaders);
00656 
00657                             continue;
00658                      }
00659 
00660                      if ( (part_start != NULL) && (next_boundary != NULL) ) {
00661                             part_end = next_boundary;
00662                             --part_end;          /* omit the trailing LF */
00663                             if (crlf_in_use) {
00664                                    --part_end;   /* omit the trailing CR */
00665                             }
00666 
00667                             if (!IsEmptyStr(partnum)) {
00668                                    snprintf(nested_partnum,
00669                                            sizeof nested_partnum,
00670                                            "%s.%d", partnum,
00671                                            ++part_seq);
00672                             }
00673                             else {
00674                                    snprintf(nested_partnum,
00675                                            sizeof nested_partnum,
00676                                            "%d", ++part_seq);
00677                             }
00678                             recurseable_mime_parser(nested_partnum,
00679                                                  part_start, 
00680                                                  part_end,
00681                                                  CallBack,
00682                                                  PreMultiPartCallBack,
00683                                                  PostMultiPartCallBack,
00684                                                  userdata,
00685                                                  dont_decode, 
00686                                                  SubMimeHeaders);
00687                      }
00688 
00689                      if (next_boundary != NULL) {
00690                             /* If we pass out of scope, don't attempt to
00691                              * read past the end boundary. */
00692                             if ((*(next_boundary + m->b[startary].len) == '-') && 
00693                                 (*(next_boundary + m->b[startary].len + 1) == '-') ){
00694                                    ptr = content_end;
00695                             }
00696                             else {
00697                                    /* Set up for the next part. */
00698                                    part_start = strstr(next_boundary, "\n");
00699                                    
00700                                    /* Determine whether newlines are LF or CRLF */
00701                                    evaluate_crlf_ptr = part_start;
00702                                    --evaluate_crlf_ptr;
00703                                    if ((*evaluate_crlf_ptr == '\r') && 
00704                                        (*(evaluate_crlf_ptr + 1) == '\n'))
00705                                    {
00706                                           crlf_in_use = 1;
00707                                    }
00708                                    else {
00709                                           crlf_in_use = 0;
00710                                    }
00711 
00712                                    /* Advance past the LF ... now we're in the next part */
00713                                    ++part_start;
00714                                    ptr = part_start;
00715                             }
00716                      }
00717                      else {
00718                             /* Invalid end of multipart.  Bail out! */
00719                             ptr = content_end;
00720                      }
00721                      FlushInterestingMimes(SubMimeHeaders);
00722               } while ( (ptr < content_end) && (next_boundary != NULL) );
00723 
00724               free(SubMimeHeaders);
00725 
00726               if (PostMultiPartCallBack != NULL) {
00727                      PostMultiPartCallBack("", 
00728                                          "", 
00729                                          partnum, 
00730                                          "", 
00731                                          NULL,
00732                                          m->b[content_type].Key, 
00733                                          m->b[charset].Key,
00734                                          0, 
00735                                          m->b[encoding].Key, 
00736                                          m->b[id].Key, 
00737                                          userdata);
00738               }
00739        } /* If it's not a multipart message, then do something with it */
00740        else {
00741               size_t length;
00742               part_start = ptr;
00743               length = content_end - part_start;
00744               ptr = part_end = content_end;
00745 
00746 
00747               /* The following code will truncate the MIME part to the size
00748                * specified by the Content-length: header.   We have commented it
00749                * out because these headers have a tendency to be wrong.
00750                *
00751                *     if ( (content_length > 0) && (length > content_length) ) {
00752                *            length = content_length;
00753                *     }
00754                  */
00755 
00756               /* Sometimes the "name" field is tacked on to Content-type,
00757                * and sometimes it's tacked on to Content-disposition.  Use
00758                * whichever one we have.
00759                */
00760               if (m->b[content_disposition_name].len > m->b[content_type_name].len) {
00761                      chosen_name = &m->b[content_disposition_name];
00762               }
00763               else {
00764                      chosen_name = &m->b[content_type_name];
00765               }
00766        
00767               /* Ok, we've got a non-multipart part here, so do something with it.
00768                */
00769               mime_decode(partnum,
00770                          part_start, 
00771                          length,
00772                          m->b[content_type].Key, 
00773                          m->b[charset].Key,
00774                          m->b[encoding].Key, 
00775                          m->b[disposition].Key, 
00776                          m->b[id].Key, 
00777                          chosen_name->Key, 
00778                          m->b[filename].Key,
00779                          CallBack, 
00780                          NULL, NULL,
00781                          userdata, 
00782                          dont_decode
00783                      );
00784 
00785               /*
00786                * Now if it's an encapsulated message/rfc822 then we have to recurse into it
00787                */
00788               if (!strcasecmp(&m->b[content_type].Key[0], "message/rfc822")) {
00789 
00790                      if (PreMultiPartCallBack != NULL) {
00791                             PreMultiPartCallBack("", 
00792                                                "", 
00793                                                partnum, 
00794                                                "",
00795                                                NULL, 
00796                                                m->b[content_type].Key, 
00797                                                m->b[charset].Key,
00798                                                0, 
00799                                                m->b[encoding].Key, 
00800                                                m->b[id].Key, 
00801                                                userdata);
00802                      }
00803                      if (CallBack != NULL) {
00804                             if (strlen(partnum) > 0) {
00805                                    snprintf(nested_partnum,
00806                                            sizeof nested_partnum,
00807                                            "%s.%d", partnum,
00808                                            ++part_seq);
00809                             }
00810                             else {
00811                                    snprintf(nested_partnum,
00812                                            sizeof nested_partnum,
00813                                            "%d", ++part_seq);
00814                             }
00815                             the_mime_parser(nested_partnum,
00816                                           part_start, 
00817                                           part_end,
00818                                           CallBack,
00819                                           PreMultiPartCallBack,
00820                                           PostMultiPartCallBack,
00821                                           userdata,
00822                                           dont_decode
00823                                    );
00824                      }
00825                      if (PostMultiPartCallBack != NULL) {
00826                             PostMultiPartCallBack("", 
00827                                                 "", 
00828                                                 partnum, 
00829                                                 "", 
00830                                                 NULL,
00831                                                 m->b[content_type].Key, 
00832                                                 m->b[charset].Key,
00833                                                 0, 
00834                                                 m->b[encoding].Key, 
00835                                                 m->b[id].Key, 
00836                                                 userdata);
00837                      }
00838 
00839 
00840               }
00841 
00842        }
00843 
00844 }
00845 
00846 /*
00847  * Break out the components of a multipart message
00848  * (This function expects to be fed HEADERS + CONTENT)
00849  * Note: NULL can be supplied as content_end; in this case, the message is
00850  * considered to have ended when the parser encounters a 0x00 byte.
00851  */
00852 void the_mime_parser(char *partnum,
00853                    char *content_start, char *content_end,
00854                    MimeParserCallBackType CallBack,
00855                    MimeParserCallBackType PreMultiPartCallBack,
00856                    MimeParserCallBackType PostMultiPartCallBack,
00857                    void *userdata,
00858                    int dont_decode)
00859 {
00860        interesting_mime_headers *m;
00861 
00862        /* If the caller didn't supply an endpointer, generate one by measure */
00863        if (content_end == NULL) {
00864               content_end = &content_start[strlen(content_start)];
00865        }
00866 
00867        m = InitInterestingMimes();
00868 
00869        if (!parse_MimeHeaders(m, &content_start, content_end))
00870        {
00871 
00872               recurseable_mime_parser(partnum,
00873                                    content_start, content_end,
00874                                    CallBack,
00875                                    PreMultiPartCallBack,
00876                                    PostMultiPartCallBack,
00877                                    userdata,
00878                                    dont_decode,
00879                                    m);
00880        }
00881        free(m);
00882 }
00883 
00884 /*
00885  * Entry point for the MIME parser.
00886  * (This function expects to be fed HEADERS + CONTENT)
00887  * Note: NULL can be supplied as content_end; in this case, the message is
00888  * considered to have ended when the parser encounters a 0x00 byte.
00889  */
00890 void mime_parser(char *content_start,
00891                char *content_end,
00892                MimeParserCallBackType CallBack,
00893                MimeParserCallBackType PreMultiPartCallBack,
00894                MimeParserCallBackType PostMultiPartCallBack,
00895                void *userdata,
00896                int dont_decode)
00897 {
00898 
00899        the_mime_parser("", content_start, content_end,
00900                      CallBack,
00901                      PreMultiPartCallBack,
00902                      PostMultiPartCallBack,
00903                      userdata, dont_decode);
00904 }
00905 
00906 
00907 
00908 
00909 
00910 
00911 typedef struct _MimeGuess {
00912        const char *Pattern;
00913        size_t PatternLen;
00914        long PatternOffset;
00915        const char *MimeString;
00916 } MimeGuess;
00917 
00918 MimeGuess MyMimes [] = {
00919        {
00920               "GIF",
00921               3,
00922               0,
00923               "image/gif"
00924        },
00925        {
00926               "\xff\xd8",
00927               2,
00928               0,
00929               "image/jpeg"
00930        },
00931        {
00932               "\x89PNG",
00933               4,
00934               0,
00935               "image/png"
00936        },
00937        { // last...
00938               "",
00939               0,
00940               0,
00941               ""
00942        }
00943 };
00944 
00945 
00946 const char *GuessMimeType(const char *data, size_t dlen)
00947 {
00948        int MimeIndex = 0;
00949 
00950        while (MyMimes[MimeIndex].PatternLen != 0)
00951        {
00952               if ((MyMimes[MimeIndex].PatternLen + 
00953                    MyMimes[MimeIndex].PatternOffset < dlen) &&
00954                   strncmp(MyMimes[MimeIndex].Pattern, 
00955                          &data[MyMimes[MimeIndex].PatternOffset], 
00956                          MyMimes[MimeIndex].PatternLen) == 0)
00957               {
00958                      return MyMimes[MimeIndex].MimeString;
00959               }
00960               MimeIndex ++;
00961        }
00962        /* 
00963         * ok, our simple minded algorythm didn't find anything, 
00964         * let the big chegger try it, he wil default to application/octet-stream
00965         */
00966        return (xdg_mime_get_mime_type_for_data(data, dlen));
00967 }
00968 
00969 
00970 const char* GuessMimeByFilename(const char *what, size_t len)
00971 {
00972        /* we know some hardcoded on our own, try them... */
00973        if ((len > 3) && !strncasecmp(&what[len - 4], ".gif", 4))
00974               return "image/gif";
00975        else if ((len > 2) && !strncasecmp(&what[len - 3], ".js", 3))
00976               return  "text/javascript";
00977        else if ((len > 3) && !strncasecmp(&what[len - 4], ".txt", 4))
00978               return "text/plain";
00979        else if ((len > 3) && !strncasecmp(&what[len - 4], ".css", 4))
00980               return "text/css";
00981        else if ((len > 3) && !strncasecmp(&what[len - 4], ".htc", 4))
00982               return "text/x-component";
00983        else if ((len > 3) && !strncasecmp(&what[len - 4], ".jpg", 4))
00984               return "image/jpeg";
00985        else if ((len > 3) && !strncasecmp(&what[len - 4], ".png", 4))
00986               return "image/png";
00987        else if ((len > 3) && !strncasecmp(&what[len - 4], ".ico", 4))
00988               return "image/x-icon";
00989        else if ((len > 3) && !strncasecmp(&what[len - 4], ".vcf", 4))
00990               return "text/x-vcard";
00991        else if ((len > 4) && !strncasecmp(&what[len - 5], ".html", 5))
00992               return "text/html";
00993        else if ((len > 3) && !strncasecmp(&what[len - 4], ".htm", 4))
00994               return "text/html";
00995        else if ((len > 3) && !strncasecmp(&what[len - 4], ".wml", 4))
00996               return "text/vnd.wap.wml";
00997        else if ((len > 4) && !strncasecmp(&what[len - 5], ".wmls", 5))
00998               return "text/vnd.wap.wmlscript";
00999        else if ((len > 4) && !strncasecmp(&what[len - 5], ".wmlc", 5))
01000               return "application/vnd.wap.wmlc";
01001        else if ((len > 5) && !strncasecmp(&what[len - 6], ".wmlsc", 6))
01002               return "application/vnd.wap.wmlscriptc";
01003        else if ((len > 4) && !strncasecmp(&what[len - 5], ".wbmp", 5))
01004               return "image/vnd.wap.wbmp";
01005        else
01006               /* and let xdgmime do the fallback. */
01007               return xdg_mime_get_mime_type_from_file_name(what);
01008 }
01009 
01010 static HashList *IconHash = NULL;
01011 
01012 typedef struct IconName IconName;
01013 
01014 struct IconName {
01015        char *FlatName;
01016        char *FileName;
01017 };
01018 
01019 static void DeleteIcon(void *IconNamePtr)
01020 {
01021        IconName *Icon = (IconName*) IconNamePtr;
01022        free(Icon->FlatName);
01023        free(Icon->FileName);
01024        free(Icon);
01025 }
01026 
01027 /*
01028 static const char *PrintFlat(void *IconNamePtr)
01029 {
01030        IconName *Icon = (IconName*) IconNamePtr;
01031        return Icon->FlatName;
01032 }
01033 static const char *PrintFile(void *IconNamePtr)
01034 {
01035        IconName *Icon = (IconName*) IconNamePtr;
01036        return Icon->FileName;
01037 }
01038 */
01039 
01040 #define GENSTR "x-generic"
01041 #define IGNORE_PREFIX_1 "gnome-mime"
01042 int LoadIconDir(const char *DirName)
01043 {
01044        DIR *filedir = NULL;
01045        struct dirent *filedir_entry;
01046        int d_namelen;
01047        int d_without_ext;
01048        IconName *Icon;
01049 
01050        filedir = opendir (DirName);
01051        IconHash = NewHash(1, NULL);
01052        if (filedir == NULL) {
01053               return 0;
01054        }
01055 
01056        while ((filedir_entry = readdir(filedir)))
01057        {
01058               char *MinorPtr;
01059               char *PStart;
01060 #ifdef _DIRENT_HAVE_D_NAMELEN
01061               d_namelen = filedir_entry->d_namelen;
01062 #else
01063               d_namelen = strlen(filedir_entry->d_name);
01064 #endif
01065               d_without_ext = d_namelen;
01066               while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
01067                      d_without_ext --;
01068               if ((d_without_ext == 0) || (d_namelen < 3))
01069                      continue;
01070 
01071               if ((sizeof(IGNORE_PREFIX_1) < d_namelen) &&
01072                   (strncmp(IGNORE_PREFIX_1, 
01073                           filedir_entry->d_name, 
01074                           sizeof(IGNORE_PREFIX_1) - 1) == 0)) {
01075                      PStart = filedir_entry->d_name + sizeof(IGNORE_PREFIX_1);
01076                      d_without_ext -= sizeof(IGNORE_PREFIX_1);
01077               }
01078               else {
01079                      PStart = filedir_entry->d_name;
01080               }
01081               Icon = malloc(sizeof(IconName));
01082 
01083               Icon->FileName = malloc(d_namelen + 1);
01084               memcpy(Icon->FileName, filedir_entry->d_name, d_namelen + 1);
01085 
01086               Icon->FlatName = malloc(d_without_ext + 1);
01087               memcpy(Icon->FlatName, PStart, d_without_ext);
01088               Icon->FlatName[d_without_ext] = '\0';
01089               /* Try to find Minor type in image-jpeg */
01090               MinorPtr = strchr(Icon->FlatName, '-');
01091               if (MinorPtr != NULL) {
01092                      size_t MinorLen;
01093                      MinorLen = 1 + d_without_ext - (MinorPtr - Icon->FlatName + 1);
01094                      if ((MinorLen == sizeof(GENSTR)) && 
01095                          (strncmp(MinorPtr + 1, GENSTR, sizeof(GENSTR)) == 0)) {
01096                             /* ok, we found a generic filename. cut the generic. */
01097                             *MinorPtr = '\0';
01098                             d_without_ext = d_without_ext - (MinorPtr - Icon->FlatName);
01099                      }
01100                      else { /* Map the major / minor separator to / */
01101                             *MinorPtr = '/';
01102                      }
01103               }
01104 
01105 //            PrintHash(IconHash, PrintFlat, PrintFile);
01106 //            printf("%s - %s\n", Icon->FlatName, Icon->FileName);
01107               Put(IconHash, Icon->FlatName, d_without_ext, Icon, DeleteIcon);
01108 //            PrintHash(IconHash, PrintFlat, PrintFile);
01109        }
01110        closedir(filedir);
01111        return 1;
01112 }
01113 
01114 const char *GetIconFilename(char *MimeType, size_t len)
01115 {
01116        void *vIcon;
01117        IconName *Icon;
01118        
01119        if(IconHash == NULL)
01120               return NULL;
01121 
01122        GetHash(IconHash, MimeType, len, &vIcon), Icon = (IconName*) vIcon;
01123        /* didn't find the exact mimetype? try major only. */
01124        if (Icon == NULL) {
01125               char * pMinor;
01126               pMinor = strchr(MimeType, '/');
01127               if (pMinor != NULL) {
01128                      *pMinor = '\0';
01129                      GetHash(IconHash, MimeType, pMinor - MimeType, &vIcon),
01130                             Icon = (IconName*) vIcon;
01131               }
01132        }
01133        if (Icon == NULL) {
01134               return NULL;
01135        }
01136 
01137        /*printf("Getting: [%s] == [%s] -> [%s]\n", MimeType, Icon->FlatName, Icon->FileName);*/
01138        return Icon->FileName;
01139 }
01140 
01141 void ShutDownLibCitadelMime(void)
01142 {
01143        DeleteHash(&IconHash);
01144 }