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 |