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  |