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 |