1    | /***************************************
2    |   $Revision: 1.7 $
3    | 
4    |   mm - MIME Parser module. Functions to parse a mail message,
5    |   find if it is MIME-encapsulated, and return the parts of
6    |   the message which are supported by the UP module.
7    | 
8    |   Status: NOT REVUED 
9    | 
10   |   Design and implementation by: Daniele Arena
11   | 
12   |   ******************/ /******************
13   |   Copyright (c) 2000                              RIPE NCC
14   |  
15   |   All Rights Reserved
16   |   
17   |   Permission to use, copy, modify, and distribute this software and its
18   |   documentation for any purpose and without fee is hereby granted,
19   |   provided that the above copyright notice appear in all copies and that
20   |   both that copyright notice and this permission notice appear in
21   |   supporting documentation, and that the name of the author not be
22   |   used in advertising or publicity pertaining to distribution of the
23   |   software without specific, written prior permission.
24   |   
25   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
27   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
28   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
29   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
30   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31   |   ***************************************/
32   | 
33   | /* Parts of this code stolen from mtest.c, 
34   |  * part of the IMAP toolkit by Mark Crispin
35   |  */
36   | 
37   | /* Original version Copyright 1988 by The Leland Stanford Junior University
38   |  * Copyright 1999 by the University of Washington
39   |  *
40   |  *  Permission to use, copy, modify, and distribute this software and its
41   |  * documentation for any purpose and without fee is hereby granted, provided
42   |  * that the above copyright notices appear in all copies and that both the
43   |  * above copyright notices and this permission notice appear in supporting
44   |  * documentation, and that the name of the University of Washington or The
45   |  * Leland Stanford Junior University not be used in advertising or publicity
46   |  * pertaining to distribution of the software without specific, written prior
47   |  * permission.  This software is made available "as is", and
48   |  * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
49   |  * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
50   |  * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
51   |  * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
52   |  * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
53   |  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
54   |  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
55   |  * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
56   |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
57   |  *
58   |  */
59   | 
60   | 
61   | 
62   | /* Standard headers */
63   | #include <stdio.h>
64   | #include <signal.h>
65   | #include <string.h>
66   | #include <sys/time.h>
67   | #include <libgen.h>	/* This is a Solaris (System V) library. Not standard. */
68   | 
69   | 
70   | /* This is the local header */
71   | #include "mm.h"
72   | 
73   | 
74   | /* Comments about this module:
75   | 
76   |    - Still need to free() the allocated chunks. This is not strictly necessary,
77   |      as this module will be called each time from anew and then will bail out,
78   |      so all the memory will be freed anyway.
79   |      But for the sake of cleanness, this needs to be done.
80   |    - A good idea would be to use glib for allocations, linked lists etc.
81   |      This still needs to be done.
82   |    - Comments to be added.
83   |    - Cleanup of internal functions.
84   |    - printfs should be replaced with calls to ER module
85   | 
86   |    */
87   | 
88   | 
89   | 
90   | /***************************************
91   |  *
92   |  * API functions
93   |  *
94   |  ***************************************/
95   | 
96   | /* MM_decode. The main API function:
97   |    it parses the file mail_file, at the message mesgno,
98   |    and returns a structure pointing to files containing 
99   |    all the different MIME parts, plus more information.
100  |    It also returns some headers of the message.
101  | */
102  | 
103  | int MM_decode (
104  | 	       char *mail_file,			/* filename of the "mailbox" */
105  | 	       MM_header *mail_header,		/* Headers: to be returned */
106  | 	       MM_xmp_list *part_list,		/* List of MIME parts: to be returned */
107  | 	       long mesgno,			/* Message number in the mailbox */
108  | 	       long debug			/* debug level */
109  | 	       )
110  | {
111  | 
112  |   MAILSTREAM *stream = NULL;		/* MAILSTREAM is defined in c-client */
113  |   char tmp[MAILTMPLEN];			/* MAILTMPLEN is set in c-client */
114  |   int mm_retcode;			/* return code of the subroutine */
115  | 
116  | 
117  | #include "linkage.c"		/* c-client requires it to be included... */
118  |   
119  | 
120  |   sprintf (tmp, "%s", mail_file);
121  |   
122  |   /* open mailbox and get the mail stream */
123  |   stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL);
124  |   
125  |   
126  |   /* Process the stream */
127  |   if (!stream)
128  |     {
129  |       printf ("Invalid mailbox: %s\n", mail_file);
130  |       return (1);
131  |     }
132  |   else
133  |     {
134  | 
135  |       if (debug)
136  | 	{
137  | 	  printf ("------------------ Message status:\n");
138  | 	  status (stream);			/* report message status */
139  | 	  printf ("------------------ End of message status\n");
140  | 	  if (debug >= 2) 
141  | 	    printf ("================== DEBUG: Calling mm function...\n");
142  | 	}
143  | 
144  |       /* run "user interface" */
145  |       mm_retcode = mm (stream,mail_header,part_list,mesgno,debug);	
146  | 
147  |       return (mm_retcode);
148  |     }
149  | 
150  |   /* We should never get here... */
151  |   /* return(1); */
152  | 
153  | }
154  | 
155  | 
156  | /*********************************************/
157  | 
158  | 
159  | /* MM_store. Store stdin in a file. */
160  | 
161  | void MM_store (char *destination_file, long debug)
162  | {
163  | 
164  | 
165  | #define LINESIZE STR_S
166  | #define REGEXP "^From "
167  | #define FIRSTCHARS 10
168  | 
169  |   int c;
170  |   FILE *fd;
171  |   time_t ti = time (0);
172  |   char line[LINESIZE];
173  |   char *tmpstr;
174  |   int linechars = 0;
175  |   int i;
176  |   short charcount = 0;
177  |   char firstline[LINESIZE];
178  | 
179  |   if ((fd = fopen(destination_file,"w")) != NULL)
180  |     {
181  |       /* fprintf (fd,"From dbase@whois.ripe.net %s",ctime (&ti)); */
182  |       
183  |       /* This works. However, it can't be used since there is 
184  | 	 no line length limitation in e-mail messages... */
185  |       /* while (tmpstr = fgets(line, LINESIZE, stdin))
186  | 	 {
187  | 	 if (perform_regex_test(REGEXP,tmpstr)) fprintf (fd,">");
188  | 	 fputs (line,fd);
189  | 	 } */
190  | 	
191  |       /* A non-trivial file dump from stdin.
192  | 	 The problem here is that we need to store 
193  | 	 the beginning of each line to check if
194  | 	 the line starts with "From", in order to escape it with a ">".
195  | 	 The string-only method cannot be used, for mail messages don't have
196  | 	 a limit in line length 
197  | 	 (we cannot use "gets" for buffer overflow risks).
198  | 	 Thus we need to use a "mixed" method,
199  | 	 grabbing the first "LINESIZE" characters in a string to check with
200  | 	 regexp. This string is then dumped. All the characters not
201  | 	 at the beginning of the string are directly dumped with putc. */
202  | 
203  |       /* This is not a very generic algorithm... 
204  | 	 It is only fit when you are looking
205  | 	 for a match at the beginning of a line. 
206  | 	 BTW, the LINESIZE should be bigger
207  | 	 than the regexp you are trying to match... 
208  | 	 And: it only starts to work at the second line of the text... 
209  | 	 Basically, it's ugly but it fits our needs. */
210  | 
211  |       /* Reset string */
212  |       for (i = 0; i < LINESIZE; i++)
213  | 	firstline[i] = 0;
214  | 
215  | 
216  |       while ((c = getchar()) != EOF)
217  | 	{
218  | 	  /* This is done to write the file so that it can be 
219  | 	     interpreted by c-client as a mailbox in "unix" format:
220  | 	     the first line must start with "From " */
221  | 
222  | 	  /* Get first characters to see if the first line is a "^From " line */
223  | 	  if (charcount < FIRSTCHARS)
224  | 	    {
225  | 	      firstline[charcount] = c;
226  | 	      charcount++;
227  | 	      continue;
228  | 	    }
229  | 	  if (charcount == FIRSTCHARS)
230  | 	    {
231  | 	      /* If the first line is not a "^From " line, put a fake one */
232  | 	      if (!perform_regex_test(REGEXP,firstline))
233  | 		  fprintf (fd,"From dbase@whois.ripe.net %s",ctime (&ti));
234  | 	      charcount++; /* otherwise it executes this block forever */
235  | 	      fprintf (fd,"%s",firstline); /* dump all the string anyway */
236  | 	    }
237  | 
238  | 
239  | 	  /* Work with the rest of the message */
240  | 	  if ((c == 10)	||			/* new line or */
241  | 	      (linechars >= LINESIZE))		/* defined string length passed */
242  | 	    {
243  | 	      /* If there is a string in the buffer, the string is full or we have
244  | 		 a new line. We have to:
245  | 		 - check for the regexp
246  | 		 - dump the string in the file 
247  | 		 - reset the string */
248  | 	      if (linechars)
249  | 		{
250  | 		  tmpstr = line;
251  | 		  if (perform_regex_test(REGEXP,tmpstr))	/* got regexp: */
252  | 		    fprintf (fd,">");				/* Escape the line */
253  | 		  fprintf (fd,"%s",line);			/* dump string anyway */
254  | 
255  | 		  /* Reset string */
256  | 		  linechars = 0;
257  | 		  for (i = 0; i < LINESIZE; i++)
258  | 		    line[i] = 0;
259  | 		}
260  | 	      
261  | 	      /* If we are at a new line, then start to get the string */
262  | 	      if (c == 10)
263  | 		linechars = 1;
264  | 	      putc (c,fd);	/* Dump the character anyway */
265  | 	    }
266  | 	  else if (linechars)		/* We are getting the string */
267  | 	    {
268  | 	      sprintf (line+linechars-1,"%c",c);
269  | 	      linechars++;
270  | 	    }
271  | 	  else				/* Too far from the start of the line: */
272  | 	    putc (c,fd);		/* We just dump the character to the file */
273  | 	} 
274  |       fclose(fd);
275  |     }
276  |   else
277  |     printf ("Error: couldn't open file %s for writing\n",destination_file);
278  | }
279  | 
280  | 
281  | /*********************************************/
282  | 
283  | /* MM_cleanup. Cleans the files containing the MIME parts
284  |    when they're not needed anymore.
285  |    Todo: also clean memory. */
286  | 
287  | void MM_cleanup (MM_xmp_list *part_list, long debug)
288  | {
289  |   MM_xmp *partptr;
290  | 
291  |   partptr = part_list->head;
292  | 
293  |   while (partptr != NULL)
294  |     {
295  |       if (debug) printf ("Removing file %s...\n",partptr->file);
296  |       remove(partptr->file);
297  |       partptr = partptr->next;
298  |     }
299  | 
300  | }
301  | 
302  | 
303  | /***************************************
304  |  *
305  |  * End of API functions
306  |  *
307  |  ***************************************/
308  | 
309  | 
310  | 
311  | /* User interface */
312  | 
313  | int mm (MAILSTREAM *stream, MM_header *hdr, MM_xmp_list *part_list, long mesgno, long debug)
314  | {
315  | 
316  |   char *section;
317  |   char *result;
318  |   char tmp[MAILTMPLEN];
319  |   char strtmp[MAILTMPLEN];
320  |   char *mailtext;
321  |   unsigned long length;
322  |   long flags;
323  |   BODY *body;
324  |   STRINGLIST *lines;
325  |   STRINGLIST *cur;
326  |   char fileprefix[FILENAMELEN];
327  |   struct timeval *currenttime;
328  |   pid_t proc_id;
329  |   MM_b_section *secptr;
330  |   MM_bs_list *section_list;
331  |   MM_b_section *tmpsecptr;
332  |   char *tmpsection;
333  |   MM_xmp *newpart;
334  |   int retcode = 0;
335  | 
336  | 
337  |   /* Initialize the list of the body sections */
338  | 
339  |   section_list = (MM_bs_list *)malloc(sizeof(MM_bs_list));
340  |   MM_bs_list_init (section_list);
341  |   
342  |   /* Create the filename prefix for the output files */
343  |   
344  |   currenttime = (struct timeval *)malloc(sizeof(struct timeval));
345  |   if (!gettimeofday(currenttime,NIL))
346  |     {
347  |       if (proc_id = getpid())
348  | 	{
349  | 	  sprintf (fileprefix,"%s/%s.%ld-%d",TEMPDIR,GLOBALPREFIX,currenttime->tv_sec,(int)proc_id);
350  | 	}
351  |       else printf ("ERROR: could not get Process ID\n");
352  |     }
353  |   else printf ("ERROR: Could not gettimeofday\n");
354  | 
355  | 
356  |   if (mesgno && (mesgno <= stream->nmsgs)) 
357  |     {
358  | 
359  |       /* Get the headers we need. */
360  |       
361  |       if (debug >= 2) printf ("================== DEBUG: my headers\n");
362  |       
363  |       
364  |       lines = mail_newstringlist ();      
365  |       cur = lines;
366  |       
367  |       /* Get information about the mentioned lines in the header */
368  |       
369  |       hdr->from = get_header_line(stream,mesgno,cur,"From");
370  |       
371  |       hdr->subject = get_header_line(stream,mesgno,cur,"Subject");
372  |       
373  |       hdr->date = get_header_line(stream,mesgno,cur,"Date");
374  |       
375  |       hdr->message_id = get_header_line(stream,mesgno,cur,"Message-ID");
376  |       
377  |       hdr->reply_to = get_header_line(stream,mesgno,cur,"Reply-To");
378  |       
379  |       hdr->cc = get_header_line(stream,mesgno,cur,"Cc");
380  |       
381  |       mail_free_stringlist (&lines);
382  |       
383  |       if (debug >= 2) printf ("================== DEBUG: After getting headers\n");
384  |       
385  |       
386  |       
387  |       if (debug) printf ("Message number: %lu. Total messages: %lu\n", mesgno, stream->nmsgs);
388  |       
389  |       
390  |       /* Get structure of the message: body 
391  | 	 (and envelope, which is unused) */
392  | 
393  |       if (debug >= 2) 
394  | 	printf ("================== DEBUG: Calling mail_fetchstructure...\n");
395  |       mail_fetchstructure (stream,mesgno,&body);
396  | 
397  | 
398  |       if (debug >= 2) 
399  | 	printf ("================== DEBUG: Printing body information...\n");
400  | 
401  |       if (body) 
402  | 	{
403  | 	  /*printf ("Body encoding: %d (%s)\n", body->encoding, body_encodings[body->encoding]);*/
404  | 
405  | 	  /* 
406  | 	   * Switch by supported body types.
407  | 	   * The supported body types are:
408  | 	   * - discrete:
409  | 	   * text/plain
410  | 	   * application/pgp
411  | 	   * application/pgp-signature (inside multipart/signed)
412  | 	   * - composite:
413  | 	   * multipart/mixed
414  | 	   * multipart/alternative
415  | 	   * multipart/signed
416  | 	   */
417  | 	  if (debug >= 2) printf ("================== DEBUG: Calling get_body_info...\n");
418  | 	  get_body_info (body,NIL,(long) 0, section_list, debug);
419  | 	  if (debug >= 2) printf ("================== DEBUG: After get_body_info...\n");
420  | 		      
421  | 	  secptr = section_list->head;
422  | 
423  | 	  if (debug >= 3)
424  | 	    {
425  | 	      printf ("================== DEBUG 3: number: %s\n",secptr->number);
426  | 	      printf ("================== DEBUG 3: type: %s\n",secptr->type);
427  | 	    }
428  | 
429  | 
430  | 
431  | 	  switch (body->type)
432  | 	    {
433  | 
434  | 	    case TYPETEXT:
435  | 	      mailtext = tmp;
436  | 	      if ((body->subtype) && (!strcmp(body->subtype,"PLAIN")))
437  | 		{
438  | 
439  | 		  /* Can this explode with huge messages? */
440  | 		  mailtext = mail_fetchtext(stream,mesgno);
441  | 
442  | 		  if (debug >= 3)
443  | 		    {
444  | 		      printf ("Type text/plain\n");
445  | 		      printf ("Message contents:\n");
446  | 		      printf ("%s\n",mailtext); 
447  | 		    }
448  | 
449  | 		  secptr->supported = 1;
450  | 
451  | 		}
452  | 	      else
453  | 		{
454  | 		  sprintf (mailtext,"Unsupported content type: %s",
455  | 			  body_types[body->type]);
456  | 		  if (body->subtype) sprintf (mailtext+strlen(mailtext),"/%s\n",body->subtype);
457  | 		  /* printf ("%s",mailtext); */
458  | 		  secptr->supported = 0;
459  | 		}
460  | 
461  | 		  /* Write in a file */
462  | 		  
463  | 		  put_in_file (fileprefix,"1",mailtext,strlen(mailtext));
464  | 
465  | 	      break;
466  | 
467  | 	    case TYPEAPPLICATION:
468  | 
469  | 	      mailtext = tmp;
470  | 	      if ((body->subtype) && (!strcmp(body->subtype,"PGP")))
471  | 		{
472  | 		  mailtext = mail_fetchtext(stream,mesgno);
473  | 
474  | 		  /* printf ("Type application/pgp\n");
475  | 		     printf ("Message contents:\n");
476  | 		     printf ("%s\n",mailtext); */
477  | 
478  | 		  secptr->supported = 1;
479  | 
480  | 		}
481  | 	      else
482  | 		{
483  | 		  sprintf (mailtext,"Unsupported content type: %s",
484  | 			  body_types[body->type]);
485  | 		  if (body->subtype) sprintf (mailtext+strlen(mailtext),"/%s\n",body->subtype);
486  | 		  /* printf ("%s",mailtext); */
487  | 		  secptr->supported = 0;
488  | 		}
489  | 
490  | 	      /* Write in a file */
491  | 	      
492  | 	      put_in_file (fileprefix,"1",mailtext,strlen(mailtext));
493  | 
494  | 	      break;
495  | 
496  | 	    case TYPEMULTIPART:
497  | 	      if (body->subtype)
498  | 		{
499  | 		  if ((!strcmp(body->subtype,"MIXED")) || (!strcmp(body->subtype,"ALTERNATIVE")) || (!strcmp(body->subtype,"SIGNED")))
500  | 		    {
501  | 		      /* printf ("Supported content type: %s/%s\n",body_types[body->type],body->subtype); */
502  | 		      
503  | 		      
504  | 		      flags = 0;
505  | 		      if (debug) printf ("Sections:\n");
506  | 		      while (secptr != NULL)
507  | 			{
508  | 			  section = secptr->number;
509  | 			  if (debug) 
510  | 			    {
511  | 			      printf("++++++++++++++++++++++++++++++++++++++++++++\n");
512  | 			      printf ("%s\n",section);
513  | 			    }
514  | 			  /*printf ("%s\n",secptr->type);*/
515  | 			  
516  | 			  if ((!strcmp(secptr->type,"TEXT/PLAIN")) || (!strcmp(secptr->type,"APPLICATION/PGP-SIGNATURE")))
517  | 			    {
518  | 			      secptr->supported = 1;
519  | 			      result = mail_fetch_mime (stream, mesgno, section, &length, flags);
520  | 			      
521  | 			      
522  | 			      if (debug) 
523  | 				{
524  | 				  printf ("Supported content type: %s\n",secptr->type);
525  | 				  printf ("Length: %lu . Result: \n",length);
526  | 				}
527  | 			      
528  | 			      /* secptr->size: size of the contents of the body part.
529  | 				 length: size of the MIME header of the body part. */
530  | 			      
531  | 			      secptr->mime_headers = (char *)malloc(length);
532  | 			      
533  | 			      strncpy(secptr->mime_headers,result,(size_t)length);
534  | 			      
535  | 			      /* printf ("--MIME headers:\n%s\n--End of MIME headers\n",secptr->mime_headers); */
536  | 			      
537  | 			      secptr->contents = (char *)malloc(secptr->size);
538  | 			      
539  | 			      strncpy(secptr->contents,result + length,(size_t)secptr->size);
540  | 
541  | 			      /* Write in a file */
542  | 
543  | 			      put_in_file (fileprefix,section,secptr->contents,secptr->size);
544  | 			      
545  | 			    }
546  | 			  else
547  | 			    {
548  | 			      sprintf (strtmp,"Unsupported content type: %s\n",secptr->type);
549  | 			      secptr->supported = 0;
550  | 			      /* printf ("%s",strtmp); */
551  | 			      /* Write in a file */
552  | 			      put_in_file (fileprefix,section,strtmp,strlen(strtmp));
553  | 			    }
554  | 			  
555  | 			  
556  | 			  printf ("\n\n");
557  | 			  
558  | 			  
559  | 			  
560  | 			  secptr = secptr->next;
561  | 			}
562  | 		    }
563  | 		  else
564  | 		    {
565  | 		      sprintf (strtmp,"Unsupported content type: %s/%s\n",body_types[body->type],body->subtype);
566  | 		      secptr->supported = 0;
567  | 		      /* printf ("%s",strtmp); */
568  | 		      /* Write in a file */
569  | 		      put_in_file (fileprefix,"1",strtmp,strlen(strtmp));
570  | 		      /* Problem here - the notice is only written in the first file.
571  | 			 It is right, for we only should have one file for multipart/unsupported.
572  | 			 But from get_body_info, section_list is composed of all the parts
573  | 			 anyway...
574  | 			 a solution is to reduce here section_list to only one member,
575  | 			 as follows. */
576  | 		      secptr->next = NULL;
577  | 		      section_list->size = 1;
578  | 
579  | 		    }
580  | 		}
581  | 	      else
582  | 		{
583  | 
584  | 		  /* In current c-client implementation, we _should_ never get here,
585  | 		     since the subtype "unknown" is added if no subtype is
586  | 		     specified. */
587  | 
588  | 		  sprintf (strtmp,"Unknown multipart subtype\n");
589  | 		  secptr->supported = 0;
590  | 		  /* printf ("%s",strtmp); */
591  | 		  /* Write in a file */
592  | 		  put_in_file (fileprefix,"1",strtmp,strlen(strtmp));
593  | 		  /* Same problem here as above: the notice is
594  | 		     only written in the first file. We reduce the list to
595  | 		     a single member. */
596  | 		  secptr->next = NULL;
597  | 		  section_list->size = 1;
598  | 
599  | 		}
600  | 
601  | 	      break;
602  | 
603  | 	    default:
604  | 	      sprintf (strtmp,"Unsupported content type: %s",body_types[body->type]);
605  | 	      secptr->supported = 0;
606  | 	      if (body->subtype) sprintf (strtmp+strlen(strtmp),"/%s\n",body->subtype);
607  | 	      
608  | 	      /* printf ("%s",strtmp); */
609  | 
610  | 	      /* Write in a file */
611  | 	      put_in_file (fileprefix,"1",strtmp,strlen(strtmp));
612  | 	      break;
613  | 	    }
614  | 
615  | 
616  | 	  /* Copy the relevant information to the structure used
617  | 	     by the API, MM_xmp */
618  | 
619  | 	  tmpsecptr = section_list->head;
620  | 	  
621  | 	  while (tmpsecptr != NULL)
622  | 	    {
623  | 
624  | 	      newpart = (MM_xmp *)malloc(sizeof(MM_xmp));
625  | 
626  | 	      /* printf("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n"); */
627  | 	      tmpsection = tmpsecptr->number;
628  | 
629  | 	      newpart->number = (char *)malloc(strlen(tmpsection) + 1);
630  | 	      sprintf (newpart->number,"%s",tmpsection);
631  | 	      newpart->file = (char *)malloc(strlen(fileprefix) + strlen(tmpsection) + 2);
632  | 	      sprintf (newpart->file,"%s-%s",fileprefix,tmpsection);
633  | 	      newpart->type = (char *)malloc(strlen(tmpsecptr->type));
634  | 	      sprintf (newpart->type,"%s",tmpsecptr->type);
635  | 	      /* printf ("%s\n",newpart->number);
636  | 		 printf ("%s\n",newpart->file);
637  | 		 if (debug) printf ("Reading file %s...\n",newpart->file);
638  | 		 read_file(newpart->file); */
639  | 
640  | 	      newpart->supported = tmpsecptr->supported;
641  | 	      /* printf("Supported: %hd\n",newpart->supported); */
642  | 
643  | 	      MM_xmp_list_ins_last(part_list, newpart);
644  | 	      tmpsecptr = tmpsecptr->next;
645  | 	    }
646  | 	  
647  | 
648  | 	  
649  | 	}
650  |       else
651  | 	{
652  | 	  puts ("No body information available");
653  | 	  retcode = 1;
654  | 	}
655  |       
656  | 
657  | 
658  |     }
659  |   else 
660  |     {
661  |       printf ("Wrong message number: %lu. The maximum number of messages is %lu.\n",mesgno,stream->nmsgs);
662  |       retcode = 1;
663  |     }
664  |   
665  |   return(retcode);
666  |   
667  | }
668  | 
669  | 
670  | /* Internal functions */
671  | 
672  | 
673  | /* MM get body information
674  |  * Accepts: BODY structure pointer
675  |  *          prefix string
676  |  *          index
677  |  *          section list pointer
678  |  *          debug switch
679  |  */
680  | 
681  | /* This function has been taken almost unchanged from mtest.c,
682  |  * in the IMAP distribution. There, it is called display_body.
683  |  */
684  | 
685  | 
686  | void get_body_info (BODY *body,char *pfx,long i, MM_bs_list *section_list, long debug)
687  | {
688  | 
689  |   char tmp[MAILTMPLEN];
690  |   char sectno[MAILTMPLEN];
691  |   char sectype[MAILTMPLEN];
692  |   char *s = tmp;
693  |   PARAMETER *par;
694  |   PART *part;                  
695  |   MM_b_section *newsection;
696  | 
697  | 
698  |   if (body->type == TYPEMULTIPART) 
699  |     {
700  |       if (debug) printf ("++++multipart\n");
701  |       /* if not first time, extend prefix */
702  |       if (pfx) sprintf (tmp,"%s%ld.",pfx,++i);
703  |       else tmp[0] = '\0';
704  |       for (i = 0,part = body->nested.part; part; part = part->next)
705  | 	get_body_info (&part->body,tmp,i++, section_list, debug);
706  |     }
707  |   else 
708  |     {                        /* non-multipart, output oneline descriptor */
709  |       if (debug) printf ("++++nonmultipart\n");
710  |       if (!pfx) pfx = "";         /* dummy prefix if top level */
711  | 
712  |       sprintf (s," %s%ld %s",pfx,++i,body_types[body->type]);
713  | 
714  |       newsection = (MM_b_section *)malloc(sizeof(MM_b_section));
715  | 
716  |       newsection->number = (char *) malloc (strlen(sectno)+1);
717  |       sprintf (sectno,"%s%ld",pfx,i);
718  |       sprintf (newsection->number,"%s",sectno);
719  | 
720  |       sprintf (sectype, "%s",body_types[body->type]);
721  | 
722  |       if (body->subtype) 
723  | 	{
724  | 	  sprintf (s += strlen (s),"/%s",body->subtype);
725  | 	  sprintf (sectype + strlen (sectype),"/%s",body->subtype);
726  | 	}
727  | 
728  |       newsection->type = (char *) malloc (strlen(sectype)+1);
729  | 
730  |       sprintf (newsection->type,"%s",sectype);     
731  | 
732  |       /* Insert an element at the end of the list */
733  | 
734  |       MM_bs_list_ins_last (section_list, newsection);
735  | 
736  | 
737  |  
738  |       if (body->description) sprintf (s += strlen (s)," (%s)",body->description);
739  | 
740  | 
741  |       if ((par = body->parameter)) do
742  | 	sprintf (s += strlen (s),";%s=%s",par->attribute,par->value);
743  | 				 while ((par = par->next));
744  |       if (body->id) sprintf (s += strlen (s),", id = %s",body->id);
745  |       switch (body->type) {       /* bytes or lines depending upon body type */
746  |       case TYPEMESSAGE:           /* encapsulated message */
747  |       case TYPETEXT:              /* plain text */
748  | 	sprintf (s += strlen (s)," (%lu lines)",body->size.lines);
749  | 	newsection->size = body->size.bytes;
750  | 	sprintf (s += strlen (s),"\n   size: %lu",body->contents.text.size);
751  | 	break;
752  |       default:
753  | 	sprintf (s += strlen (s)," (%lu bytes)",body->size.bytes);
754  | 	newsection->size = body->size.bytes;
755  | 	break;
756  |       }
757  |       if (debug) puts (tmp);                 /* output this line */
758  | 
759  |       /* We should never arrive here, since this body type is not supported.
760  | 	 This part should be deleted. */
761  |       /* encapsulated message? */
762  |       /* if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822") &&
763  |        * (body = body->nested.msg->body)) 
764  |        * {
765  |        * if (body->type == TYPEMULTIPART) get_body_info (body,pfx,i-1, section_list, debug);
766  |        * else 
767  |        * {                    /* build encapsulation prefix */
768  |       /* sprintf (tmp,"%s%ld.",pfx,i);
769  |        * get_body_info (body,tmp,(long) 0, section_list, debug); 
770  |        * }
771  |        * }
772  |        */
773  |     }
774  | 
775  |   return;
776  | 
777  | }
778  | 
779  | 
780  | /* MM status report
781  |  * Accepts: MAIL stream
782  |  */
783  | 
784  | void status (MAILSTREAM *stream)
785  | {
786  |   long i;
787  |   char date[MAILTMPLEN];
788  |   rfc822_date (date);
789  |   puts (date);
790  |   if (stream) 
791  |     {
792  |       if (stream->mailbox)
793  | 	printf (" %s mailbox: %s, %lu messages, %lu recent\n",
794  | 		stream->dtb->name,stream->mailbox,stream->nmsgs,stream->recent);
795  |       else puts ("%No mailbox is open on this stream");
796  |       if (stream->user_flags[0]) 
797  | 	{
798  | 	  printf ("Keywords: %s",stream->user_flags[0]);
799  | 	  for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
800  | 	    printf (", %s",stream->user_flags[i]);
801  | 	  puts ("");
802  | 	}
803  |     }
804  | }
805  | 
806  | 
807  | 
808  | /*Initialize body_section list */
809  | 
810  | void MM_bs_list_init (MM_bs_list *section_list)
811  | {
812  | 
813  |   section_list->size = 0;
814  |   section_list->head = NULL;
815  |   section_list->tail = NULL;
816  |   /* return; */
817  | 
818  | }
819  | 
820  | /* Insert an element at the end of the body_section list */
821  | 
822  | void MM_bs_list_ins_last (MM_bs_list *section_list, MM_b_section *newsection)
823  | {
824  |   
825  |   if (section_list->size == 0)
826  |     {
827  |       section_list->head = newsection;
828  |       section_list->tail = newsection;
829  |       section_list->size++;
830  |     }
831  |   else
832  |     {
833  |       section_list->tail->next = newsection;
834  |       section_list->tail = newsection;
835  |       section_list->size++;
836  |     }
837  | 
838  |       newsection->next = NULL;
839  | 
840  | }
841  | 
842  | 
843  | /*Initialize extracted_mimepart list */
844  | 
845  | void MM_xmp_list_init (MM_xmp_list *part_list)
846  | {
847  | 
848  |   part_list->size = 0;
849  |   part_list->head = NULL;
850  |   part_list->tail = NULL;
851  |   /* return; */
852  | 
853  | }
854  | 
855  | 
856  | /* Insert an element at the end of the body_section list */
857  | 
858  | void MM_xmp_list_ins_last (MM_xmp_list *part_list, MM_xmp *newpart)
859  | {
860  |   
861  |   if (part_list->size == 0)
862  |     {
863  |       part_list->head = newpart;
864  |       part_list->tail = newpart;
865  |       part_list->size++;
866  |     }
867  |   else
868  |     {
869  |       part_list->tail->next = newpart;
870  |       part_list->tail = newpart;
871  |       part_list->size++;
872  |     }
873  | 
874  |       newpart->next = NULL;
875  | 
876  | }
877  | 
878  | 
879  | char *get_header_line (MAILSTREAM *stream, long mesgno, STRINGLIST *cur, char *hdr_title)
880  | {
881  | 
882  |   unsigned long offset;
883  |   size_t tmplength;
884  |   char *curtmp;
885  |   char *hdr_attr;
886  |   long a,b;
887  | 
888  | 
889  |   /* We need to insert the header title into a STRINGLIST structure, as
890  |    * this is the type that must be supplied to mail_fetchheader_full.
891  |    */
892  | 
893  |   cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *) 
894  | 				     cpystr (hdr_title)));
895  |   
896  |   /* We don't want to return the header title, but only the contents.
897  |    * This offset allows us to strip the header title.
898  |    */
899  |   
900  |   offset = cur->text.size + 2;
901  |   
902  |   /* Get the header line, if it exists */
903  |   
904  |   curtmp = mail_fetchheader_full (stream,mesgno,cur,NIL,NIL);
905  | 
906  |   tmplength = strlen(curtmp);
907  |   hdr_attr = (char *)malloc(tmplength);
908  |   
909  |   /* cur contains the header title string, like "From:", "Subject:" etc.
910  |    * tmplength is the length of the corresponding header line extracted
911  |    * from the message. If a real line is returned, the header title
912  |    * ("From:", "Subject:" etc.) will be contained within, hence
913  |    * tmplength >= cur->text.size . This means that if
914  |    * (cur->text.size > tmplength), no such header is present in the mail:
915  |    * we must return an (almost) empty string.
916  |    */
917  |   
918  |   a = (long)tmplength;
919  |   b = (long)cur->text.size;
920  |   if (a > b)
921  |     {
922  |       sprintf (hdr_attr,"%s",curtmp + offset);
923  |       /* printf ("%s",hdr_attr); */
924  |     }
925  |   else
926  |     {
927  |       sprintf (hdr_attr,"\n\n");
928  |     }
929  |   
930  |   return (hdr_attr);
931  | }
932  | 
933  | 
934  | /* Subroutine for writing in a file */
935  | 
936  | void write_file (char *filename, char *text, size_t text_size)
937  | {
938  | 
939  |   FILE *fd;
940  |   size_t i;
941  | 
942  |   /* printf ("%s\n",filename); */
943  |   
944  |   if ((fd = fopen(filename,"w")) != NULL)
945  |     {
946  |       for (i = 0; i < text_size; i++)
947  | 	fprintf (fd, "%c",text[i]);
948  |       fclose(fd);
949  |     }
950  |   else
951  |     printf ("Error: could not open file %s\n",filename);
952  |   
953  | }
954  | 
955  | 
956  | void read_file (char *filename)
957  | {
958  | 
959  |   FILE *fd;
960  |   int c;
961  | 
962  |   if ((fd = fopen (filename,"r")) != NULL)
963  |     {
964  |       while ((c = getc(fd)) != EOF)
965  | 	putc (c, stdout);
966  |       fclose (fd);
967  |     }
968  |   else
969  |     printf ("Error: could not open file %s\n",filename);
970  | 
971  | }
972  | 
973  | 
974  | void put_in_file (char *fileprefix, char *extension, char *text, size_t text_size)
975  | {
976  | 
977  |   char filename[FILENAMELEN];
978  | 
979  | 
980  |   /* Write in a file */
981  |   
982  |   sprintf (filename,"%s-%s",fileprefix,extension);
983  |   /* printf ("%s\n",filename); */
984  |   
985  |   write_file(filename,text,text_size);
986  |   
987  | }
988  | 
989  | 
990  | /* Stolen from which_keytypes.h */
991  | /* Actually, it only works with SysV libgen.h
992  |  * It should be rendered POSIX-compliant, and use regexp.h
993  |  */
994  | 
995  | static int perform_regex_test(const char *pattern, char *string) 
996  | {
997  |   int match;
998  | 
999  |   char *re;
1000 | 
1001 |   re = regcmp(pattern, (char*)0);
1002 |   if (regex(re, string) == NULL) {
1003 |     match = 0;
1004 |   }
1005 |   else {
1006 |     match = 1;
1007 |   }
1008 | 
1009 |   free(re); /* not a wrapper, because we have not allocated it */
1010 | 
1011 |   return match;
1012 | } /* perform_regex_test() */
1013 | 
1014 | 
1015 | 
1016 | /* Interfaces to c-client.
1017 |  * They must be here for the code to be compiled,
1018 |  * but most can stay empty.
1019 |  */
1020 | 
1021 | void mm_searched (MAILSTREAM *stream,unsigned long number)
1022 | {
1023 | }
1024 | 
1025 | 
1026 | void mm_exists (MAILSTREAM *stream,unsigned long number)
1027 | {
1028 | }
1029 | 
1030 | 
1031 | void mm_expunged (MAILSTREAM *stream,unsigned long number)
1032 | {
1033 | }
1034 | 
1035 | 
1036 | void mm_flags (MAILSTREAM *stream,unsigned long number)
1037 | {
1038 | }
1039 | 
1040 | void mm_notify (MAILSTREAM *stream,char *string,long errflg)
1041 | {
1042 | }
1043 | 
1044 | void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
1045 | {
1046 | }
1047 | 
1048 | void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
1049 | {
1050 | }
1051 | 
1052 | void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
1053 | {
1054 | }
1055 | 
1056 | void mm_log (char *string,long errflg)
1057 | {
1058 |   switch ((short) errflg) {
1059 |   case NIL:
1060 |     printf ("[%s]\n",string);
1061 |     break;
1062 |   case PARSE:
1063 |   case WARN:
1064 |     printf ("%%%s\n",string);
1065 |     break;
1066 |   case ERROR:
1067 |     printf ("?%s\n",string);
1068 |     break;
1069 |   }
1070 | }
1071 | 
1072 | void mm_dlog (char *string)
1073 | {
1074 |   puts (string);
1075 | }
1076 | 
1077 | void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
1078 | {
1079 | }
1080 | 
1081 | void mm_critical (MAILSTREAM *stream)
1082 | {
1083 | }
1084 | 
1085 | void mm_nocritical (MAILSTREAM *stream)
1086 | {
1087 | }
1088 | 
1089 | long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
1090 | {
1091 | #if UNIXLIKE
1092 |   kill (getpid (),SIGSTOP);
1093 | #else
1094 |   abort ();
1095 | #endif
1096 |   return NIL;
1097 | }
1098 | 
1099 | void mm_fatal (char *string)
1100 | {
1101 |   printf ("?%s\n",string);
1102 | }
1103 |