1    | /***************************************
2    |   $Revision: 1.28 $
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  | 			      const char *keyRing,
110  | 			      const char *gpgcmd) {
111  |   EP_Mail_DescrPtr ptr;
112  |   char hostname[MAXHOSTNAMELEN];
113  |   int retcode;
114  |   long debug = 0;
115  |   char mail_file[FILENAMELEN];
116  | 
117  |   EP_Debug = debug;
118  | 
119  |   gethostname(hostname, MAXHOSTNAMELEN);
120  |   sprintf(EP_outputPrefix, "%s/EPMtmp.%s.%d.", outputPath,
121  | 	  hostname, getpid());
122  |   strcpy(EP_keyRing, keyRing);
123  |   strcpy(EP_gpgcmd, gpgcmd);
124  | 
125  |   sprintf (mail_file,"%sunprocessed", EP_outputPrefix);      /* the file where the mail message will be stored */
126  | 
127  |   /* if ((retcode = MM_store((char*)inputFile,mail_file, debug)) != 0)
128  |     exit (retcode); */
129  | 
130  |   MM_store((char*)inputFile,mail_file, debug);
131  |   
132  |   ptr = InitializeMailDescr(mail_file);
133  |   /* Invoke the MIME parser */
134  |   retcode = MM_extract_mime(mail_file, NULL, ptr->tree, debug);
135  | 
136  |   return ptr;
137  | }
138  | 
139  | /* ------------------------------------------------- */
140  | 
141  | EPNodePtr EP_ParseText(const char *inputFile,
142  | 		       const char *outputPath,
143  | 		       const char *keyRing,
144  | 		       const char *gpgcmd) {
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  |   strcpy(EP_keyRing, keyRing);
155  |   strcpy(EP_gpgcmd, gpgcmd);
156  | 
157  |   ptr = EP_InitializeRootNode(inputFile);
158  | 
159  |   return PA_ParseMessage(ptr);
160  | }
161  | 
162  | 
163  | /* ------------------------------------------------- */
164  | 
165  | EPNodePtr EP_MIMEParse(const EPNodePtr p)
166  | {
167  |   char mail_file[FILENAMELEN];
168  |   int retcode;
169  |   FILE * fin;
170  |   char *strptr;
171  |   int found = 0, headers_end = 0;
172  |   char txt[MAX_LINE_BUF];
173  | 
174  |   sprintf (mail_file,"%s%d.unprocessed", EP_outputPrefix, p->nodeID);      /* the file where the mail message will be stored */
175  |   
176  |   /* Quest for a mail header:
177  |      look for a mail header of type (content-type || mime version).
178  |   */
179  | 
180  |   if ((fin = fopen(p->file, "r")) != NULL) { 
181  |     while ( !headers_end && !found && 
182  | 	    (strptr = fgets(txt, MAX_LINE_BUF, fin)) != NULL) {
183  |       if ( do_regex_test("^Content-Type:", txt) || 
184  | 	   do_regex_test("^MIME-Version:", txt)) {
185  | 	found = 1;
186  | 	fclose(fin);
187  | 	
188  | 	/* if ((retcode = MM_store((char*)p->file,mail_file, EP_Debug)) != 0) {
189  | 	   fprintf(stderr, "Error on MM_Store: %d\n", retcode );
190  | 	   } */
191  | 
192  | 	MM_store((char*)p->file,mail_file, EP_Debug);
193  | 
194  |  	/* Invoke the MIME parser */
195  | 	retcode = MM_extract_mime(mail_file, NULL, p, EP_Debug);
196  |       } else
197  | 	if ( do_regex_test("^ *\n", txt) ) 
198  | 	  headers_end = 1; 
199  |     }
200  | 
201  |     if (!found) {
202  |       fclose(fin);
203  |       PA_ParseMessage(p);
204  |     }
205  | 
206  |   } else {
207  |     p->isValidPGPSignature = vS_NO_IN_FILES;
208  |   }
209  | 
210  |   return p;
211  | }
212  | 
213  | /* ------------------------------------------------- */
214  | 
215  | EPNodePtr EP_InitializeRootNode( const char *inputFile ) {
216  |   EPNodePtr rootNode;
217  | 
218  |   EP_TreeHeight = EP_Node_ID = 0;
219  | 
220  |   rootNode = UT_malloc(sizeof(struct EPNode));
221  | 
222  |   rootNode->nodeID = 0;
223  |   rootNode->isValidPGPSignature = vS_IS_NOT_PGP;
224  |   rootNode->keyID = 0;
225  |   rootNode->MIMEContentType = -1;
226  |   rootNode->strMIMEContentType = NULL;
227  |   rootNode->file = strdup(inputFile);
228  |   rootNode->inner = NULL;
229  |   rootNode->next = NULL;
230  | 
231  |   return rootNode; 
232  | }
233  | 
234  | /* ------------------------------------------------- */
235  | 
236  | EPNodePtr EP_InitializeNode( const char *inputFile, const int nodeID ) {
237  |   EPNodePtr node;
238  | 
239  |   node = UT_malloc(sizeof(struct EPNode));
240  | 
241  |   node->nodeID = nodeID;
242  |   node->isValidPGPSignature = vS_IS_NOT_PGP;
243  |   node->keyID = 0;
244  |   node->MIMEContentType = -1;
245  |   node->strMIMEContentType = NULL;
246  |   node->file = strdup(inputFile);
247  |   node->inner = NULL;
248  |   node->next = NULL;
249  | 
250  |   return node; 
251  | }
252  | 
253  | /* ------------------------------------------------- */
254  | 
255  | EPNodePtr EP_DefineNewNode( const int nodeID,
256  | 			    const short isValidPGPSignature,
257  | 			    const t_MM_type MIMEContentType,
258  | 			    const char *strMIMEContentType,
259  | 			    const u32 keyID) {
260  |   EPNodePtr node;
261  | 
262  |   node = (EPNodePtr) UT_malloc(sizeof(EP_mail_node));
263  | 
264  |   /*  printf("node: %d, %p\n", nodeID, node); */
265  | 
266  |   node->nodeID = nodeID;
267  |   node->isValidPGPSignature = isValidPGPSignature;
268  |   node->keyID = keyID;
269  |   node->MIMEContentType = MIMEContentType;
270  |   node->strMIMEContentType = (strMIMEContentType == NULL ? NULL : 
271  | 			      strdup(strMIMEContentType) );
272  |   node->inner = NULL;
273  |   node->next = NULL;
274  |   EP_BuildFilename(node);
275  | 
276  |   return node; 
277  | }
278  | 
279  | /* ------------------------------------------------- */
280  | /* Deallocate parsing tree and remove files */
281  | 
282  | void EP_TreeCleanUp(const EPNodePtr ptr) {
283  | 
284  |   if (ptr->file != NULL) {
285  |     unlink(ptr->file);
286  |     /*    printf("node: %d, %p\n", ptr->nodeID, ptr); */
287  |     free(ptr->file);
288  |   }
289  |   if (ptr->strMIMEContentType != NULL) {
290  |     free(ptr->strMIMEContentType);
291  |   }
292  | 
293  |   if (ptr->inner != NULL) EP_TreeCleanUp(ptr->inner);
294  |   if (ptr->next != NULL) EP_TreeCleanUp(ptr->next);
295  | 
296  |   free(ptr);
297  | }
298  | 
299  | /* ------------------------------------------------- */
300  | void MailHeaderFieldCleanUp(Mail_Header_FieldPtr p) {
301  |   Mail_Header_FieldPtr ptmp = p, prev; 
302  | 
303  |   while (ptmp != NULL) {
304  |     prev = ptmp;
305  |     ptmp = ptmp->next;
306  |     if (prev->field != NULL)
307  |       free(prev->field);
308  |     free(prev);
309  |   }
310  | }
311  | 
312  | 
313  | /* ------------------------------------------------- */
314  | 
315  | /* Deallocate parsing tree and remove files */
316  | 
317  | void EP_MailDescrCleanUp(const EP_Mail_DescrPtr ptr) {
318  | 
319  |   if (ptr != NULL) {
320  | 
321  |     MailHeaderFieldCleanUp(ptr->from);
322  |     MailHeaderFieldCleanUp(ptr->subject);
323  |     MailHeaderFieldCleanUp(ptr->date);
324  |     MailHeaderFieldCleanUp(ptr->message_id);
325  |     MailHeaderFieldCleanUp(ptr->reply_to);
326  |     MailHeaderFieldCleanUp(ptr->cc);
327  |     MailHeaderFieldCleanUp(ptr->content_type);
328  | 
329  |     EP_TreeCleanUp(ptr->tree);
330  |     free(ptr);
331  |   }
332  | }
333  | 
334  | /* ------------------------------------------------- */
335  | /* Build a node filename */
336  | 
337  | void EP_BuildFilename(const EPNodePtr ptr) {
338  |   char file[FILENAME_LENGTH];
339  | 
340  |   sprintf(file, "%s%d", EP_outputPrefix, ptr->nodeID);
341  |   ptr->file = strdup(file);
342  | }
343  | 
344  | /* ------------------------------------------------- */
345  | 
346  | void EP_ShowTree(const EPNodePtr p) {
347  |   if (p != NULL) {
348  |     /* if (EP_HasContent(p)) { */
349  |       printf("Node ID: %d\n", p->nodeID);
350  |       printf("isValidPGPSignature: %s\n",  vS_strRC[p->isValidPGPSignature]);  
351  |       printf("MIMEContentType: %d\n", p->MIMEContentType);
352  |       printf("Key ID: %0X\n", p->keyID);
353  |       printf("file: %s\n\n\n", p->file);
354  |       /* } */
355  |     if (p->inner != NULL)
356  |       EP_ShowTree(p->inner);
357  |     if (p->next != NULL)
358  |       EP_ShowTree(p->next);
359  |   }
360  | }
361  | 
362  | /* ------------------------------------------------- */
363  | 
364  | EPTokenPtr EP_DefineNewToken( const t_MM_type MIMEContentType,
365  | 			      const char *file,
366  | 			      const EPTokenKeysPtr keysList ) {
367  |   EPTokenPtr token;
368  |   EPTokenKeysPtr head = NULL, p = keysList, pnew, prev = NULL;
369  |   
370  |   token = (EPTokenPtr) UT_malloc(sizeof(EPToken));
371  |   token->file = (char*)file;
372  |   token->MIMEContentType = MIMEContentType;
373  | 
374  | 
375  |   /* generate head, and build the key list for this result node */
376  |   if (p != NULL) {
377  |     pnew = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
378  |     pnew->isValidPGPSignature = p->isValidPGPSignature;
379  |     pnew->keyID = p->keyID;
380  |     pnew->next = NULL;
381  |     head = prev = pnew;
382  |     p = p->next;
383  |   }
384  | 
385  |   while (p != NULL) {
386  |     pnew = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
387  |     pnew->isValidPGPSignature = p->isValidPGPSignature;
388  |     pnew->keyID = p->keyID;
389  |     pnew->next = NULL;
390  |     prev->next = pnew;
391  |     prev = pnew;
392  |     p = p->next;
393  |   }
394  | 
395  |   token->keys = head;
396  |   token->next = token->prev = NULL;
397  | 
398  |   return token;   
399  | }
400  | 
401  | /* ------------------------------------------------- */
402  | 
403  | EPTokenKeysPtr AddKeyInfo( EPTokenKeysPtr keysList, const EPNodePtr p ){
404  |   EPTokenKeysPtr ptk;
405  | 
406  |   ptk = (EPTokenKeysPtr) UT_malloc(sizeof(EPTokenKeys));
407  |   ptk->isValidPGPSignature = p->isValidPGPSignature;
408  |   ptk->keyID = p->keyID;
409  |   ptk->next = NULL;
410  |   if (keysList == NULL)
411  |     return ptk;
412  |   else {
413  |     ptk->next = keysList;
414  |     return ptk;
415  |   }
416  | }
417  | 
418  | /* ------------------------------------------------- */
419  | 
420  | EPTokenKeysPtr RemoveKeyInfo( const EPTokenKeysPtr keysHead ) {
421  |   EPTokenKeysPtr tmp = keysHead->next;
422  | 
423  |   free(keysHead);
424  |   return tmp;
425  | }
426  | /* ------------------------------------------------- */
427  | 
428  | EPTokenPtr EP_GetTokens(const EPNodePtr p, const EPTokenPtr head, 
429  | 			EPTokenKeysPtr keysList) {
430  |   EPTokenPtr pt, ptmp = head;
431  |   EPTokenKeysPtr kl = keysList;
432  |   
433  |   if (p != NULL) {
434  |     if (p->isValidPGPSignature != vS_IS_NOT_PGP ) {
435  |       kl = AddKeyInfo(kl, p);
436  |     }
437  |     if (EP_HasContent(p)) {
438  |       pt = EP_DefineNewToken(p->MIMEContentType, p->file, kl);
439  |       if (ptmp != NULL) {
440  | 	pt->next = ptmp;
441  | 	ptmp->prev = pt;
442  | 	ptmp = pt;
443  |       } else 
444  | 	ptmp = pt;
445  |     } else
446  |       ptmp = EP_GetTokens(p->inner, ptmp, kl);
447  | 
448  |     if (p->isValidPGPSignature != vS_IS_NOT_PGP ) {
449  |       kl = RemoveKeyInfo(kl);
450  |     }
451  | 
452  |     ptmp = EP_GetTokens(p->next, ptmp, kl);
453  |   }
454  |   return ptmp;
455  | }
456  | 
457  | /* ------------------------------------------------- */
458  | void EP_PrintTokens(EPTokenPtr head) {
459  |   EPTokenPtr p = head;
460  |   EPTokenKeysPtr ptk;
461  | 
462  |   while (p != NULL) {
463  |     printf("Token: %s, MIMEtype: %d\n", p->file, p->MIMEContentType);
464  |     ptk = p->keys;
465  |     while (ptk != NULL) {
466  |       printf("     key: %0X, isValid: %s\n", 
467  | 	     ptk->keyID, vS_strRC[ptk->isValidPGPSignature]);
468  |       ptk = ptk->next;
469  |     }
470  |     p = p->next;
471  |   }
472  | }
473  | 
474  | /* ------------------------------------------------- */
475  | 
476  | void EP_CleanTokens(const EPTokenPtr head) {
477  |   EPTokenPtr prevp, p = head;
478  |   EPTokenKeysPtr ptk, prevptk;
479  | 
480  |   while (p != NULL) {
481  |     ptk = p->keys;
482  |     while (ptk != NULL) {
483  |       prevptk = ptk;
484  |       ptk = ptk->next;
485  |       free(prevptk);
486  |     }
487  |     prevp = p;
488  |     p = p->next;
489  |     free(prevp);
490  |   }
491  | }
492  | 
493  |