LCOV - code coverage report
Current view: top level - lib - mime_parser.c (source / functions) Hit Total Coverage
Test: libcitadel.info Lines: 359 425 84.5 %
Date: 2010-12-07 Functions: 19 21 90.5 %
Branches: 236 306 77.1 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This is the MIME parser for Citadel.
       3                 :            :  *
       4                 :            :  * Copyright (c) 1998-2010 by the citadel.org development team.
       5                 :            :  * This code is distributed under the GNU General Public License v3.
       6                 :            :  *
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <stdlib.h>
      10                 :            : #include <unistd.h>
      11                 :            : #include <stdio.h>
      12                 :            : #include <signal.h>
      13                 :            : #include <sys/types.h>
      14                 :            : #include <ctype.h>
      15                 :            : #include <string.h>
      16                 :            : #include <sys/stat.h>
      17                 :            : #include <sys/types.h>
      18                 :            : #include <dirent.h>
      19                 :            : #include <errno.h>
      20                 :            : 
      21                 :            : #include "xdgmime/xdgmime.h"
      22                 :            : #include "libcitadel.h"
      23                 :            : #include "libcitadellocal.h"
      24                 :            : 
      25                 :            : const unsigned char FromHexTable [256] = {
      26                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //  0
      27                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 10
      28                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20
      29                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 30
      30                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, // 40
      31                 :            :         0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, // 50
      32                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 60
      33                 :            :         0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 70
      34                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 80
      35                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, // 90
      36                 :            :         0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //100
      37                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //110
      38                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //120
      39                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //130
      40                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //140
      41                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //150
      42                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //160
      43                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //170
      44                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //180
      45                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //190
      46                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //200
      47                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //210
      48                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //220
      49                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //230
      50                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //240
      51                 :            :         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF                          //250
      52                 :            : };
      53                 :            : 
      54                 :            : 
      55                 :       9988 : long extract_key(char *target, char *source, long sourcelen, char *key, long keylen, char KeyEnd)
      56                 :            : {
      57                 :       9988 :         char *sptr, *ptr = NULL;
      58                 :       9988 :         int double_quotes = 0;
      59                 :       9988 :         long RealKeyLen = keylen;
      60                 :            : 
      61                 :       9988 :         sptr = source;
      62                 :            : 
      63         [ +  + ]:      19976 :         while (sptr != NULL)
      64                 :            :         {
      65                 :       9988 :                 ptr = bmstrcasestr_len(sptr, sourcelen - (sptr - source), 
      66                 :            :                                        key, keylen);
      67         [ +  + ]:       9988 :                 if(ptr != NULL)
      68                 :            :                 {
      69         [ -  + ]:       3052 :                         while (isspace(*(ptr + RealKeyLen)))
      70                 :          0 :                                 RealKeyLen ++;
      71         [ +  - ]:       3052 :                         if (*(ptr + RealKeyLen) == KeyEnd)
      72                 :            :                         {
      73                 :       3052 :                                 sptr = NULL;
      74                 :       3052 :                                 RealKeyLen ++;                          
      75                 :            :                         }
      76                 :            :                         else
      77                 :            :                         {
      78                 :       3052 :                                 sptr = ptr + RealKeyLen + 1;
      79                 :            :                         }
      80                 :            :                 }
      81                 :            :                 else 
      82                 :       6936 :                         sptr = ptr;
      83                 :            :         }
      84         [ +  + ]:       9988 :         if (ptr == NULL) {
      85                 :       6936 :                 *target = '\0';
      86                 :       6936 :                 return 0;
      87                 :            :         }
      88                 :       3052 :         strcpy(target, (ptr + RealKeyLen));
      89                 :            : 
      90         [ +  + ]:      61300 :         for (ptr=target; (*ptr != 0); ptr++) {
      91                 :            : 
      92                 :            :                 /* A semicolon means we've hit the end of the key, unless we're inside double quotes */
      93 [ +  + ][ +  + ]:      58248 :                 if ( (double_quotes != 1) && (*ptr == ';')) {
      94                 :        149 :                         *ptr = 0;
      95                 :            :                 }
      96                 :            : 
      97                 :            :                 /* if we find double quotes, we've got a great set of string boundaries */
      98         [ +  + ]:      58248 :                 if (*ptr == '\"') {
      99                 :       6006 :                         ++double_quotes;
     100         [ +  + ]:       6006 :                         if (double_quotes == 1) {
     101                 :       2881 :                                 strcpy(ptr, ptr+1);
     102                 :            :                         }
     103                 :            :                         else {
     104                 :       3125 :                                 *ptr = 0;
     105                 :            :                         }
     106                 :            :                 }
     107                 :            :         }
     108                 :       3052 :         *ptr = '\0';
     109                 :       9988 :         return ptr - target;
     110                 :            : }
     111                 :            : 
     112                 :            : 
     113                 :            : /*
     114                 :            :  * For non-multipart messages, we need to generate a quickie partnum of "1"
     115                 :            :  * to return to callback functions.  Some callbacks demand it.
     116                 :            :  */
     117                 :       2455 : char *fixed_partnum(char *supplied_partnum) {
     118         [ -  + ]:       2455 :         if (supplied_partnum == NULL) return "1";
     119         [ +  + ]:       2455 :         if (strlen(supplied_partnum)==0) return "1";
     120                 :       2455 :         return supplied_partnum;
     121                 :            : }
     122                 :            : 
     123                 :            : 
     124                 :      16500 : static inline unsigned int _decode_hex(const char *Source)
     125                 :            : {
     126                 :      16500 :         int ret = '?';
     127                 :            :         unsigned char LO_NIBBLE;
     128                 :            :         unsigned char HI_NIBBLE;
     129                 :            : 
     130                 :      16500 :         HI_NIBBLE = FromHexTable[(unsigned char) *Source];
     131                 :      16500 :         LO_NIBBLE = FromHexTable[(unsigned char) *(Source+1)];
     132                 :            :         
     133 [ +  - ][ -  + ]:      16500 :         if ((LO_NIBBLE == 0xFF) || (LO_NIBBLE == 0xFF))
     134                 :          0 :                 return ret;
     135                 :      16500 :         ret = HI_NIBBLE;
     136                 :      16500 :         ret = ret << 4;
     137                 :      16500 :         ret = ret | LO_NIBBLE;
     138                 :      16500 :         return ret;
     139                 :            : }
     140                 :            : 
     141                 :          0 : unsigned int decode_hex(char *Source) {return _decode_hex(Source);}
     142                 :            : 
     143                 :            : /*
     144                 :            :  * Convert "quoted-printable" to binary.  Returns number of bytes decoded.
     145                 :            :  * according to RFC2045 section 6.7
     146                 :            :  */
     147                 :        125 : int CtdlDecodeQuotedPrintable(char *decoded, char *encoded, int sourcelen) {
     148                 :            :         unsigned int ch;
     149                 :        125 :         int decoded_length = 0;
     150                 :        125 :         int pos = 0;
     151                 :            : 
     152         [ +  + ]:     314572 :         while (pos < sourcelen)
     153                 :            :         {
     154         [ +  + ]:     314447 :                 if (*(encoded + pos) == '=')
     155                 :            :                 {
     156                 :      18985 :                         pos ++;
     157         [ +  + ]:      18985 :                         if (*(encoded + pos) == '\n')
     158                 :            :                         {
     159                 :       2485 :                                 pos ++;
     160                 :            :                         }
     161         [ -  + ]:      16500 :                         else if (*(encoded + pos) == '\r')
     162                 :            :                         {
     163                 :          0 :                                 pos ++;
     164         [ #  # ]:          0 :                                 if (*(encoded + pos) == '\n')
     165                 :          0 :                                         pos++;
     166                 :            :                         }
     167                 :            :                         else
     168                 :            :                         {
     169                 :      16500 :                                 ch = 0;
     170                 :      16500 :                                 ch = _decode_hex(&encoded[pos]);
     171                 :      16500 :                                 pos += 2;
     172                 :      18985 :                                 decoded[decoded_length++] = ch;
     173                 :            :                         }
     174                 :            :                 }
     175                 :            :                 else
     176                 :            :                 {
     177                 :     295462 :                         decoded[decoded_length++] = encoded[pos];
     178                 :     295462 :                         pos += 1;
     179                 :            :                 }
     180                 :            :         }
     181                 :        125 :         decoded[decoded_length] = 0;
     182                 :        125 :         return(decoded_length);
     183                 :            : }
     184                 :            : 
     185                 :            : 
     186                 :            : /*
     187                 :            :  * Given a message or message-part body and a length, handle any necessary
     188                 :            :  * decoding and pass the request up the stack.
     189                 :            :  */
     190                 :       2623 : void mime_decode(char *partnum,
     191                 :            :                  char *part_start, size_t length,
     192                 :            :                  char *content_type, char *charset, char *encoding,
     193                 :            :                  char *disposition,
     194                 :            :                  char *id,
     195                 :            :                  char *name, char *filename,
     196                 :            :                  MimeParserCallBackType CallBack,
     197                 :            :                  MimeParserCallBackType PreMultiPartCallBack,
     198                 :            :                  MimeParserCallBackType PostMultiPartCallBack,
     199                 :            :                  void *userdata,
     200                 :            :                  int dont_decode)
     201                 :            : {
     202                 :            : 
     203                 :            :         char *decoded;
     204                 :       2623 :         size_t bytes_decoded = 0;
     205                 :            : 
     206                 :            :         /* Some encodings aren't really encodings */
     207         [ +  + ]:       2623 :         if (!strcasecmp(encoding, "7bit"))
     208                 :        156 :                 strcpy(encoding, "");
     209         [ +  + ]:       2623 :         if (!strcasecmp(encoding, "8bit"))
     210                 :         18 :                 strcpy(encoding, "");
     211         [ -  + ]:       2623 :         if (!strcasecmp(encoding, "binary"))
     212                 :          0 :                 strcpy(encoding, "");
     213                 :            : 
     214                 :            :         /* If this part is not encoded, send as-is */
     215 [ +  + ][ +  + ]:       2623 :         if ( (strlen(encoding) == 0) || (dont_decode)) {
     216         [ +  + ]:       1224 :                 if (CallBack != NULL) {
     217                 :       1114 :                         CallBack(name, 
     218                 :            :                                  filename, 
     219                 :            :                                  fixed_partnum(partnum),
     220                 :            :                                  disposition, 
     221                 :            :                                  part_start,
     222                 :            :                                  content_type, 
     223                 :            :                                  charset, 
     224                 :            :                                  length, 
     225                 :            :                                  encoding, 
     226                 :            :                                  id,
     227                 :            :                                  userdata);
     228                 :            :                         }
     229                 :       1224 :                 return;
     230                 :            :         }
     231                 :            :         
     232                 :            :         /* Fail silently if we hit an unknown encoding. */
     233 [ +  + ][ +  + ]:       1399 :         if ((strcasecmp(encoding, "base64"))
     234                 :        167 :             && (strcasecmp(encoding, "quoted-printable"))) {
     235                 :         58 :                 return;
     236                 :            :         }
     237                 :            : 
     238                 :            :         /*
     239                 :            :          * Allocate a buffer for the decoded data.  The output buffer is slightly
     240                 :            :          * larger than the input buffer; this assumes that the decoded data
     241                 :            :          * will never be significantly larger than the encoded data.  This is a
     242                 :            :          * safe assumption with base64, uuencode, and quoted-printable.
     243                 :            :          */
     244                 :       1341 :         decoded = malloc(length + 32768);
     245         [ -  + ]:       1341 :         if (decoded == NULL) {
     246                 :          0 :                 return;
     247                 :            :         }
     248                 :            : 
     249         [ +  + ]:       1341 :         if (!strcasecmp(encoding, "base64")) {
     250                 :       1232 :                 bytes_decoded = CtdlDecodeBase64(decoded, part_start, length);
     251                 :            :         }
     252         [ +  - ]:        109 :         else if (!strcasecmp(encoding, "quoted-printable")) {
     253                 :        109 :                 bytes_decoded = CtdlDecodeQuotedPrintable(decoded, part_start, length);
     254                 :            :         }
     255                 :            : 
     256 [ +  - ][ +  - ]:       1341 :         if (bytes_decoded > 0) if (CallBack != NULL) {
     257                 :            :                         char encoding_buf[SIZ];
     258                 :            : 
     259                 :       1341 :                         strcpy(encoding_buf, "binary");
     260                 :       1341 :                         CallBack(name, 
     261                 :            :                                  filename, 
     262                 :            :                                  fixed_partnum(partnum),
     263                 :            :                                  disposition, 
     264                 :            :                                  decoded,
     265                 :            :                                  content_type, 
     266                 :            :                                  charset, 
     267                 :            :                                  bytes_decoded, 
     268                 :            :                                  encoding_buf, 
     269                 :            :                                  id, 
     270                 :            :                                  userdata);
     271                 :            :         }
     272                 :            : 
     273                 :       2623 :         free(decoded);
     274                 :            : }
     275                 :            : 
     276                 :            : /*
     277                 :            :  * this is the extract of mime_decode which can be called if 'dont_decode' was set; 
     278                 :            :  * to save the cpu intense process of decoding to the time when it realy wants the content. 
     279                 :            :  * returns: 
     280                 :            :  *   - > 0 we decoded something, its on *decoded, you need to free it.
     281                 :            :  *   - = 0 no need to decode stuff. *decoded will be NULL.
     282                 :            :  *   - < 0 an error occured, either an unknown encoding, or alloc failed. no need to free.
     283                 :            :  */
     284                 :        103 : int mime_decode_now (char *part_start, 
     285                 :            :                      size_t length,
     286                 :            :                      char *encoding,
     287                 :            :                      char **decoded,
     288                 :            :                      size_t *bytes_decoded)
     289                 :            : {
     290                 :        103 :         *bytes_decoded = 0;
     291                 :        103 :         *decoded = NULL;
     292                 :            :         /* Some encodings aren't really encodings */
     293         [ -  + ]:        103 :         if (!strcasecmp(encoding, "7bit"))
     294                 :          0 :                 strcpy(encoding, "");
     295         [ -  + ]:        103 :         if (!strcasecmp(encoding, "8bit"))
     296                 :          0 :                 strcpy(encoding, "");
     297         [ +  + ]:        103 :         if (!strcasecmp(encoding, "binary"))
     298                 :         51 :                 strcpy(encoding, "");
     299                 :            : 
     300                 :            :         /* If this part is not encoded, send as-is */
     301         [ +  - ]:        103 :         if (strlen(encoding) == 0) {
     302                 :        103 :                 return 0;
     303                 :            :         }
     304                 :            :         
     305                 :            : 
     306                 :            :         /* Fail if we hit an unknown encoding. */
     307 [ #  # ][ #  # ]:          0 :         if ((strcasecmp(encoding, "base64"))
     308                 :          0 :             && (strcasecmp(encoding, "quoted-printable"))) {
     309                 :          0 :                 return -1;
     310                 :            :         }
     311                 :            : 
     312                 :            :         /*
     313                 :            :          * Allocate a buffer for the decoded data.  The output buffer is slightly
     314                 :            :          * larger than the input buffer; this assumes that the decoded data
     315                 :            :          * will never be significantly larger than the encoded data.  This is a
     316                 :            :          * safe assumption with base64, uuencode, and quoted-printable.
     317                 :            :          */
     318                 :          0 :         *decoded = malloc(length + 32768);
     319         [ #  # ]:          0 :         if (decoded == NULL) {
     320                 :          0 :                 return -1;
     321                 :            :         }
     322                 :            : 
     323         [ #  # ]:          0 :         if (!strcasecmp(encoding, "base64")) {
     324                 :          0 :                 *bytes_decoded = CtdlDecodeBase64(*decoded, part_start, length);
     325                 :          0 :                 return 1;
     326                 :            :         }
     327         [ #  # ]:          0 :         else if (!strcasecmp(encoding, "quoted-printable")) {
     328                 :          0 :                 *bytes_decoded = CtdlDecodeQuotedPrintable(*decoded, part_start, length);
     329                 :          0 :                 return 1;
     330                 :            :         }
     331                 :        103 :         return -1;
     332                 :            : }
     333                 :            : 
     334                 :            : typedef enum _eIntMimeHdrs {
     335                 :            :         boundary,
     336                 :            :         startary,
     337                 :            :         endary,
     338                 :            :         content_type,
     339                 :            :         charset,
     340                 :            :         encoding,
     341                 :            :         content_type_name,
     342                 :            :         content_disposition_name,
     343                 :            :         filename,
     344                 :            :         disposition,
     345                 :            :         id,
     346                 :            :         eMax /* don't move ! */
     347                 :            : } eIntMimeHdrs;
     348                 :            : 
     349                 :            : typedef struct _CBufStr {
     350                 :            :         char Key[SIZ];
     351                 :            :         long len;
     352                 :            : }CBufStr;
     353                 :            : 
     354                 :            : typedef struct _interesting_mime_headers {
     355                 :            :         CBufStr b[eMax];
     356                 :            :         long content_length;
     357                 :            :         long is_multipart;
     358                 :            : } interesting_mime_headers;
     359                 :            : 
     360                 :            : 
     361                 :       3564 : static void FlushInterestingMimes(interesting_mime_headers *m)
     362                 :            : {
     363                 :            :         int i;
     364                 :            :         
     365         [ +  + ]:      42768 :         for (i = 0; i < eMax; i++) {
     366                 :      39204 :              m->b[i].Key[0] = '\0';
     367                 :      39204 :              m->b[i].len = 0;
     368                 :            :         }
     369                 :       3564 :         m->content_length = -1;
     370                 :       3564 : }
     371                 :        908 : static interesting_mime_headers *InitInterestingMimes(void)
     372                 :            : {
     373                 :            :         interesting_mime_headers *m;
     374                 :        908 :         m = (interesting_mime_headers*) malloc( sizeof(interesting_mime_headers));
     375                 :            : 
     376                 :        908 :         FlushInterestingMimes(m);
     377                 :            : 
     378                 :        908 :         return m;
     379                 :            : }
     380                 :            : 
     381                 :            : 
     382                 :       3974 : static long parse_MimeHeaders(interesting_mime_headers *m, 
     383                 :            :                               char** pcontent_start, 
     384                 :            :                               char *content_end)
     385                 :            : {
     386                 :            :         char buf[SIZ];
     387                 :            :         char header[SIZ];
     388                 :            :         long headerlen;
     389                 :            :         char *ptr, *pch;
     390                 :       3974 :         int buflen = 0;
     391                 :            :         int i;
     392                 :            : 
     393                 :            :         /* Learn interesting things from the headers */
     394                 :       3974 :         ptr = *pcontent_start;
     395                 :       3974 :         *header = '\0';
     396                 :       3974 :         headerlen = 0;
     397                 :            :         do {
     398                 :      19370 :                 ptr = memreadlinelen(ptr, buf, SIZ, &buflen);
     399                 :            : 
     400         [ +  + ]:     634801 :                 for (i = 0; i < buflen; ++i) {
     401         [ +  + ]:     615431 :                         if (isspace(buf[i])) {
     402                 :      35548 :                                 buf[i] = ' ';
     403                 :            :                         }
     404                 :            :                 }
     405                 :            : 
     406 [ +  + ][ +  + ]:      19370 :                 if (!isspace(buf[0]) && (headerlen > 0)) {
     407         [ +  + ]:      12684 :                         if (!strncasecmp(header, "Content-type:", 13)) {
     408                 :       2626 :                                 memcpy (m->b[content_type].Key, &header[13], headerlen - 12);
     409                 :       2626 :                                 m->b[content_type].len = striplt (m->b[content_type].Key);
     410                 :            : 
     411                 :       2626 :                                 m->b[content_type_name].len = extract_key(m->b[content_type_name].Key, CKEY(m->b[content_type]), HKEY("name"), '=');
     412                 :       2626 :                                 m->b[charset].len           = extract_key(m->b[charset].Key,           CKEY(m->b[content_type]), HKEY("charset"), '=');
     413                 :       2626 :                                 m->b[boundary].len          = extract_key(m->b[boundary].Key,          header,       headerlen,  HKEY("boundary"), '=');
     414                 :            : 
     415                 :            :                                 /* Deal with weird headers */
     416                 :       2626 :                                 pch = strchr(m->b[content_type].Key, ' ');
     417         [ +  + ]:       2626 :                                 if (pch != NULL) {
     418                 :       1930 :                                         *pch = '\0';
     419                 :       1930 :                                         m->b[content_type].len = m->b[content_type].Key - pch;
     420                 :            :                                 }
     421                 :       2626 :                                 pch = strchr(m->b[content_type].Key, ';');
     422         [ +  + ]:       2626 :                                 if (pch != NULL) {
     423                 :       1930 :                                         *pch = '\0';
     424                 :       2626 :                                         m->b[content_type].len = m->b[content_type].Key - pch;
     425                 :            :                                 }
     426                 :            :                         }
     427         [ +  + ]:      10058 :                         else if (!strncasecmp(header, "Content-Disposition:", 20)) {
     428                 :       1055 :                                 memcpy (m->b[disposition].Key, &header[20], headerlen - 19);
     429                 :       1055 :                                 m->b[disposition].len = striplt(m->b[disposition].Key);
     430                 :            : 
     431                 :       1055 :                                 m->b[content_disposition_name].len = extract_key(m->b[content_disposition_name].Key, CKEY(m->b[disposition]), HKEY("name"), '=');
     432                 :       1055 :                                 m->b[filename].len                 = extract_key(m->b[filename].Key,                 CKEY(m->b[disposition]), HKEY("filename"), '=');
     433                 :       1055 :                                 pch = strchr(m->b[disposition].Key, ';');
     434         [ +  + ]:       1055 :                                 if (pch != NULL) *pch = '\0';
     435                 :       1055 :                                 m->b[disposition].len = striplt(m->b[disposition].Key);
     436                 :            :                         }
     437         [ +  + ]:       9003 :                         else if (!strncasecmp(header, "Content-ID:", 11)) {
     438                 :       1240 :                                 memcpy(m->b[id].Key, &header[11], headerlen);
     439                 :       1240 :                                 striplt(m->b[id].Key);
     440                 :       1240 :                                 m->b[id].len = stripallbut(m->b[id].Key, '<', '>');
     441                 :            :                         }
     442         [ +  + ]:       7763 :                         else if (!strncasecmp(header, "Content-length: ", 15)) {
     443                 :            :                                 char *clbuf;
     444                 :         66 :                                 clbuf = &header[15];
     445         [ +  + ]:        132 :                                 while (isspace(*clbuf))
     446                 :         66 :                                         clbuf ++;
     447                 :         66 :                                 m->content_length = (size_t) atol(clbuf);
     448                 :            :                         }
     449         [ +  + ]:       7697 :                         else if (!strncasecmp(header, "Content-transfer-encoding: ", 26)) {
     450                 :       1749 :                                 memcpy(m->b[encoding].Key, &header[26], headerlen - 26);
     451                 :       1749 :                                 m->b[encoding].len = striplt(m->b[encoding].Key);
     452                 :            :                         }
     453                 :      12684 :                         *header = '\0';
     454                 :      12684 :                         headerlen = 0;
     455                 :            :                 }
     456         [ +  - ]:      19370 :                 if ((headerlen + buflen + 2) < SIZ) {
     457                 :      19370 :                         memcpy(&header[headerlen], buf, buflen);
     458                 :      19370 :                         headerlen += buflen;
     459                 :      19370 :                         header[headerlen] = '\0';
     460                 :            :                 }
     461         [ +  + ]:      19370 :                 if (ptr >= content_end) {
     462                 :         64 :                         return -1;
     463                 :            :                 }
     464 [ +  + ][ +  - ]:      19306 :         } while ((!IsEmptyStr(buf)) && (*ptr != 0));
     465                 :            : 
     466                 :       3910 :         m->is_multipart = m->b[boundary].len != 0;
     467                 :       3910 :         *pcontent_start = ptr;
     468                 :            : 
     469                 :       3974 :         return 0;
     470                 :            : }
     471                 :            : 
     472                 :            : 
     473                 :       3446 : static int IsAsciiEncoding(interesting_mime_headers *m)
     474                 :            : {
     475                 :            : 
     476 [ +  + ][ +  + ]:       3446 :         if ((m->b[encoding].len != 0) &&
     477                 :       1724 :             (strcasecmp(m->b[encoding].Key, "base64") == 0))
     478                 :       1344 :                 return 1;
     479 [ +  + ][ +  + ]:       2102 :         if ((m->b[encoding].len != 0) &&
     480                 :        380 :             (strcmp(m->b[encoding].Key, "quoted-printable") == 0))
     481                 :        134 :                 return 1;
     482                 :            : 
     483                 :       3446 :         return 0;
     484                 :            : }
     485                 :            : 
     486                 :       3446 : static char *FindNextContent(char *ptr,
     487                 :            :                              char *content_end,
     488                 :            :                              interesting_mime_headers *SubMimeHeaders,
     489                 :            :                              interesting_mime_headers *m)
     490                 :            : {
     491                 :            :         char *next_boundary;
     492                 :            :         char  tmp;
     493                 :            : 
     494         [ +  + ]:       3446 :         if (IsAsciiEncoding(SubMimeHeaders)) {
     495                 :       1478 :                 tmp = *content_end;
     496                 :       1478 :                 *content_end = '\0';
     497                 :            : 
     498                 :            :                 /** 
     499                 :            :                  * ok, if we have a content length of the mime part, 
     500                 :            :                  * try skipping the content on the search for the next
     501                 :            :                  * boundary. since we don't trust the content_length
     502                 :            :                  * to be all accurate, and suspect it to lose one digit 
     503                 :            :                  * per line with a line length of 80 chars, we need 
     504                 :            :                  * to start searching a little before..
     505                 :            :                  */
     506                 :            :                                    
     507 [ -  + ][ #  # ]:       1478 :                 if ((SubMimeHeaders->content_length != -1) &&
     508                 :          0 :                     (SubMimeHeaders->content_length > 10))
     509                 :            :                 {
     510                 :            :                         char *pptr;
     511                 :            :                         long lines;
     512                 :            :                                         
     513                 :          0 :                         lines = SubMimeHeaders->content_length / 80;
     514                 :          0 :                         pptr = ptr + SubMimeHeaders->content_length - lines - 10;
     515         [ #  # ]:          0 :                         if (pptr < content_end)
     516                 :          0 :                                 ptr = pptr;
     517                 :            :                 }
     518                 :            :                         
     519                 :       1478 :                 next_boundary = strstr(ptr, m->b[startary].Key);
     520                 :       1478 :                 *content_end = tmp;
     521                 :            :         }
     522                 :            :         else {
     523                 :            :                 char *srch;
     524                 :            :                 /** 
     525                 :            :                  * ok, if we have a content length of the mime part, 
     526                 :            :                  * try skipping the content on the search for the next
     527                 :            :                  * boundary. since we don't trust the content_length
     528                 :            :                  * to be all accurate, start searching a little before..
     529                 :            :                  */
     530                 :            :                                    
     531 [ +  + ][ +  - ]:       1968 :                 if ((SubMimeHeaders->content_length != -1) &&
     532                 :         18 :                     (SubMimeHeaders->content_length > 10))
     533                 :            :                 {
     534                 :            :                         char *pptr;
     535                 :         18 :                         pptr = ptr + SubMimeHeaders->content_length - 10;
     536         [ +  - ]:         18 :                         if (pptr < content_end)
     537                 :         18 :                                 ptr = pptr;
     538                 :            :                 }
     539                 :            :                 
     540                 :            : 
     541                 :       1968 :                 next_boundary = NULL;
     542 [ +  + ][ +  - ]:      42992 :                 for (srch=ptr; 
     543                 :            :                      (srch != NULL) && (srch < content_end); 
     544                 :      41024 :                      srch = memchr(srch, '-',  content_end - srch)) 
     545                 :            :                 {
     546         [ +  + ]:      41024 :                         if (!memcmp(srch, 
     547                 :      41024 :                                     m->b[startary].Key, 
     548                 :      41024 :                                     m->b[startary].len)) 
     549                 :            :                         {
     550                 :       1915 :                                 next_boundary = srch;
     551                 :       1915 :                                 srch = content_end;
     552                 :            :                         }
     553                 :      39109 :                         else srch ++;
     554                 :            : 
     555                 :            :                 }
     556                 :            : 
     557                 :            :         }
     558                 :       3446 :         return next_boundary;
     559                 :            : }
     560                 :            : 
     561                 :            : /*
     562                 :            :  * Break out the components of a multipart message
     563                 :            :  * (This function expects to be fed HEADERS + CONTENT)
     564                 :            :  * Note: NULL can be supplied as content_end; in this case, the message is
     565                 :            :  * considered to have ended when the parser encounters a 0x00 byte.
     566                 :            :  */
     567                 :       3067 : static void recurseable_mime_parser(char *partnum,
     568                 :            :                                     char *content_start, char *content_end,
     569                 :            :                                     MimeParserCallBackType CallBack,
     570                 :            :                                     MimeParserCallBackType PreMultiPartCallBack,
     571                 :            :                                     MimeParserCallBackType PostMultiPartCallBack,
     572                 :            :                                     void *userdata,
     573                 :            :                                     int dont_decode, 
     574                 :            :                                     interesting_mime_headers *m)
     575                 :            : {
     576                 :            :         interesting_mime_headers *SubMimeHeaders;
     577                 :            :         char     *ptr;
     578                 :            :         char     *part_start;
     579                 :       3067 :         char     *part_end = NULL;
     580                 :       3067 :         char     *evaluate_crlf_ptr = NULL;
     581                 :            :         char     *next_boundary;
     582                 :            :         char      nested_partnum[256];
     583                 :       3067 :         int       crlf_in_use = 0;
     584                 :       3067 :         int       part_seq = 0;
     585                 :            :         CBufStr  *chosen_name;
     586                 :            : 
     587                 :            : 
     588                 :            :         /* If this is a multipart message, then recursively process it */
     589                 :       3067 :         ptr = content_start;
     590                 :       3067 :         part_start = NULL;
     591         [ +  + ]:       3067 :         if (m->is_multipart) {
     592                 :            : 
     593                 :            :                 /* Tell the client about this message's multipartedness */
     594         [ +  + ]:        444 :                 if (PreMultiPartCallBack != NULL) {
     595                 :         90 :                         PreMultiPartCallBack("", 
     596                 :            :                                              "", 
     597                 :            :                                              partnum, 
     598                 :            :                                              "",
     599                 :            :                                              NULL, 
     600                 :         90 :                                              m->b[content_type].Key, 
     601                 :         90 :                                              m->b[charset].Key,
     602                 :            :                                              0, 
     603                 :         90 :                                              m->b[encoding].Key, 
     604                 :         90 :                                              m->b[id].Key, 
     605                 :            :                                              userdata);
     606                 :            :                 }
     607                 :            : 
     608                 :            :                 /* Figure out where the boundaries are */
     609                 :        444 :                 m->b[startary].len = snprintf(m->b[startary].Key, SIZ, "--%s", m->b[boundary].Key);
     610                 :        444 :                 SubMimeHeaders = InitInterestingMimes ();
     611         [ -  + ]:        444 :                 if (*ptr == '\r')
     612                 :          0 :                         ptr ++;
     613         [ +  + ]:        444 :                 if (*ptr == '\n')
     614                 :        101 :                         ptr ++;
     615         [ +  + ]:        444 :                 if (strncmp(ptr, m->b[startary].Key, m->b[startary].len) == 0)
     616                 :        258 :                         ptr += m->b[startary].len;
     617         [ +  + ]:        444 :                 if (*ptr == '\r')
     618                 :         43 :                         ptr ++;
     619         [ +  + ]:        444 :                 if (*ptr == '\n')
     620                 :        258 :                         ptr ++;
     621                 :        444 :                 part_start = NULL;
     622                 :            :                 do {
     623                 :            : 
     624         [ +  + ]:       3510 :                         if (parse_MimeHeaders(SubMimeHeaders, &ptr, content_end) != 0)
     625                 :         64 :                                 break;
     626                 :       3446 :                         part_start = ptr;
     627                 :            :                         
     628                 :       3446 :                         next_boundary = FindNextContent(ptr,
     629                 :            :                                                         content_end,
     630                 :            :                                                         SubMimeHeaders,
     631                 :            :                                                         m);
     632   [ +  +  +  + ]:       3446 :                         if ((next_boundary != NULL) && 
     633                 :       3393 :                             (next_boundary - part_start < 3))
     634                 :        790 :                                 continue;
     635                 :            : 
     636 [ +  - ][ +  + ]:       2656 :                         if ( (part_start != NULL) && (next_boundary != NULL) ) {
     637                 :       2603 :                                 part_end = next_boundary;
     638                 :       2603 :                                 --part_end;             /* omit the trailing LF */
     639         [ +  + ]:       2603 :                                 if (crlf_in_use) {
     640                 :        409 :                                         --part_end;     /* omit the trailing CR */
     641                 :            :                                 }
     642                 :            : 
     643         [ +  + ]:       2603 :                                 if (!IsEmptyStr(partnum)) {
     644                 :        558 :                                         snprintf(nested_partnum,
     645                 :            :                                                  sizeof nested_partnum,
     646                 :            :                                                  "%s.%d", partnum,
     647                 :            :                                                  ++part_seq);
     648                 :            :                                 }
     649                 :            :                                 else {
     650                 :       2045 :                                         snprintf(nested_partnum,
     651                 :            :                                                  sizeof nested_partnum,
     652                 :            :                                                  "%d", ++part_seq);
     653                 :            :                                 }
     654                 :       2603 :                                 recurseable_mime_parser(nested_partnum,
     655                 :            :                                                         part_start, 
     656                 :            :                                                         part_end,
     657                 :            :                                                         CallBack,
     658                 :            :                                                         PreMultiPartCallBack,
     659                 :            :                                                         PostMultiPartCallBack,
     660                 :            :                                                         userdata,
     661                 :            :                                                         dont_decode, 
     662                 :            :                                                         SubMimeHeaders);
     663                 :            :                         }
     664                 :            : 
     665         [ +  + ]:       2656 :                         if (next_boundary != NULL) {
     666                 :            :                                 /* If we pass out of scope, don't attempt to
     667                 :            :                                  * read past the end boundary. */
     668 [ +  + ][ -  + ]:       2603 :                                 if ((*(next_boundary + m->b[startary].len + 1) == '-') && 
     669                 :        439 :                                     (*(next_boundary + m->b[startary].len + 2) == '-') ){
     670                 :          0 :                                         ptr = content_end;
     671                 :            :                                 }
     672                 :            :                                 else {
     673                 :            :                                         /* Set up for the next part. */
     674                 :       2603 :                                         part_start = strstr(next_boundary, "\n");
     675                 :            :                                         
     676                 :            :                                         /* Determine whether newlines are LF or CRLF */
     677                 :       2603 :                                         evaluate_crlf_ptr = part_start;
     678                 :       2603 :                                         --evaluate_crlf_ptr;
     679 [ +  + ][ +  - ]:       2603 :                                         if ((*evaluate_crlf_ptr == '\r') && 
     680                 :        452 :                                             (*(evaluate_crlf_ptr + 1) == '\n'))
     681                 :            :                                         {
     682                 :        452 :                                                 crlf_in_use = 1;
     683                 :            :                                         }
     684                 :            :                                         else {
     685                 :       2151 :                                                 crlf_in_use = 0;
     686                 :            :                                         }
     687                 :            : 
     688                 :            :                                         /* Advance past the LF ... now we're in the next part */
     689                 :       2603 :                                         ++part_start;
     690                 :       2603 :                                         ptr = part_start;
     691                 :            :                                 }
     692                 :            :                         }
     693                 :            :                         else {
     694                 :            :                                 /* Invalid end of multipart.  Bail out! */
     695                 :         53 :                                 ptr = content_end;
     696                 :            :                         }
     697                 :       2656 :                         FlushInterestingMimes(SubMimeHeaders);
     698 [ +  + ][ +  - ]:       3446 :                 } while ( (ptr < content_end) && (next_boundary != NULL) );
     699                 :            : 
     700                 :        444 :                 free(SubMimeHeaders);
     701                 :            : 
     702         [ +  + ]:        444 :                 if (PostMultiPartCallBack != NULL) {
     703                 :        444 :                         PostMultiPartCallBack("", 
     704                 :            :                                               "", 
     705                 :            :                                               partnum, 
     706                 :            :                                               "", 
     707                 :            :                                               NULL,
     708                 :         90 :                                               m->b[content_type].Key, 
     709                 :         90 :                                               m->b[charset].Key,
     710                 :            :                                               0, 
     711                 :         90 :                                               m->b[encoding].Key, 
     712                 :         90 :                                               m->b[id].Key, 
     713                 :            :                                               userdata);
     714                 :            :                 }
     715                 :            :         } /* If it's not a multipart message, then do something with it */
     716                 :            :         else {
     717                 :            :                 size_t length;
     718                 :       2623 :                 part_start = ptr;
     719                 :       2623 :                 length = content_end - part_start;
     720                 :       2623 :                 ptr = part_end = content_end;
     721                 :            : 
     722                 :            : 
     723                 :            :                 /* The following code will truncate the MIME part to the size
     724                 :            :                  * specified by the Content-length: header.   We have commented it
     725                 :            :                  * out because these headers have a tendency to be wrong.
     726                 :            :                  *
     727                 :            :                  *      if ( (content_length > 0) && (length > content_length) ) {
     728                 :            :                  *              length = content_length;
     729                 :            :                  *      }
     730                 :            :                  */
     731                 :            : 
     732                 :            :                 /* Sometimes the "name" field is tacked on to Content-type,
     733                 :            :                  * and sometimes it's tacked on to Content-disposition.  Use
     734                 :            :                  * whichever one we have.
     735                 :            :                  */
     736         [ +  + ]:       2623 :                 if (m->b[content_disposition_name].len > m->b[content_type_name].len) {
     737                 :        463 :                         chosen_name = &m->b[content_disposition_name];
     738                 :            :                 }
     739                 :            :                 else {
     740                 :       2160 :                         chosen_name = &m->b[content_type_name];
     741                 :            :                 }
     742                 :            :         
     743                 :            :                 /* Ok, we've got a non-multipart part here, so do something with it.
     744                 :            :                  */
     745                 :       2623 :                 mime_decode(partnum,
     746                 :            :                             part_start, 
     747                 :            :                             length,
     748                 :       2623 :                             m->b[content_type].Key, 
     749                 :       2623 :                             m->b[charset].Key,
     750                 :       2623 :                             m->b[encoding].Key, 
     751                 :       2623 :                             m->b[disposition].Key, 
     752                 :       2623 :                             m->b[id].Key, 
     753                 :            :                             chosen_name->Key, 
     754                 :       2623 :                             m->b[filename].Key,
     755                 :            :                             CallBack, 
     756                 :            :                             NULL, NULL,
     757                 :            :                             userdata, 
     758                 :            :                             dont_decode
     759                 :            :                         );
     760                 :            : 
     761                 :            :                 /*
     762                 :            :                  * Now if it's an encapsulated message/rfc822 then we have to recurse into it
     763                 :            :                  */
     764         [ +  + ]:       2623 :                 if (!strcasecmp(&m->b[content_type].Key[0], "message/rfc822")) {
     765                 :            : 
     766         [ +  + ]:        277 :                         if (PreMultiPartCallBack != NULL) {
     767                 :         45 :                                 PreMultiPartCallBack("", 
     768                 :            :                                                      "", 
     769                 :            :                                                      partnum, 
     770                 :            :                                                      "",
     771                 :            :                                                      NULL, 
     772                 :         45 :                                                      m->b[content_type].Key, 
     773                 :         45 :                                                      m->b[charset].Key,
     774                 :            :                                                      0, 
     775                 :         45 :                                                      m->b[encoding].Key, 
     776                 :         45 :                                                      m->b[id].Key, 
     777                 :            :                                                      userdata);
     778                 :            :                         }
     779         [ +  + ]:        277 :                         if (CallBack != NULL) {
     780         [ +  - ]:        274 :                                 if (strlen(partnum) > 0) {
     781                 :        274 :                                         snprintf(nested_partnum,
     782                 :            :                                                  sizeof nested_partnum,
     783                 :            :                                                  "%s.%d", partnum,
     784                 :            :                                                  ++part_seq);
     785                 :            :                                 }
     786                 :            :                                 else {
     787                 :          0 :                                         snprintf(nested_partnum,
     788                 :            :                                                  sizeof nested_partnum,
     789                 :            :                                                  "%d", ++part_seq);
     790                 :            :                                 }
     791                 :        274 :                                 the_mime_parser(nested_partnum,
     792                 :            :                                                 part_start, 
     793                 :            :                                                 part_end,
     794                 :            :                                                 CallBack,
     795                 :            :                                                 PreMultiPartCallBack,
     796                 :            :                                                 PostMultiPartCallBack,
     797                 :            :                                                 userdata,
     798                 :            :                                                 dont_decode
     799                 :            :                                         );
     800                 :            :                         }
     801         [ +  + ]:        277 :                         if (PostMultiPartCallBack != NULL) {
     802                 :         45 :                                 PostMultiPartCallBack("", 
     803                 :            :                                                       "", 
     804                 :            :                                                       partnum, 
     805                 :            :                                                       "", 
     806                 :            :                                                       NULL,
     807                 :         45 :                                                       m->b[content_type].Key, 
     808                 :         45 :                                                       m->b[charset].Key,
     809                 :            :                                                       0, 
     810                 :         45 :                                                       m->b[encoding].Key, 
     811                 :         45 :                                                       m->b[id].Key, 
     812                 :            :                                                       userdata);
     813                 :            :                         }
     814                 :            : 
     815                 :            : 
     816                 :            :                 }
     817                 :            : 
     818                 :            :         }
     819                 :            : 
     820                 :       3067 : }
     821                 :            : 
     822                 :            : /*
     823                 :            :  * Break out the components of a multipart message
     824                 :            :  * (This function expects to be fed HEADERS + CONTENT)
     825                 :            :  * Note: NULL can be supplied as content_end; in this case, the message is
     826                 :            :  * considered to have ended when the parser encounters a 0x00 byte.
     827                 :            :  */
     828                 :        464 : void the_mime_parser(char *partnum,
     829                 :            :                      char *content_start, char *content_end,
     830                 :            :                      MimeParserCallBackType CallBack,
     831                 :            :                      MimeParserCallBackType PreMultiPartCallBack,
     832                 :            :                      MimeParserCallBackType PostMultiPartCallBack,
     833                 :            :                      void *userdata,
     834                 :            :                      int dont_decode)
     835                 :            : {
     836                 :            :         interesting_mime_headers *m;
     837                 :            : 
     838                 :            :         /* If the caller didn't supply an endpointer, generate one by measure */
     839         [ -  + ]:        464 :         if (content_end == NULL) {
     840                 :          0 :                 content_end = &content_start[strlen(content_start)];
     841                 :            :         }
     842                 :            : 
     843                 :        464 :         m = InitInterestingMimes();
     844                 :            : 
     845         [ +  - ]:        464 :         if (!parse_MimeHeaders(m, &content_start, content_end))
     846                 :            :         {
     847                 :            : 
     848                 :        464 :                 recurseable_mime_parser(partnum,
     849                 :            :                                         content_start, content_end,
     850                 :            :                                         CallBack,
     851                 :            :                                         PreMultiPartCallBack,
     852                 :            :                                         PostMultiPartCallBack,
     853                 :            :                                         userdata,
     854                 :            :                                         dont_decode,
     855                 :            :                                         m);
     856                 :            :         }
     857                 :        464 :         free(m);
     858                 :        464 : }
     859                 :            : 
     860                 :            : /*
     861                 :            :  * Entry point for the MIME parser.
     862                 :            :  * (This function expects to be fed HEADERS + CONTENT)
     863                 :            :  * Note: NULL can be supplied as content_end; in this case, the message is
     864                 :            :  * considered to have ended when the parser encounters a 0x00 byte.
     865                 :            :  */
     866                 :        190 : void mime_parser(char *content_start,
     867                 :            :                  char *content_end,
     868                 :            :                  MimeParserCallBackType CallBack,
     869                 :            :                  MimeParserCallBackType PreMultiPartCallBack,
     870                 :            :                  MimeParserCallBackType PostMultiPartCallBack,
     871                 :            :                  void *userdata,
     872                 :            :                  int dont_decode)
     873                 :            : {
     874                 :            : 
     875                 :        190 :         the_mime_parser("", content_start, content_end,
     876                 :            :                         CallBack,
     877                 :            :                         PreMultiPartCallBack,
     878                 :            :                         PostMultiPartCallBack,
     879                 :            :                         userdata, dont_decode);
     880                 :        190 : }
     881                 :            : 
     882                 :            : 
     883                 :            : 
     884                 :            : 
     885                 :            : 
     886                 :            : 
     887                 :            : typedef struct _MimeGuess {
     888                 :            :         const char *Pattern;
     889                 :            :         size_t PatternLen;
     890                 :            :         long PatternOffset;
     891                 :            :         const char *MimeString;
     892                 :            : } MimeGuess;
     893                 :            : 
     894                 :            : MimeGuess MyMimes [] = {
     895                 :            :         {
     896                 :            :                 "GIF",
     897                 :            :                 3,
     898                 :            :                 0,
     899                 :            :                 "image/gif"
     900                 :            :         },
     901                 :            :         {
     902                 :            :                 "\xff\xd8",
     903                 :            :                 2,
     904                 :            :                 0,
     905                 :            :                 "image/jpeg"
     906                 :            :         },
     907                 :            :         {
     908                 :            :                 "\x89PNG",
     909                 :            :                 4,
     910                 :            :                 0,
     911                 :            :                 "image/png"
     912                 :            :         },
     913                 :            :         { // last...
     914                 :            :                 "",
     915                 :            :                 0,
     916                 :            :                 0,
     917                 :            :                 ""
     918                 :            :         }
     919                 :            : };
     920                 :            : 
     921                 :            : 
     922                 :          4 : const char *GuessMimeType(const char *data, size_t dlen)
     923                 :            : {
     924                 :          4 :         int MimeIndex = 0;
     925                 :            : 
     926         [ +  + ]:         12 :         while (MyMimes[MimeIndex].PatternLen != 0)
     927                 :            :         {
     928 [ +  - ][ +  + ]:         10 :                 if ((MyMimes[MimeIndex].PatternLen + 
     929                 :         10 :                      MyMimes[MimeIndex].PatternOffset < dlen) &&
     930                 :            :                     strncmp(MyMimes[MimeIndex].Pattern, 
     931                 :         10 :                             &data[MyMimes[MimeIndex].PatternOffset], 
     932                 :         20 :                             MyMimes[MimeIndex].PatternLen) == 0)
     933                 :            :                 {
     934                 :          2 :                         return MyMimes[MimeIndex].MimeString;
     935                 :            :                 }
     936                 :          8 :                 MimeIndex ++;
     937                 :            :         }
     938                 :            :         /* 
     939                 :            :          * ok, our simple minded algorythm didn't find anything, 
     940                 :            :          * let the big chegger try it, he wil default to application/octet-stream
     941                 :            :          */
     942                 :          4 :         return (xdg_mime_get_mime_type_for_data(data, dlen));
     943                 :            : }
     944                 :            : 
     945                 :            : 
     946                 :         21 : const char* GuessMimeByFilename(const char *what, size_t len)
     947                 :            : {
     948                 :            :         /* we know some hardcoded on our own, try them... */
     949 [ +  + ][ +  + ]:         21 :         if ((len > 3) && !strncasecmp(&what[len - 4], ".gif", 4))
     950                 :          1 :                 return "image/gif";
     951 [ +  + ][ +  + ]:         20 :         else if ((len > 2) && !strncasecmp(&what[len - 3], ".js", 3))
     952                 :          1 :                 return  "text/javascript";
     953 [ +  + ][ +  + ]:         19 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".txt", 4))
     954                 :          1 :                 return "text/plain";
     955 [ +  + ][ +  + ]:         18 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".css", 4))
     956                 :          1 :                 return "text/css";
     957 [ +  + ][ +  + ]:         17 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".htc", 4))
     958                 :          1 :                 return "text/x-component";
     959 [ +  + ][ +  + ]:         16 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".jpg", 4))
     960                 :          1 :                 return "image/jpeg";
     961 [ +  + ][ +  + ]:         15 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".png", 4))
     962                 :          2 :                 return "image/png";
     963 [ +  + ][ +  + ]:         13 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".ico", 4))
     964                 :          1 :                 return "image/x-icon";
     965 [ +  + ][ +  + ]:         12 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".vcf", 4))
     966                 :          1 :                 return "text/x-vcard";
     967 [ +  + ][ +  + ]:         11 :         else if ((len > 4) && !strncasecmp(&what[len - 5], ".html", 5))
     968                 :          1 :                 return "text/html";
     969 [ +  + ][ +  + ]:         10 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".htm", 4))
     970                 :          1 :                 return "text/html";
     971 [ +  + ][ +  + ]:          9 :         else if ((len > 3) && !strncasecmp(&what[len - 4], ".wml", 4))
     972                 :          1 :                 return "text/vnd.wap.wml";
     973 [ +  + ][ +  + ]:          8 :         else if ((len > 4) && !strncasecmp(&what[len - 5], ".wmls", 5))
     974                 :          1 :                 return "text/vnd.wap.wmlscript";
     975 [ +  + ][ +  + ]:          7 :         else if ((len > 4) && !strncasecmp(&what[len - 5], ".wmlc", 5))
     976                 :          1 :                 return "application/vnd.wap.wmlc";
     977 [ +  + ][ +  + ]:          6 :         else if ((len > 5) && !strncasecmp(&what[len - 6], ".wmlsc", 6))
     978                 :          1 :                 return "application/vnd.wap.wmlscriptc";
     979 [ +  + ][ +  + ]:          5 :         else if ((len > 4) && !strncasecmp(&what[len - 5], ".wbmp", 5))
     980                 :          1 :                 return "image/vnd.wap.wbmp";
     981                 :            :         else
     982                 :            :                 /* and let xdgmime do the fallback. */
     983                 :         21 :                 return xdg_mime_get_mime_type_from_file_name(what);
     984                 :            : }
     985                 :            : 
     986                 :            : static HashList *IconHash = NULL;
     987                 :            : 
     988                 :            : typedef struct IconName IconName;
     989                 :            : 
     990                 :            : struct IconName {
     991                 :            :         char *FlatName;
     992                 :            :         char *FileName;
     993                 :            : };
     994                 :            : 
     995                 :          0 : static void DeleteIcon(void *IconNamePtr)
     996                 :            : {
     997                 :          0 :         IconName *Icon = (IconName*) IconNamePtr;
     998                 :          0 :         free(Icon->FlatName);
     999                 :          0 :         free(Icon->FileName);
    1000                 :          0 :         free(Icon);
    1001                 :          0 : }
    1002                 :            : 
    1003                 :            : /*
    1004                 :            : static const char *PrintFlat(void *IconNamePtr)
    1005                 :            : {
    1006                 :            :         IconName *Icon = (IconName*) IconNamePtr;
    1007                 :            :         return Icon->FlatName;
    1008                 :            : }
    1009                 :            : static const char *PrintFile(void *IconNamePtr)
    1010                 :            : {
    1011                 :            :         IconName *Icon = (IconName*) IconNamePtr;
    1012                 :            :         return Icon->FileName;
    1013                 :            : }
    1014                 :            : */
    1015                 :            : 
    1016                 :            : #define GENSTR "x-generic"
    1017                 :            : #define IGNORE_PREFIX_1 "gnome-mime"
    1018                 :         25 : int LoadIconDir(const char *DirName)
    1019                 :            : {
    1020                 :         25 :         DIR *filedir = NULL;
    1021                 :            :         struct dirent *filedir_entry;
    1022                 :            :         int d_namelen;
    1023                 :            :         int d_without_ext;
    1024                 :            :         IconName *Icon;
    1025                 :            : 
    1026                 :         25 :         filedir = opendir (DirName);
    1027                 :         25 :         IconHash = NewHash(1, NULL);
    1028         [ +  - ]:         25 :         if (filedir == NULL) {
    1029                 :         25 :                 return 0;
    1030                 :            :         }
    1031                 :            : 
    1032         [ #  # ]:          0 :         while ((filedir_entry = readdir(filedir)))
    1033                 :            :         {
    1034                 :            :                 char *MinorPtr;
    1035                 :            :                 char *PStart;
    1036                 :            : #ifdef _DIRENT_HAVE_D_NAMELEN
    1037                 :            :                 d_namelen = filedir_entry->d_namelen;
    1038                 :            : #else
    1039                 :          0 :                 d_namelen = strlen(filedir_entry->d_name);
    1040                 :            : #endif
    1041                 :          0 :                 d_without_ext = d_namelen;
    1042 [ #  # ][ #  # ]:          0 :                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
    1043                 :          0 :                         d_without_ext --;
    1044 [ #  # ][ #  # ]:          0 :                 if ((d_without_ext == 0) || (d_namelen < 3))
    1045                 :          0 :                         continue;
    1046                 :            : 
    1047 [ #  # ][ #  # ]:          0 :                 if ((sizeof(IGNORE_PREFIX_1) < d_namelen) &&
    1048                 :          0 :                     (strncmp(IGNORE_PREFIX_1, 
    1049                 :            :                              filedir_entry->d_name, 
    1050                 :            :                              sizeof(IGNORE_PREFIX_1) - 1) == 0)) {
    1051                 :          0 :                         PStart = filedir_entry->d_name + sizeof(IGNORE_PREFIX_1);
    1052                 :          0 :                         d_without_ext -= sizeof(IGNORE_PREFIX_1);
    1053                 :            :                 }
    1054                 :            :                 else {
    1055                 :          0 :                         PStart = filedir_entry->d_name;
    1056                 :            :                 }
    1057                 :          0 :                 Icon = malloc(sizeof(IconName));
    1058                 :            : 
    1059                 :          0 :                 Icon->FileName = malloc(d_namelen + 1);
    1060                 :          0 :                 memcpy(Icon->FileName, filedir_entry->d_name, d_namelen + 1);
    1061                 :            : 
    1062                 :          0 :                 Icon->FlatName = malloc(d_without_ext + 1);
    1063                 :          0 :                 memcpy(Icon->FlatName, PStart, d_without_ext);
    1064                 :          0 :                 Icon->FlatName[d_without_ext] = '\0';
    1065                 :            :                 /* Try to find Minor type in image-jpeg */
    1066                 :          0 :                 MinorPtr = strchr(Icon->FlatName, '-');
    1067         [ #  # ]:          0 :                 if (MinorPtr != NULL) {
    1068                 :            :                         size_t MinorLen;
    1069                 :          0 :                         MinorLen = 1 + d_without_ext - (MinorPtr - Icon->FlatName + 1);
    1070 [ #  # ][ #  # ]:          0 :                         if ((MinorLen == sizeof(GENSTR)) && 
    1071                 :          0 :                             (strncmp(MinorPtr + 1, GENSTR, sizeof(GENSTR)) == 0)) {
    1072                 :            :                                 /* ok, we found a generic filename. cut the generic. */
    1073                 :          0 :                                 *MinorPtr = '\0';
    1074                 :          0 :                                 d_without_ext = d_without_ext - (MinorPtr - Icon->FlatName);
    1075                 :            :                         }
    1076                 :            :                         else { /* Map the major / minor separator to / */
    1077                 :          0 :                                 *MinorPtr = '/';
    1078                 :            :                         }
    1079                 :            :                 }
    1080                 :            : 
    1081                 :            : //              PrintHash(IconHash, PrintFlat, PrintFile);
    1082                 :            : //              printf("%s - %s\n", Icon->FlatName, Icon->FileName);
    1083                 :          0 :                 Put(IconHash, Icon->FlatName, d_without_ext, Icon, DeleteIcon);
    1084                 :            : //              PrintHash(IconHash, PrintFlat, PrintFile);
    1085                 :            :         }
    1086                 :          0 :         closedir(filedir);
    1087                 :         25 :         return 1;
    1088                 :            : }
    1089                 :            : 
    1090                 :         25 : const char *GetIconFilename(char *MimeType, size_t len)
    1091                 :            : {
    1092                 :            :         void *vIcon;
    1093                 :            :         IconName *Icon;
    1094                 :            :         
    1095         [ -  + ]:         25 :         if(IconHash == NULL)
    1096                 :          0 :                 return NULL;
    1097                 :            : 
    1098                 :         25 :         GetHash(IconHash, MimeType, len, &vIcon), Icon = (IconName*) vIcon;
    1099                 :            :         /* didn't find the exact mimetype? try major only. */
    1100         [ +  - ]:         25 :         if (Icon == NULL) {
    1101                 :            :                 char * pMinor;
    1102                 :         25 :                 pMinor = strchr(MimeType, '/');
    1103         [ +  - ]:         25 :                 if (pMinor != NULL) {
    1104                 :         25 :                         *pMinor = '\0';
    1105                 :         25 :                         GetHash(IconHash, MimeType, pMinor - MimeType, &vIcon),
    1106                 :         25 :                                 Icon = (IconName*) vIcon;
    1107                 :            :                 }
    1108                 :            :         }
    1109         [ +  - ]:         25 :         if (Icon == NULL) {
    1110                 :         25 :                 return NULL;
    1111                 :            :         }
    1112                 :            : 
    1113                 :            :         /*printf("Getting: [%s] == [%s] -> [%s]\n", MimeType, Icon->FlatName, Icon->FileName);*/
    1114                 :         25 :         return Icon->FileName;
    1115                 :            : }
    1116                 :            : 
    1117                 :         26 : void ShutDownLibCitadelMime(void)
    1118                 :            : {
    1119                 :         26 :         DeleteHash(&IconHash);
    1120                 :         26 : }

Generated by: LCOV version 1.8