Source File ep/mail_parser.c

    1    | /***************************************
    2    |   $Revision: 1.33 $
    3    |
    4    |   Email Parser module (ep) - wrapping functions to parse email,
    5    |   calling MM and PA.
    6    |
    7    |   Status: NOT REVUED, TESTED
    8    |
    9    |   ******************/ /******************
    10   |   Filename            : mail_parser.c
    11   |   Authors             : Filippo Portera, Daniele Arena
    12   |   OSs Tested          : Solaris 7
    13   |   ******************/ /******************
    14   |   Copyright (c) 2000,2001,2002                    RIPE NCC
    15   |
    16   |   All Rights Reserved
    17   |
    18   |   Permission to use, copy, modify, and distribute this software and its
    19   |   documentation for any purpose and without fee is hereby granted,
    20   |   provided that the above copyright notice appear in all copies and that
    21   |   both that copyright notice and this permission notice appear in
    22   |   supporting documentation, and that the name of the author not be
    23   |   used in advertising or publicity pertaining to distribution of the
    24   |   software without specific, written prior permission.
    25   |
    26   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    27   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    28   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    29   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    30   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    31   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    32   |   ***************************************/
    33   |
    34   | #include <stdio.h>
    35   | #include <string.h>
    36   | #include <stdlib.h>
    37   | #include <netdb.h>
    38   | #include <sys/param.h>
    39   |
    40   | #include "mm.h"
    41   | #include "gpg.h"
    42   | #include "mail_parser.h"
    43   |
    44   | /* Parse the mail message stored in inputFile and develop it
    45   |    in distinct text files, writing them on the outputPath
    46   |    directory and using the variable keyRing to read public
    47   |    keys needed for the verification process.
    48   |
    49   |    The common use of this parse should look like this:
    50   |
    51   |    p = EP_ParseMessage("mail.001", "/tmp", "~/.gnupg/pubring.gpg");
    52   |
    53   |    < parse the tree: p->tree >
    54   |
    55   |    EP_TreeCleanUp(p);
    56   |
    57   | */
    58   |
    59   | /* Globals to store shared data for tree nodes */
    60   |
    61   | char EP_outputPrefix[FILENAME_LENGTH];
    62   | /* char EP_keyRing[FILENAME_LENGTH];
    63   |    char EP_gpgcmd[FILENAME_LENGTH]; */
    64   | int  EP_TreeHeight;
    65   | int  EP_Node_ID;
    66   | int  EP_Debug;
    67   |
    68   | const char *vS_strRC[] = { "IS_VALID",
    69   | 			   "IS_NOT_PGP",
    70   | 			   "KO",
    71   | 			   "CRC_ERROR",
    72   | 			   "NO_PUBLIC_KEY",
    73   | 			   "NO_OPENPGP_DATA",
    74   | 			   "NO_IN_FILES",
    75   | 			   "NO_OUT_FILES",
    76   | 			   "TO_BE_PGPVERIFIED",
    77   | 			   "UNABLE_TO_WRITE_FILE",
    78   | 			   "UNMATCHED_PGP_DELIMITERS"
    79   |                          };
    80   |
    81   | #define EP_TREEMAXHEIGHT 10;
    82   |
    83   | EP_Mail_DescrPtr InitializeMailDescr( const char *inputFile ) {
    84   |
    85   |   EP_Mail_DescrPtr ptr;
    86   |   /*  EPNodePtr rootNode; */
    87   |   int retcode;
    88   |   long debug = 0;
    89   |
    90   |   ptr = UT_malloc(sizeof(EP_Mail_Descr));
    91   |
    92   |   ptr->from = ptr->subject = ptr->date =
    93   |   ptr->message_id = ptr->reply_to = ptr->cc =
    94   |   ptr->content_type = NULL ;
    95   |
    96   |
    97   |   /* Obtain headers */
    98   |   retcode = MM_get_headers(inputFile, ptr, debug);
    99   |
    100  |   ptr->tree = EP_InitializeRootNode(inputFile);
    101  |
    102  |   return ptr;
    103  | }
    104  |
    105  | /* ------------------------------------------------- */
    106  |
    107  | EP_Mail_DescrPtr EP_ParseMail(const char *inputFile,
    108  | 			      const char *outputPath) {
    109  |   EP_Mail_DescrPtr ptr;
    110  |   char hostname[MAXHOSTNAMELEN];
    111  |   int retcode;
    112  |   long debug = 0;
    113  |   char mail_file[FILENAMELEN];
    114  |
    115  |   EP_Debug = debug;
    116  |
    117  |   gethostname(hostname, MAXHOSTNAMELEN);
    118  |   sprintf(EP_outputPrefix, "%s/EPMtmp.%s.%d.", outputPath,
    119  | 	  hostname, getpid());
    120  |   PA_SetOutputPrefix(EP_outputPrefix);
    121  |
    122  |   /*
    123  |   strcpy(EP_keyRing, keyRing);
    124  |   strcpy(EP_gpgcmd, gpgcmd);
    125  |   */
    126  |
    127  |   sprintf (mail_file,"%sunprocessed", EP_outputPrefix);      /* the file where the mail message will be stored */
    128  |
    129  |   /* if ((retcode = MM_store((char*)inputFile,mail_file, debug)) != 0)
    130  |     exit (retcode); */
    131  |
    132  |   MM_store((char*)inputFile,mail_file, debug, 0);
    133  |
    134  |   ptr = InitializeMailDescr(mail_file);
    135  |   /* Invoke the MIME parser */
    136  |   retcode = MM_extract_mime(mail_file, NULL, ptr->tree, debug);
    137  |
    138  |   return ptr;
    139  | }
    140  |
    141  | /* ------------------------------------------------- */
    142  |
    143  | EPNodePtr EP_ParseText(const char *inputFile,
    144  | 		       const char *outputPath) {
    145  |   EPNodePtr ptr;
    146  |   char hostname[MAXHOSTNAMELEN];
    147  |
    148  |   EP_Debug = 0;
    149  |
    150  |   gethostname(hostname, MAXHOSTNAMELEN);
    151  |   sprintf(EP_outputPrefix, "%s/EPTtmp.%s.%d.", outputPath,
    152  | 	  hostname, getpid());
    153  |
    154  |   /*
    155  |   strcpy(EP_keyRing, keyRing);
    156  |   strcpy(EP_gpgcmd, gpgcmd);
    157  |   */
    158  |
    159  |   ptr = EP_InitializeRootNode(inputFile);
    160  |
    161  |   PA_SetOutputPrefix(EP_outputPrefix);
    162  |   return PA_ParseMessage(ptr);
    163  | }
    164  |
    165  |
    166  | /* ------------------------------------------------- */
    167  |
    168  | EPNodePtr EP_MIMEParse(const EPNodePtr p)
    169  | {
    170  |   char mail_file[FILENAMELEN];
    171  |   int retcode;
    172  |   FILE * fin;
    173  |   char *strptr;
    174  |   int found = 0, headers_end = 0;
    175  |   char txt[MAX_LINE_BUF];
    176  |
    177  |   sprintf (mail_file,"%s%d.unprocessed", EP_outputPrefix, p->nodeID);      /* the file where the mail message will be stored */
    178  |
    179  |   PA_SetOutputPrefix(EP_outputPrefix);
    180  |   /* Quest for a mail header:
    181  |      look for a mail header of type (content-type || mime version).
    182  |   */
    183  |
    184  |   if ((fin = fopen(p->file, "r")) != NULL) {
    185  |     while ( !headers_end && !found &&
    186  | 	    (strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL) {
    187  |       if ( do_regex_test("^Content-Type:", txt) ||
    188  | 	   do_regex_test("^MIME-Version:", txt)) {
    189  | 	found = 1;
    190  | 	fclose(fin);
    191  |
    192  | 	/* if ((retcode = MM_store((char*)p->file,mail_file, EP_Debug)) != 0) {
    193  | 	   fprintf(stderr, "Error on MM_Store: %d\n", retcode );
    194  | 	   } */
    195  |
    196  | 	MM_store((char*)p->file,mail_file, EP_Debug, 0);
    197  |
    198  |  	/* Invoke the MIME parser */
    199  | 	retcode = MM_extract_mime(mail_file, NULL, p, EP_Debug);
    200  |       } else
    201  | 	if ( do_regex_test("^ *\n", txt) )
    202  | 	  headers_end = 1;
    203  |     }
    204  |
    205  |     if (!found) {
    206  |       fclose(fin);
    207  |       PA_SetOutputPrefix(EP_outputPrefix);
    208  |
    209  |       PA_ParseMessage(p);
    210  |
    211  |     }
    212  |
    213  |   } else {
    214  |     p->isValidPGPSignature = vS_NO_IN_FILES;
    215  |   }
    216  |
    217  |   return p;
    218  | }
    219  |
    220  | /* ------------------------------------------------- */
    221  |
    222  | EPNodePtr EP_InitializeRootNode( const char *inputFile ) {
    223  |   EPNodePtr rootNode;
    224  |
    225  |   EP_TreeHeight = EP_Node_ID = 0;
    226  |
    227  |   rootNode = UT_malloc(sizeof(struct EPNode));
    228  |
    229  |   rootNode->nodeID = 0;
    230  |   rootNode->isValidPGPSignature = vS_IS_NOT_PGP;
    231  |   rootNode->keyID = 0;
    232  |   rootNode->MIMEContentType = -1;
    233  |   rootNode->strMIMEContentType = NULL;
    234  |   rootNode->file = strdup(inputFile);
    235  |   rootNode->inner = NULL;
    236  |   rootNode->next = NULL;
    237  |
    238  |   return rootNode;
    239  | }
    240  |
    241  | /* ------------------------------------------------- */
    242  |
    243  | EPNodePtr EP_InitializeNode( const char *inputFile, const int nodeID ) {
    244  |   EPNodePtr node;
    245  |
    246  |   node = UT_malloc(sizeof(struct EPNode));
    247  |
    248  |   node->nodeID = nodeID;
    249  |   node->isValidPGPSignature = vS_IS_NOT_PGP;
    250  |   node->keyID = 0;
    251  |   node->MIMEContentType = -1;
    252  |   node->strMIMEContentType = NULL;
    253  |   node->file = strdup(inputFile);
    254  |   node->inner = NULL;
    255  |   node->next = NULL;
    256  |
    257  |   return node;
    258  | }
    259  |
    260  | /* ------------------------------------------------- */
    261  |
    262  | EPNodePtr EP_DefineNewNode( const int nodeID,
    263  | 			    const short isValidPGPSignature,
    264  | 			    const t_MM_type MIMEContentType,
    265  | 			    const char *strMIMEContentType,
    266  | 			    const u32 keyID) {
    267  |   EPNodePtr node;
    268  |
    269  |   node = (EPNodePtr) UT_malloc(sizeof(EP_mail_node));
    270  |
    271  |   /*  printf("node: %d, %p\n", nodeID, node); */
    272  |
    273  |   node->nodeID = nodeID;
    274  |   node->isValidPGPSignature = isValidPGPSignature;
    275  |   node->keyID = keyID;
    276  |   node->MIMEContentType = MIMEContentType;
    277  |   node->strMIMEContentType = (strMIMEContentType == NULL ? NULL :
    278  | 			      strdup(strMIMEContentType) );
    279  |   node->inner = NULL;
    280  |   node->next = NULL;
    281  |   EP_BuildFilename(node);
    282  |
    283  |   return node;
    284  | }
    285  |
    286  | /* ------------------------------------------------- */
    287  | /* Deallocate parsing tree and remove files */
    288  |
    289  | void EP_TreeCleanUp(const EPNodePtr ptr) {
    290  |
    291  |   if (ptr->file != NULL) {
    292  |     unlink(ptr->file);
    293  |     /*    printf("node: %d, %p\n", ptr->nodeID, ptr); */
    294  |     free(ptr->file);
    295  |   }
    296  |   if (ptr->strMIMEContentType != NULL) {
    297  |     free(ptr->strMIMEContentType);
    298  |   }
    299  |
    300  |   if (ptr->inner != NULL) EP_TreeCleanUp(ptr->inner);
    301  |   if (ptr->next != NULL) EP_TreeCleanUp(ptr->next);
    302  |
    303  |   free(ptr);
    304  | }
    305  |
    306  | /* ------------------------------------------------- */
    307  | void MailHeaderFieldCleanUp(Mail_Header_FieldPtr p) {
    308  |   Mail_Header_FieldPtr ptmp = p, prev;
    309  |
    310  |   while (ptmp != NULL) {
    311  |     prev = ptmp;
    312  |     ptmp = ptmp->next;
    313  |     if (prev->field != NULL)
    314  |       free(prev->field);
    315  |     free(prev);
    316  |   }
    317  | }
    318  |
    319  |
    320  | /* ------------------------------------------------- */
    321  |
    322  | /* Deallocate parsing tree and remove files */
    323  |
    324  | void EP_MailDescrCleanUp(const EP_Mail_DescrPtr ptr) {
    325  |
    326  |   if (ptr != NULL) {
    327  |
    328  |     MailHeaderFieldCleanUp(ptr->from);
    329  |     MailHeaderFieldCleanUp(ptr->subject);
    330  |     MailHeaderFieldCleanUp(ptr->date);
    331  |     MailHeaderFieldCleanUp(ptr->message_id);
    332  |     MailHeaderFieldCleanUp(ptr->reply_to);
    333  |     MailHeaderFieldCleanUp(ptr->cc);
    334  |     MailHeaderFieldCleanUp(ptr->content_type);
    335  |
    336  |     EP_TreeCleanUp(ptr->tree);
    337  |     free(ptr);
    338  |   }
    339  | }
    340  |
    341  | /* ------------------------------------------------- */
    342  | /* Build a node filename */
    343  |
    344  | void EP_BuildFilename(const EPNodePtr ptr) {
    345  |   char file[FILENAME_LENGTH];
    346  |
    347  |   sprintf(file, "%s%d", EP_outputPrefix, ptr->nodeID);
    348  |   ptr->file = strdup(file);
    349  | }
    350  |
    351  | /* ------------------------------------------------- */
    352  |
    353  | void EP_ShowTree(const EPNodePtr p) {
    354  |   if (p != NULL) {
    355  |     /* if (EP_HasContent(p)) { */
    356  |       printf("Node ID: %d\n", p->nodeID);
    357  |       printf("isValidPGPSignature: %s\n",  vS_strRC[p->isValidPGPSignature]);
    358  |       printf("MIMEContentType: %d\n", p->MIMEContentType);
    359  |       printf("Key ID: %0X\n", p->keyID);
    360  |       printf("file: %s\n\n\n", p->file);
    361  |       /* } */
    362  |     if (p->inner != NULL)
    363  |       EP_ShowTree(p->inner);
    364  |     if (p->next != NULL)
    365  |       EP_ShowTree(p->next);
    366  |   }
    367  | }
    368  |
    369  | /* ------------------------------------------------- */
    370  |
    371  | EPTokenPtr EP_DefineNewToken( const t_MM_type MIMEContentType,
    372  | 			      const char *file,
    373  | 			      const EPTokenKeysPtr keysList ) {
    374  |   EPTokenPtr token;
    375  |   EPTokenKeysPtr head = NULL, p = keysList, pnew, prev = NULL;
    376  |
    377  |   token = (EPTokenPtr) UT_malloc(sizeof(EPToken));
    378  |   token->file = (char*)file;
    379  |   token->MIMEContentType = MIMEContentType;
    380  |
    381  |
    382  |   /* generate head, and build the key list for this result node */
    383  |   if (p != NULL) {
    384  |     pnew = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
    385  |     pnew->isValidPGPSignature = p->isValidPGPSignature;
    386  |     pnew->keyID = p->keyID;
    387  |     pnew->next = NULL;
    388  |     head = prev = pnew;
    389  |     p = p->next;
    390  |   }
    391  |
    392  |   while (p != NULL) {
    393  |     pnew = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
    394  |     pnew->isValidPGPSignature = p->isValidPGPSignature;
    395  |     pnew->keyID = p->keyID;
    396  |     pnew->next = NULL;
    397  |     prev->next = pnew;
    398  |     prev = pnew;
    399  |     p = p->next;
    400  |   }
    401  |
    402  |   token->keys = head;
    403  |   token->next = token->prev = NULL;
    404  |
    405  |   return token;
    406  | }
    407  |
    408  | /* ------------------------------------------------- */
    409  |
    410  | EPTokenKeysPtr AddKeyInfo( EPTokenKeysPtr keysList, const EPNodePtr p ){
    411  |   EPTokenKeysPtr ptk;
    412  |
    413  |   ptk = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
    414  |   ptk->isValidPGPSignature = p->isValidPGPSignature;
    415  |   ptk->keyID = p->keyID;
    416  |   ptk->next = NULL;
    417  |   if (keysList == NULL)
    418  |     return ptk;
    419  |   else {
    420  |     ptk->next = keysList;
    421  |     return ptk;
    422  |   }
    423  | }
    424  |
    425  | /* ------------------------------------------------- */
    426  |
    427  | EPTokenKeysPtr RemoveKeyInfo( const EPTokenKeysPtr keysHead ) {
    428  |   EPTokenKeysPtr tmp = keysHead->next;
    429  |
    430  |   free(keysHead);
    431  |   return tmp;
    432  | }
    433  |
    434  | /* ------------------------------------------------- */
    435  |
    436  | /*
    437  |  ep_GetPasswords: collects all passwords in the current mail chunk
    438  |  */
    439  | GSList* ep_GetPasswords(char *file) {
    440  |   FILE *input_file;
    441  |   gchar line[1024];
    442  |   GSList *password_list = NULL;
    443  |
    444  |   if ((input_file = fopen(file, "r")) == NULL) {
    445  |     ER_perror(FAC_UP, UP_CANTOPEN, "Couldn't open the file %s\n", file);
    446  |     exit(1);
    447  |   }
    448  |
    449  |   while (fgets(line, 1024, input_file) != NULL) {
    450  |     if (strncasecmp(line, "password:", 9) == 0 &&
    451  | 	strlen(line) > 9) { // 9 = strlen 'password:'
    452  |       //printf("pwd add %s\n", g_strstrip(strdup(line + 9)));
    453  |       password_list = g_slist_append(password_list,
    454  | 				    g_strstrip(strdup(line + 9)));
    455  |     }
    456  |   }
    457  |
    458  |   fclose (input_file);
    459  |   return password_list;
    460  | }
    461  |
    462  | /* ------------------------------------------------- */
    463  |
    464  | /*
    465  |
    466  |   Password transporting works as follows:
    467  |   Current node and siblings receive passwords from parent.
    468  |   Child nodes of the current one receive the ones that the
    469  |     current received plus the ones specified on the current.
    470  |  */
    471  | EPTokenPtr EP_GetTokens(const EPNodePtr p, const EPTokenPtr head,
    472  | 			EPTokenKeysPtr keysList, GSList *passwords) {
    473  |   EPTokenPtr pt, ptmp = head;
    474  |   EPTokenKeysPtr kl = keysList;
    475  |   GSList *node_passwords = NULL;
    476  |
    477  |   if (p != NULL) {
    478  |     if (p->isValidPGPSignature != vS_IS_NOT_PGP ) {
    479  |       kl = AddKeyInfo(kl, p);
    480  |     }
    481  |     if (EP_HasContent(p)) {
    482  |       pt = EP_DefineNewToken(p->MIMEContentType, p->file, kl);
    483  |       pt->passwords = passwords;
    484  |       if (ptmp != NULL) {
    485  | 	pt->next = ptmp;
    486  | 	ptmp->prev = pt;
    487  | 	ptmp = pt;
    488  |       } else
    489  | 	ptmp = pt;
    490  |     }
    491  |     else {
    492  |       if (p->MIMEContentType<3) { //UGLY
    493  |         node_passwords = ep_GetPasswords(p->file);
    494  |         node_passwords = g_slist_concat(node_passwords, passwords);
    495  |       }
    496  |
    497  |       ptmp = EP_GetTokens(p->inner, ptmp, kl, node_passwords);
    498  |     }
    499  |
    500  |     if (p->isValidPGPSignature != vS_IS_NOT_PGP ) {
    501  |       kl = RemoveKeyInfo(kl);
    502  |     }
    503  |
    504  |     ptmp = EP_GetTokens(p->next, ptmp, kl, passwords);
    505  |   }
    506  |   return ptmp;
    507  | }
    508  |
    509  | /* ------------------------------------------------- */
    510  | void EP_PrintTokens(EPTokenPtr head) {
    511  |   EPTokenPtr p = head;
    512  |   EPTokenKeysPtr ptk;
    513  |
    514  |   while (p != NULL) {
    515  |     printf("Token: %s, MIMEtype: %d\n", p->file, p->MIMEContentType);
    516  |     ptk = p->keys;
    517  |     while (ptk != NULL) {
    518  |       printf("     key: %0X, isValid: %s\n",
    519  | 	     ptk->keyID, vS_strRC[ptk->isValidPGPSignature]);
    520  |       ptk = ptk->next;
    521  |     }
    522  |     p = p->next;
    523  |   }
    524  | }
    525  |
    526  | /* ------------------------------------------------- */
    527  |
    528  |
    529  | void EP_CleanTokens(const EPTokenPtr head) {
    530  |   EPTokenPtr prevp, p = head;
    531  |   EPTokenKeysPtr ptk, prevptk;
    532  |
    533  |   while (p != NULL) {
    534  |     ptk = p->keys;
    535  |     while (ptk != NULL) {
    536  |       prevptk = ptk;
    537  |       ptk = ptk->next;
    538  |       free(prevptk);
    539  |     }
    540  |     prevp = p;
    541  |     p = p->next;
    542  |     free(prevp);
    543  |   }
    544  | }
    545  |
    546  |