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 |