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 |