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 |