1 | /*************************************** 2 | $Revision: 2.29 $ 3 | 4 | mm - MIME Parser module. Functions to parse a mail message part, 5 | find if it is MIME-encapsulated, dispatch the part to the 6 | appropriate drivers (also included) and return tree nodes 7 | with all MIME information. 8 | 9 | Status: COMPLETE, NOT REVUED, TESTED 10 | 11 | Design and implementation by: daniele@ripe.net 12 | 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 | /* Pieces of this code stolen and/or adapted from mtest.c, 35 | * part of the IMAP toolkit by Mark Crispin: 36 | */ 37 | 38 | /* Original version Copyright 1988 by The Leland Stanford Junior University 39 | * Copyright 1999 by the University of Washington 40 | * 41 | * Permission to use, copy, modify, and distribute this software and its 42 | * documentation for any purpose and without fee is hereby granted, provided 43 | * that the above copyright notices appear in all copies and that both the 44 | * above copyright notices and this permission notice appear in supporting 45 | * documentation, and that the name of the University of Washington or The 46 | * Leland Stanford Junior University not be used in advertising or publicity 47 | * pertaining to distribution of the software without specific, written prior 48 | * permission. This software is made available "as is", and 49 | * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY 50 | * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE, 51 | * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 52 | * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF 53 | * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY 54 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 55 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF 56 | * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF 57 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 58 | * 59 | */ 60 | 61 | 62 | 63 | /************************** 64 | 65 | 66 | "Every program attempts to expand until it can read mail. 67 | Those programs which cannot so expand are replaced by 68 | ones which can." 69 | (Jamie Zawinski) 70 | 71 | 72 | **************************/ 73 | 74 | 75 | #include "rip.h" 76 | 77 | /* Standard headers */ 78 | #include <stdio.h> 79 | #include <signal.h> 80 | #include <string.h> 81 | #include <sys/time.h> 82 | #include <sys/types.h> 83 | /* #include <sys/param.h> */ 84 | #include <netdb.h> 85 | #include <regex.h> 86 | #include <unistd.h> 87 | 88 | /* c-client headers */ 89 | #include "misc.h" 90 | 91 | 92 | /* 93 | * Globals to store shared data for tree nodes 94 | * These variables come from EP 95 | */ 96 | 97 | extern char EP_outputPrefix[FILENAME_LENGTH]; 98 | extern char EP_keyRing[FILENAME_LENGTH]; 99 | extern int EP_TreeHeight; 100 | extern int EP_Node_ID; 101 | 102 | /* Global variables to be used in this module */ 103 | long debug = DEFAULT_DEBUG; 104 | 105 | char *supported_MIME_types[MAXSUPPTYPES] = { 106 | "UNKNOWN/UNKNOWN", "TEXT/PLAIN", "APPLICATION/PGP", "MULTIPART/SIGNED", 107 | "MULTIPART/MIXED", "MULTIPART/ALTERNATIVE", "MULTIPART/DIGEST", 108 | "MESSAGE/RFC822" 109 | }; 110 | 111 | long pass = 0; 112 | 113 | 114 | /* 115 | FIXMEs: 116 | - Revise the whole debug system, debug messages etc. - right now 117 | an enormous and globally useless quantity of information is dumped. 118 | */ 119 | 120 | 121 | /*+++++++++++++++++++++++++++++++++++++++ 122 | 123 | API functions 124 | 125 | +++++++++++++++++++++++++++++++++++++++*/ 126 | 127 | 128 | 129 | /*++++++++++ 130 | * 131 | * MM_store(). Stores a file (or stdin) in another file, 132 | * "escaping" the lines starting with "From " by adding 133 | * a ">" sign. This is necessary because we need to deal 134 | * with files that are "unix mailboxes". 135 | * 136 | * This function puts a limit to the line size that a mail 137 | * message may have; officially, there is no limit to this size, 138 | * but we prefer to add this limit to avoid buffer overflow. 139 | * The line size limit is MAXBUFSIZE, defined in mm.h . 140 | * 141 | ++++++++++*/ 142 | 143 | 144 | int MM_store (char *source_file, char *destination_file, long custom_debug) 145 | { 146 | 147 | 148 | /* The regexp we will be looking for */ 149 | #define REGEXP "^From " 150 | 151 | 152 | char line[MAXBUFSIZE]; 153 | FILE *ifp; 154 | FILE *ofp; 155 | FILE *actualfile; /* Actual "file" to be opened (can be stdin) */ 156 | char *tmpstr; 157 | short using_file = 0; 158 | long numlines = 0; 159 | time_t ti = time (0); 160 | 161 | 162 | 163 | if (custom_debug) 164 | debug = custom_debug; 165 | 166 | /* Check if we need to parse a file or stdin. 167 | * We parse stdin if source_file is "-" . 168 | */ 169 | 170 | if (strcmp(source_file,"-")) 171 | { 172 | if ((ifp = fopen(source_file,"r")) != NULL) 173 | { 174 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "MM_store: input file %s",source_file); 175 | actualfile = ifp; 176 | using_file = 1; 177 | } 178 | else 179 | { 180 | /* XXX Use perror to state reason? */ 181 | ER_perror(FAC_MM, MM_CANTOPEN, "%s for reading", source_file); 182 | die; 183 | } 184 | } 185 | else 186 | { 187 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "MM_store: input from stdin"); 188 | actualfile = stdin; 189 | } 190 | 191 | if ((ofp = fopen(destination_file,"w")) != NULL) 192 | { 193 | while ((tmpstr = fgets(line, MAXBUFSIZE, actualfile)) != NULL) 194 | { 195 | numlines++; 196 | if (strlen(line) >= MAXBUFSIZE - 1) 197 | { 198 | ER_inf_va(FAC_MM, ASP_MM_SEC, "Line too long error. Possible buffer overflow attempt."); 199 | ER_perror(FAC_MM, MM_LINETOOLONG, "%ld",numlines); 200 | /* XXX Should be handled better - report line too long to caller, 201 | so that a failed ack can be sent */ 202 | die; 203 | } 204 | if (numlines == 1) 205 | { 206 | /* If the first line is not a "^From " line, put a fake one */ 207 | if (!do_regex_test(REGEXP,(char *)line)) 208 | fprintf (ofp,"From dbase@whois.ripe.net %s",ctime (&ti)); 209 | fputs (line,ofp); 210 | } 211 | else 212 | { 213 | if (do_regex_test(REGEXP,(char *)line)) fprintf (ofp,">"); 214 | fputs (line,ofp); 215 | } 216 | } 217 | fclose(ofp); 218 | if (using_file) fclose(ifp); 219 | return(0); 220 | } 221 | else 222 | { 223 | /* XXX Use perror to state reason? */ 224 | ER_perror(FAC_MM, MM_CANTOPEN, "%s for writing", destination_file); 225 | die; 226 | } 227 | 228 | /* Even though we should never get here... */ 229 | return(0); 230 | 231 | } /* MM_store() */ 232 | 233 | 234 | 235 | /********** 236 | * 237 | * MM_get_msg_headers(). Get the headers of a mail contained in a file. 238 | * 239 | **********/ 240 | 241 | int MM_get_msg_headers( 242 | const char *mail_file, /* Input mail file */ 243 | EP_Mail_Descr *mail_descr, /* Structure containing the headers */ 244 | long mesgno, /* msg number in the input file */ 245 | long custom_debug /* debug level */ 246 | ) 247 | { 248 | MAILSTREAM *stream = NULL; /* MAILSTREAM is defined in c-client */ 249 | char tmp[MAILTMPLEN]; /* MAILTMPLEN is set in c-client */ 250 | int retcode; /* return code of the subroutine */ 251 | STRINGLIST *lines; /* STRINGLIST is defined in c-client */ 252 | STRINGLIST *cur; 253 | BODY *body; 254 | 255 | #include "linkage.c" /* c-client requires it to be included... */ 256 | 257 | 258 | /* If the supplied debug level is not null, then the global debug level 259 | * takes that value 260 | */ 261 | if (custom_debug) 262 | debug = custom_debug; 263 | 264 | 265 | /* open mailbox and get the mail stream */ 266 | sprintf (tmp, "%s", mail_file); 267 | stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL); 268 | 269 | /* Process the stream */ 270 | if (!stream) 271 | { 272 | ER_perror(FAC_MM, MM_INVMBX, "%s", mail_file); 273 | die; 274 | } 275 | else 276 | { 277 | ER_inf_va (FAC_MM, ASP_MM_GEN, "Getting message headers."); 278 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Message status:"); 279 | status (stream); /* report message status */ 280 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "End of message status."); 281 | 282 | /* Get the headers */ 283 | 284 | lines = mail_newstringlist (); 285 | cur = lines; 286 | 287 | /* Get information about the mentioned lines in the header */ 288 | 289 | 290 | mail_descr->from = get_mail_hdr_field(stream, mesgno, cur, "From"); 291 | 292 | mail_descr->subject = get_mail_hdr_field(stream, mesgno, cur, "Subject"); 293 | 294 | mail_descr->date = get_mail_hdr_field(stream, mesgno, cur, "Date"); 295 | 296 | mail_descr->message_id = get_mail_hdr_field(stream, mesgno, cur, "Message-Id"); 297 | 298 | mail_descr->reply_to = get_mail_hdr_field(stream, mesgno, cur, "Reply-To"); 299 | 300 | mail_descr->cc = get_mail_hdr_field(stream, mesgno, cur, "Cc"); 301 | 302 | 303 | 304 | mail_descr->content_type = (Mail_Header_Field *)UT_malloc(sizeof(Mail_Header_Field)); 305 | /* This gets all the line (with encoding etc.) */ 306 | /* mail_descr->content_type = get_mail_hdr_field(stream,mesgno,cur,"Content-Type"); */ 307 | 308 | /* This only gets the content-type itself in canonized form: */ 309 | mail_fetchstructure(stream,mesgno,&body); 310 | if (body) 311 | { 312 | mail_descr->content_type->field = (char *)UT_malloc(STR_M); 313 | mail_descr->content_type->next = NULL; 314 | sprintf(mail_descr->content_type->field,"%s",body_types[body->type]); 315 | if (body->subtype) 316 | sprintf(mail_descr->content_type->field+strlen(mail_descr->content_type->field),"/%s",body->subtype); 317 | sprintf(mail_descr->content_type->field+strlen(mail_descr->content_type->field),"\n\n"); 318 | } 319 | 320 | mail_free_stringlist (&lines); 321 | 322 | ER_inf_va (FAC_MM, ASP_MM_GEN, "Got message headers."); 323 | 324 | 325 | 326 | mail_close(stream); 327 | 328 | retcode = 0; 329 | 330 | 331 | } 332 | 333 | 334 | return(retcode); 335 | 336 | } /* MM_get_msg_headers() */ 337 | 338 | 339 | 340 | /* 341 | * MM_extract_mime(): extract MIME information 342 | * This function was inspired by display_body() in mtest.c, 343 | * in the IMAP distribution. It has been largely re-engineered 344 | * to support MIME, and be modular. 345 | * It now only acts as an initializer of the mail stream, 346 | * sending then the stream to be dispatched to the appropriate 347 | * MIME drivers. 348 | */ 349 | 350 | 351 | 352 | 353 | int MM_extract_mime ( 354 | const char *sourcefile, /* Input file containing the mail */ 355 | char *pfx, /* "prefix": this can be NULL at the 356 | * first call of the function */ 357 | EP_mail_node *mailnode, /* initialized node where to stock info */ 358 | long custom_debug /* debug level */ 359 | ) 360 | { 361 | 362 | MAILSTREAM *stream = NULL; /* MAILSTREAM is defined in c-client */ 363 | BODY *body; /* BODY is defined in c-client */ 364 | char tmp[MAILTMPLEN]; /* MAILTMPLEN is set in c-client */ 365 | int retcode = 0; /* return code of the subroutine */ 366 | long mesgno = 1; 367 | 368 | 369 | #include "linkage.c" /* c-client requires it to be included... */ 370 | 371 | /* 372 | * This (global) variable counts the number of times we pass through 373 | * MM_extract_mime(). 374 | * It is useful in generating unique temporary files (see below). 375 | */ 376 | 377 | pass++; 378 | ER_inf_va (FAC_MM, ASP_MM_GEN, "MM_extract_mime, pass %ld",pass); 379 | 380 | if (custom_debug) 381 | debug = custom_debug; 382 | 383 | /* ER_dbg_va (FAC_MM, ASP_MM_GEN, " EP_outputPrefix: %s",EP_outputPrefix); 384 | ER_dbg_va (FAC_MM, ASP_MM_GEN, " EP_keyRing: %s",EP_keyRing); */ 385 | 386 | 387 | /* open file and get the mail stream from there*/ 388 | 389 | sprintf (tmp, "%s", sourcefile); 390 | 391 | stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL); 392 | 393 | /* Process the stream */ 394 | if (!stream) 395 | { 396 | ER_perror(FAC_MM, MM_INVMBX, "%s", sourcefile); 397 | die; 398 | } 399 | else 400 | { 401 | if (debug >=2) 402 | { 403 | ER_inf_va (FAC_MM, ASP_MM_GEN, "Getting message headers."); 404 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Message status:"); 405 | status (stream); /* report message status */ 406 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "End of message status."); 407 | } 408 | if (debug >= 2) 409 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Calling mail_fetchstructure..."); 410 | mail_fetchstructure (stream,mesgno,&body); 411 | 412 | if (body) 413 | { 414 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Got body, dispatching to drivers..."); 415 | dispatch_to_driver(stream, body, pfx, mailnode); 416 | } 417 | 418 | } 419 | 420 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Closing the stream %s...",stream->mailbox); 421 | mail_close(stream); 422 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Stream Closed."); 423 | 424 | 425 | return(retcode); 426 | 427 | } /* MM_extract_mime() */ 428 | 429 | 430 | 431 | /*********************************************/ 432 | 433 | /*************************************** 434 | * 435 | * End of API functions 436 | * 437 | ***************************************/ 438 | 439 | 440 | 441 | /* Internal functions */ 442 | 443 | t_MM_type is_supported_MIMEtype (BODY *body) 444 | { 445 | 446 | char *mimetype_string; 447 | char tmpstr[STR_S]; 448 | char *tmptype = tmpstr; 449 | int i; 450 | t_MM_type mtypecode = 0; 451 | 452 | 453 | /* mimetype_string is the MIME type of the message */ 454 | mimetype_string = (char *)UT_malloc(STR_S); 455 | sprintf (mimetype_string,"%s",body_types[body->type]); 456 | if (body->subtype) 457 | sprintf (mimetype_string + strlen(mimetype_string),"/%s",body->subtype); 458 | 459 | /* 460 | * We cycle to compare the MIME type of the message 461 | * to each of the MIME types we support 462 | */ 463 | i = 0; 464 | tmptype = supported_MIME_types[i]; 465 | 466 | while ((i < MAXSUPPTYPES) && (tmptype)) 467 | { 468 | if (!strcmp(tmptype,mimetype_string)) 469 | { 470 | mtypecode = i; 471 | break; 472 | } 473 | tmptype = supported_MIME_types[++i]; 474 | } 475 | 476 | UT_free(mimetype_string); 477 | 478 | return(mtypecode); 479 | 480 | } /* is_supported_MIMEtype() */ 481 | 482 | 483 | 484 | /**** 485 | * 486 | * dispatch_to_driver() 487 | * This function dispatches a message to the proper driver 488 | * which will parse it, following the MIME type 489 | * 490 | ****/ 491 | 492 | void dispatch_to_driver(MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode) 493 | { 494 | 495 | t_MM_type is_supported; 496 | 497 | is_supported = is_supported_MIMEtype(body); 498 | /* We assign the given MIME Type to the node */ 499 | mailnode->MIMEContentType = is_supported; 500 | 501 | 502 | ER_dbg_va (FAC_MM, ASP_MM_GEN, " mailnode->MIMEContentType: %s",supported_MIME_types[mailnode->MIMEContentType]); 503 | 504 | if (!strcmp(supported_MIME_types[is_supported],"TEXT/PLAIN")) 505 | { 506 | parse_text_plain(stream, body, pfx, mailnode); 507 | } 508 | else if (!strcmp(supported_MIME_types[is_supported],"APPLICATION/PGP")) 509 | { 510 | parse_application_pgp(stream, body, pfx, mailnode); 511 | } 512 | else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/ALTERNATIVE")) 513 | { 514 | parse_multipart_alternative(stream, body, pfx, mailnode); 515 | } 516 | else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/MIXED")) 517 | { 518 | parse_multipart_mixed(stream, body, pfx, mailnode); 519 | } 520 | else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/SIGNED")) 521 | { 522 | parse_multipart_signed(stream, body, pfx, mailnode); 523 | } 524 | else if (!strcmp(supported_MIME_types[is_supported],"MULTIPART/DIGEST")) 525 | { 526 | parse_multipart_digest(stream, body, pfx, mailnode); 527 | } 528 | else if (!strcmp(supported_MIME_types[is_supported],"MESSAGE/RFC822")) 529 | { 530 | parse_message_rfc822(stream, body, pfx, mailnode); 531 | } 532 | else 533 | { 534 | /* It's not a supported MIMEtype... */ 535 | parse_unknown_unknown(stream, body, pfx, mailnode); 536 | } 537 | 538 | } /* dispatch_to_driver() */ 539 | 540 | 541 | void parse_text_plain(MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode) 542 | { 543 | 544 | char tmp[MAILTMPLEN]; 545 | char *mailtext; 546 | 547 | 548 | if (debug >= 2) 549 | ER_dbg_va (FAC_MM, ASP_MM_GEN, " Lines: %lu",body->size.lines); 550 | 551 | if (pfx == NULL) /* If top level, is not inside a multipart */ 552 | { 553 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MAILNODE->FILE: %s",mailnode->file); 554 | /* The filename of the root node has to be redefined to the processed file */ 555 | /* remove(mailnode->file); */ /* This causes complaints by mail_close() */ 556 | UT_free(mailnode->file); 557 | mailnode->file = (char *)UT_malloc(FILENAME_LENGTH); 558 | sprintf(mailnode->file,"%s%d",EP_outputPrefix,mailnode->nodeID); 559 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MAILNODE->FILE: %s",mailnode->file); 560 | } 561 | else 562 | 563 | ER_dbg_va (FAC_MM, ASP_MM_GEN, " mailnode->file: %s",mailnode->file); 564 | 565 | /* Get the plain text contents of the message */ 566 | mailtext = tmp; 567 | mailtext = mail_fetchtext(stream, 1); 568 | 569 | if (debug >= 2) 570 | { 571 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Message contents:"); 572 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "\n\n%s\n",mailtext); 573 | } 574 | 575 | 576 | /* Place the results in the file pointed by the node*/ 577 | write_file(mailnode->file,mailtext,strlen(mailtext)); 578 | 579 | PA_ParseMessage(mailnode); 580 | 581 | /* if (debug) printf ("mailnode->nodeID: %d\n",mailnode->nodeID); */ 582 | /* if (debug) printf ("mailnode->MIMEContentType: %d\n",mailnode->MIMEContentType); */ 583 | 584 | } 585 | 586 | void parse_message_rfc822(MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode) 587 | { 588 | 589 | /* The idea here is to strip the message/rfc822 part from its mail headers, 590 | * and store it in a file to resend to MM_extract_mime(). 591 | */ 592 | 593 | char tmp[MAILTMPLEN]; 594 | char *mailtext; 595 | char *content; 596 | char tmpfile[FILENAMELEN]; 597 | time_t ti = time (0); 598 | 599 | 600 | if (pfx == NULL) /* If top level, is not inside a multipart */ 601 | { 602 | /* pfx = (char *)UT_malloc(STR_L); 603 | pfx = ""; */ /* Dummy prefix */ 604 | /* The filename of the root node has to be redefined to the processed file */ 605 | mailnode->file = (char *)UT_malloc(FILENAME_LENGTH); 606 | sprintf(mailnode->file,"%s%d",EP_outputPrefix,mailnode->nodeID); 607 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MAILNODE->FILE: %s",mailnode->file); 608 | } 609 | 610 | /* Get the plain text contents of the message */ 611 | mailtext = tmp; 612 | mailtext = mail_fetchtext(stream, 1); 613 | 614 | 615 | /* This buffer has to be dumped in a file from where it will be read by MM_extract_mime(): 616 | * another stream will be opened, so the format of the file must be correct. 617 | * The LINELENGTH is to take the first 2 lines into account. 618 | */ 619 | 620 | content = (char *)UT_malloc(2*LINELENGTH + strlen(mailtext) + 2); 621 | sprintf (content,"From dbase@whois.ripe.net %s",ctime (&ti)); 622 | sprintf (content+strlen(content), "%s\n", mailtext); 623 | 624 | 625 | /* Generation of a temporary file: 626 | * The file must be unique inside the process. If we rewrite 627 | * on the same tmp file, which is used as a mailbox by c-client, 628 | * the c-client library has problems because it sees the mailbox changes 629 | * (I had problems with multipart/digest and message/rfc822). 630 | * "pass" is a global variable which increases every time we pass 631 | * through MM_extract_mime(): it should hence be unique each time 632 | * we call a driver. 633 | */ 634 | sprintf (tmpfile,"%s.tmp.%ld",mailnode->file,pass); 635 | write_file(tmpfile,content,strlen(content)); 636 | 637 | MM_extract_mime(tmpfile, pfx, mailnode, debug); 638 | 639 | /* Clean up... */ 640 | UT_free(content); 641 | remove(tmpfile); 642 | 643 | } 644 | 645 | void parse_multipart_alternative (MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode) 646 | { 647 | 648 | char tmppfx[MAILTMPLEN]; 649 | char childpfx[MAILTMPLEN]; 650 | char tmppart[MAILTMPLEN]; 651 | /* char *s = tmppfx; */ 652 | EP_mail_node *newnode; 653 | EP_mail_node *parsednode; 654 | EP_mail_node *nextnode; 655 | long i; 656 | PART *part; 657 | char *result; 658 | char *content; 659 | unsigned long length; 660 | time_t ti = time (0); 661 | char tmpfile[FILENAMELEN]; 662 | char nodefile[FILENAMELEN]; 663 | 664 | 665 | if (debug >= 2) 666 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Bytes: %lu",body->size.bytes); 667 | 668 | /* if not first time, extend prefix */ 669 | if (pfx == NULL) 670 | { 671 | tmppfx[0] = '\0'; 672 | pfx = tmppfx; 673 | } 674 | 675 | 676 | /* Initialize the first node: it is an inner node */ 677 | 678 | /* The tree height increases */ 679 | EP_TreeHeight++; 680 | 681 | /* The number of nodes increases */ 682 | EP_Node_ID++; 683 | 684 | sprintf (nodefile,"%s%d",EP_outputPrefix,EP_Node_ID); 685 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "inner-nodefile: %s",nodefile); 686 | 687 | newnode = EP_InitializeNode(nodefile, EP_Node_ID); 688 | mailnode->inner = newnode; 689 | 690 | for (i = 1,part = body->nested.part; part; part = part->next) 691 | { 692 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "i: %ld, pfx: %s",i,pfx); 693 | if (debug >= 3) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MYDEBUG: pfx=%s, tmppfx=%s,",pfx,tmppfx); 694 | sprintf (tmppart,"%ld",i); 695 | result = mail_fetch_mime(stream, (long)1, tmppart, &length, (long)0); 696 | if (debug >= 3) 697 | { 698 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "body->size.bytes: %lu",body->size.bytes); 699 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "(&part->body)->size.bytes: %lu",(&part->body)->size.bytes); 700 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "length: %lu",length); 701 | } 702 | 703 | 704 | /* This buffer has to be dumped in a file from where it will be read by MM_extract_mime(): 705 | * another stream will be opened, so the format of the file must be correct. 706 | * The LINELENGTH is to take the first 2 lines into account 707 | */ 708 | content = (char *)UT_malloc(2*LINELENGTH + length + (&part->body)->size.bytes + 2); 709 | sprintf (content,"From dbase@whois.ripe.net %sMIME-Version: 1.0\n",ctime (&ti)); 710 | /* snprintf (content+strlen(content), (size_t)(length + (&part->body)->size.bytes) + 2, "%s\n", result); */ 711 | g_snprintf ((gchar *)(content+strlen(content)), (gulong)(length + (&part->body)->size.bytes) + 2, "%s\n", result); 712 | 713 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "Result: \n---\n%s\n---\nEnd results.\n", content); 714 | 715 | /* Generation of a temporary file: 716 | * The file must be unique inside the process. If we rewrite 717 | * on the same tmp file, which is used as a mailbox by c-client, 718 | * the c-client library has problems because it sees it changes 719 | * (I had problems with multipart/digest and message/rfc822). 720 | * "pass" is a global variable which increases every time we pass 721 | * through MM_extract_mime(): it should hence be unique each time 722 | * we call a driver. 723 | */ 724 | sprintf (tmpfile,"%s.tmp.%ld",newnode->file,pass); 725 | write_file(tmpfile,content,strlen(content)); 726 | 727 | /* This is needed to extend the prefix */ 728 | sprintf (childpfx,"%s%ld.",pfx,i); 729 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "childpfx: %s",childpfx); 730 | MM_extract_mime(tmpfile, childpfx, newnode, debug); 731 | 732 | /* Clean up... */ 733 | UT_free(content); 734 | remove(tmpfile); 735 | 736 | /* Initialize the next node (if it exists) */ 737 | 738 | if (part->next != NULL) 739 | { 740 | EP_Node_ID++; 741 | sprintf (nodefile,"%s%d",EP_outputPrefix,EP_Node_ID); 742 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "next-nodefile: %s",nodefile); 743 | nextnode = EP_InitializeNode(nodefile, EP_Node_ID); 744 | parsednode = newnode; 745 | newnode = nextnode; 746 | parsednode->next = newnode; 747 | } 748 | 749 | i++; 750 | 751 | } 752 | 753 | } /* parse_multipart_alternative() */ 754 | 755 | 756 | void parse_multipart_signed (MAILSTREAM *stream, BODY *body, char *pfx, EP_mail_node *mailnode) 757 | { 758 | 759 | char tmppfx[MAILTMPLEN]; 760 | char tmppart[MAILTMPLEN]; 761 | EP_mail_node *newnode; 762 | PART *part; 763 | char *result; 764 | char *content; 765 | unsigned long length; 766 | char tmpfile[FILENAMELEN]; 767 | char nodefile[FILENAMELEN]; 768 | struct VerifySignObject vSO; 769 | /* int retcode; */ 770 | 771 | if (debug >= 2) 772 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Bytes: %lu",body->size.bytes); 773 | 774 | 775 | /* if not first time, extend prefix */ 776 | if (pfx == NULL) 777 | { 778 | tmppfx[0] = '\0'; 779 | pfx = tmppfx; 780 | } 781 | 782 | 783 | /* Initialize the inner node */ 784 | 785 | /* The tree height increases */ 786 | EP_TreeHeight++; 787 | 788 | /* The number of nodes increases */ 789 | EP_Node_ID++; 790 | 791 | sprintf (nodefile,"%s%d",EP_outputPrefix,EP_Node_ID); 792 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "inner-nodefile: %s",nodefile); 793 | 794 | newnode = EP_InitializeNode(nodefile, EP_Node_ID); 795 | mailnode->inner = newnode; 796 | 797 | /* We give the same content-type to the child so as not to leave the default 798 | value (-1) */ 799 | newnode->MIMEContentType = mailnode->MIMEContentType; 800 | 801 | /* We must get the two parts of the message. The signed part 802 | * and the signature. There can't be more than two parts 803 | * (see RFC2015). 804 | */ 805 | 806 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "pfx: %s",pfx); 807 | 808 | /* Signed part: it is the first part of the message. */ 809 | 810 | part = body->nested.part; 811 | 812 | sprintf (tmppart,"%s1",tmppfx); 813 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "tmppart: %s",tmppart); 814 | 815 | result = mail_fetch_mime(stream, (long)1, tmppart, &length, (long)0); 816 | if (debug >= 3) 817 | { 818 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "body->size.bytes: %lu",body->size.bytes); 819 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "(&part->body)->size.bytes: %lu",(&part->body)->size.bytes); 820 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "length: %lu",length); 821 | } 822 | 823 | /* The signed part must be dumped in a file together with the MIME headers */ 824 | 825 | content = (char *)UT_malloc(length + (&part->body)->size.bytes + 2); 826 | snprintf (content,(size_t)(length + (&part->body)->size.bytes) + 2, "%s\n", result); 827 | 828 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "Result: \n---\n%s\n---\nEnd results.\n", content); 829 | 830 | if (debug) ER_dbg_va (FAC_MM, ASP_MM_GEN, "MSG file: %s",newnode->file); 831 | write_file(newnode->file,content,strlen(content)); 832 | 833 | 834 | UT_free(content); 835 | 836 | /* Signature */ 837 | 838 | part = part->next; 839 | sprintf (tmppart,"%s2",tmppfx); 840 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "tmppart: %s",tmppart); 841 | 842 | result = mail_fetch_mime(stream, (long)1, tmppart, &length, (long)0); 843 | if (debug >= 2) 844 | { 845 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "body->size.bytes: %lu\n",body->size.bytes); 846 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "(&part->body)->size.bytes: %lu\n",(&part->body)->size.bytes); 847 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "length: %lu\n",length); 848 | } 849 | 850 | /* The signature must be dumped _without_ MIME headers instead! 851 | * Check where is the "length" variable... 852 | */ 853 | 854 | content = (char *)UT_malloc((&part->body)->size.bytes + 2); 855 | 856 | snprintf (content,(size_t)((&part->body)->size.bytes) + 2, "%s\n", result + length); 857 | 858 | if (debug >= 2) ER_dbg_va (FAC_MM, ASP_MM_GEN, "Result: \n---\n%s\n---\nEnd results.\n", content); 859 | 860 | sprintf (tmpfile,"%s.sig",newnode->file); 861 | if (debug) ER_dbg_va (FAC_MM, ASP_MM_GEN, "SIG file: %s",tmpfile); 862 | write_file(tmpfile,content,strlen(content)); 863 | 864 | /* Calling the verification procedure */ 865 | 866 | strcpy(vSO.iDocSigFilename, newnode->file); 867 | strcpy(vSO.iSigFilename, tmpfile); 868 | strcpy(vSO.keyRing, EP_keyRing); 869 | 870 | PA_VerifySignature(&vSO); 871 | 872 | newnode->isValidPGPSignature = vSO.isValid; 873 | newnode->keyID= vSO.keyID; 874 | 875 | EP_MIMEParse(newnode); 876 | 877 | UT_free(content); 878 | remove(tmpfile); 879 | 880 | 881 | } /* parse_multipart_signed */ 882 | 883 | 884 | 885 | /* MM status report 886 | * Accepts: MAIL stream 887 | */ 888 | 889 | void status (MAILSTREAM *stream) 890 | { 891 | long i; 892 | char date[MAILTMPLEN]; 893 | rfc822_date (date); 894 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "%s",date); 895 | if (stream) 896 | { 897 | if (stream->mailbox) 898 | { 899 | ER_dbg_va (FAC_MM, ASP_MM_GEN, " %s mailbox: %s", 900 | stream->dtb->name,stream->mailbox); 901 | ER_dbg_va (FAC_MM, ASP_MM_GEN, " %lu messages, %lu recent", 902 | stream->nmsgs,stream->recent); 903 | } 904 | else ER_dbg_va (FAC_MM, ASP_MM_GEN, "% No mailbox is open on this stream"); 905 | if (stream->user_flags[0]) 906 | { 907 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "Keywords: %s",stream->user_flags[0]); 908 | for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i) 909 | ER_dbg_va (FAC_MM, ASP_MM_GEN,", %s",stream->user_flags[i]); 910 | /* puts (""); */ 911 | } 912 | } 913 | } /* status() */ 914 | 915 | 916 | Mail_Header_Field *get_mail_hdr_field (MAILSTREAM *stream, 917 | long mesgno, 918 | STRINGLIST *cur, 919 | const char *hdr_title) 920 | { 921 | 922 | char *tmphdr; 923 | char tmpline[MAXBUFSIZE]; 924 | int i, j, k, c, tmpsize, titlesize; 925 | int continuation = 0; 926 | Mail_Header_Field *hdr_field; 927 | Mail_Header_Field *mhfp; 928 | Mail_Header_Field *newmhfp; 929 | 930 | mhfp = hdr_field = newmhfp = NULL; 931 | 932 | tmphdr = get_header_line(stream,mesgno,cur,hdr_title); 933 | 934 | tmpsize = strlen(tmphdr); 935 | 936 | /* Length of the header title plus ":" */ 937 | titlesize = strlen(hdr_title) + 1; 938 | 939 | continuation = 0; /* to detect continuation lines */ 940 | j = 0; 941 | /* also initialize tmpline */ 942 | for(i = 0; i < MAXBUFSIZE; i++){ 943 | tmpline[i] = '\0'; 944 | } 945 | 946 | /* Get one line at a time, and put the header lines in the Mail Header Field */ 947 | 948 | for (i = 0; i < tmpsize; i++) 949 | { 950 | c = tmphdr[i]; 951 | if (c == 10) /* EOL */ 952 | { 953 | if ((j > 1) || ((mhfp == NULL) && (i == tmpsize - 1))) /* j>1 and not j>0 because "\r" is always read; 954 | * The second option is needed for 955 | * the empty headers */ 956 | { 957 | newmhfp = (Mail_Header_Field *)UT_malloc(sizeof(Mail_Header_Field)); 958 | newmhfp->next = NULL; 959 | newmhfp->field = (char *)UT_malloc(j + 2); 960 | if (j > 1) 961 | { 962 | if ( ! continuation ) 963 | /* We do not copy the header title from the first line */ 964 | sprintf (newmhfp->field,"%s\n",tmpline + titlesize); 965 | else 966 | /* There is no header title on continuation lines */ 967 | sprintf (newmhfp->field,"%s\n",tmpline); 968 | } 969 | else 970 | sprintf (newmhfp->field,"\n"); 971 | 972 | if (mhfp == NULL) 973 | { 974 | mhfp = newmhfp; 975 | hdr_field = newmhfp; 976 | } 977 | else 978 | { 979 | mhfp->next = newmhfp; 980 | mhfp = newmhfp; 981 | } 982 | } 983 | continuation = 1; /* next time we are reading continuation lines */ 984 | j = 0; 985 | /* re-initialize tmpline */ 986 | for (k = 0; k < MAXBUFSIZE; k++) 987 | { 988 | tmpline[k] = '\0'; 989 | } 990 | } 991 | else 992 | { 993 | sprintf (tmpline + j++,"%c", c); 994 | } 995 | } 996 | 997 | UT_free(tmphdr); 998 | 999 | return (hdr_field); 1000 | 1001 | } /* get_mail_hdr_field() */ 1002 | 1003 | 1004 | 1005 | char *get_header_line (MAILSTREAM *stream, long mesgno, STRINGLIST *cur, const char *hdr_title) 1006 | { 1007 | 1008 | unsigned long offset; 1009 | size_t tmplength; 1010 | char *curtmp; 1011 | char *hdr_attr; 1012 | long a,b; 1013 | 1014 | 1015 | /* We need to insert the header title into a STRINGLIST structure, as 1016 | * this is the type that must be supplied to mail_fetchheader_full. 1017 | */ 1018 | 1019 | cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) 1020 | cpystr (hdr_title))); 1021 | 1022 | /* If we don't want to return the header title, but only the contents, 1023 | * this offset allows us to strip the header title. The "magic number" 2 1024 | * is the string ": " of the header. 1025 | * This method is uneffective for multiple headers (ex. Cc, Reply-To, etc.). 1026 | */ 1027 | 1028 | offset = cur->text.size + 2; 1029 | 1030 | /* Get the header line, if it exists */ 1031 | 1032 | curtmp = mail_fetchheader_full (stream,mesgno,cur,NIL,NIL); 1033 | 1034 | tmplength = strlen(curtmp); 1035 | hdr_attr = (char *)UT_malloc(tmplength + 4); 1036 | 1037 | /* cur contains the header title string, like "From:", "Subject:" etc. 1038 | * tmplength is the length of the corresponding header line extracted 1039 | * from the message. If a real line is returned, the header title 1040 | * ("From:", "Subject:" etc.) will be contained within, hence 1041 | * tmplength >= cur->text.size . This means that if 1042 | * (cur->text.size > tmplength), no such header is present in the mail: 1043 | * we must return an (almost) empty string. 1044 | */ 1045 | 1046 | a = (long)tmplength; 1047 | b = (long)cur->text.size; 1048 | if (a > b) 1049 | { 1050 | /* If we want to strip the header */ 1051 | /*sprintf (hdr_attr,"%s",curtmp + offset); */ 1052 | sprintf (hdr_attr,"%s",curtmp); 1053 | /* printf ("%s",hdr_attr); */ 1054 | } 1055 | else 1056 | { 1057 | sprintf (hdr_attr,"\n\n"); 1058 | } 1059 | 1060 | return (hdr_attr); 1061 | } /* get_header_line() */ 1062 | 1063 | 1064 | 1065 | 1066 | /* Subroutine for writing in a file */ 1067 | 1068 | void write_file (char *filename, char *text, size_t text_size) 1069 | { 1070 | 1071 | FILE *fd; 1072 | size_t i; 1073 | 1074 | /* printf ("%s\n",filename); */ 1075 | 1076 | if ((fd = fopen(filename,"w")) != NULL) 1077 | { 1078 | for (i = 0; i < text_size; i++) 1079 | if (text[i] != 13) 1080 | fprintf (fd, "%c",text[i]); 1081 | fclose(fd); 1082 | } 1083 | else 1084 | { 1085 | ER_perror(FAC_MM, MM_CANTOPEN, "%s for writing\n",filename); 1086 | die; 1087 | } 1088 | 1089 | } /* write_file() */ 1090 | 1091 | 1092 | void read_file (const char *filename) 1093 | { 1094 | 1095 | FILE *fd; 1096 | int c; 1097 | 1098 | if ((fd = fopen (filename,"r")) != NULL) 1099 | { 1100 | while ((c = getc(fd)) != EOF) 1101 | putc (c, stdout); 1102 | fclose (fd); 1103 | } 1104 | else 1105 | { 1106 | ER_perror(FAC_MM, MM_CANTOPEN, "%s for reading\n",filename); 1107 | die; 1108 | } 1109 | 1110 | } /* read_file() */ 1111 | 1112 | 1113 | void put_in_file (char *fileprefix, char *extension, char *text, size_t text_size) 1114 | { 1115 | 1116 | char filename[FILENAMELEN]; 1117 | 1118 | 1119 | /* Write in a file */ 1120 | 1121 | sprintf (filename,"%s-%s",fileprefix,extension); 1122 | /* printf ("%s\n",filename); */ 1123 | 1124 | write_file(filename,text,text_size); 1125 | 1126 | }/* put_in_file() */ 1127 | 1128 | 1129 | /* Stolen from which_keytypes.c and converted to use regex.h instead of libgen.h */ 1130 | 1131 | 1132 | int do_regex_test (const char *pattern, char *string) 1133 | { 1134 | 1135 | int match = 0; 1136 | 1137 | /* These are not used, since REG_NOSUB is specified in regcomp() */ 1138 | size_t nmatch = 0; 1139 | regmatch_t pmatch[1]; 1140 | 1141 | regex_t *re; 1142 | 1143 | re = (regex_t *)UT_malloc(STR_XL); 1144 | 1145 | regcomp(re, pattern, REG_NOSUB || REG_NEWLINE); 1146 | if (regexec(re, string, nmatch, pmatch, 0)) 1147 | match = 0; 1148 | else 1149 | match = 1; 1150 | 1151 | regfree(re); 1152 | 1153 | /* Caution! regfree() does not do this job... */ 1154 | UT_free(re); 1155 | 1156 | return(match); 1157 | 1158 | } /* do_regex_test() */ 1159 | 1160 | 1161 | /* Interfaces to c-client. 1162 | * They must be here for the code to be compiled, 1163 | * but most can stay empty. 1164 | */ 1165 | 1166 | void mm_searched (MAILSTREAM *stream,unsigned long number) 1167 | { 1168 | } 1169 | 1170 | 1171 | void mm_exists (MAILSTREAM *stream,unsigned long number) 1172 | { 1173 | } 1174 | 1175 | 1176 | void mm_expunged (MAILSTREAM *stream,unsigned long number) 1177 | { 1178 | } 1179 | 1180 | 1181 | void mm_flags (MAILSTREAM *stream,unsigned long number) 1182 | { 1183 | } 1184 | 1185 | void mm_notify (MAILSTREAM *stream,char *string,long errflg) 1186 | { 1187 | } 1188 | 1189 | void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes) 1190 | { 1191 | } 1192 | 1193 | void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes) 1194 | { 1195 | } 1196 | 1197 | void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) 1198 | { 1199 | } 1200 | 1201 | void mm_log (char *string,long errflg) 1202 | { 1203 | switch ((short) errflg) { 1204 | case NIL: 1205 | ER_dbg_va (FAC_MM, ASP_MM_GEN, "[%s]",string); 1206 | break; 1207 | case PARSE: 1208 | case WARN: 1209 | ER_perror (FAC_MM, MM_WARNCCL, "%%%s",string); 1210 | break; 1211 | case ERROR: 1212 | ER_perror (FAC_MM, MM_ERRCCL, "%s",string); 1213 | break; 1214 | } 1215 | } 1216 | 1217 | void mm_dlog (char *string) 1218 | { 1219 | puts (string); 1220 | } 1221 | 1222 | void mm_login (NETMBX *mb,char *user,char *pwd,long trial) 1223 | { 1224 | } 1225 | 1226 | void mm_critical (MAILSTREAM *stream) 1227 | { 1228 | } 1229 | 1230 | void mm_nocritical (MAILSTREAM *stream) 1231 | { 1232 | } 1233 | 1234 | long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) 1235 | { 1236 | #if UNIXLIKE 1237 | kill (getpid (),SIGSTOP); 1238 | #else 1239 | abort (); 1240 | #endif 1241 | return NIL; 1242 | } 1243 | 1244 | void mm_fatal (char *string) 1245 | { 1246 | ER_perror(FAC_MM, MM_FATCCL, "%s\n",string); 1247 | die; 1248 | } 1249 |