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