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 |