Subject: Re: MIME-files

From: "Koos Starfield" <[email-masked]>
Date: Tue, 04 Jul 2000 04:03:34 PDT

Hello,
This is not exactly what i am looking for, in the example you send you 
already have the Body of a message.
I want to parse a mime file without header information... in fact a file 
which looks like this:

----Begin of  file
MIME version
Content-Type: ......
Content-Encoding: .....
Content-Disposition: .....

filedata
----End of File

>From: amaury habrard <[email-masked]>
>To: Koos Starfield <[email-masked]>
>CC: <[email-masked]>
>Subject: Re: MIME-files
>Date: Tue, 04 Jul 2000 12:22:23 +0200
>
>  Koos Starfield <[email-masked]> wrote:
>'I need to read files which are in MIME format.'
>
>I am developping a little mime parser using the c-client library,
>I give you an example of my program, it is not finished and I think
>there are still some mistakes, but it can be an example to see how to
>process. And I have test all the types, but it works with MULTIPART
>messages. (if you see big bugs tell me)
>
>I hope this can help you.
>
>After you have the body of your mesg
>(for example like this:
>BODY * body=NULL;
>mail_fetchstructure(stream,no_msg,&body);
>if(body)  parse_body(stream,no_msg,body,NIL,(long) 0);
>/*where stream is the MAILSTREAM * of your mailbox and no_msg the
>message number you want to parse*/)
>
>you can use a parse function like this one (be careful it is not
>complete but it works on some multipart message):
>int parse_body(MAILSTREAM * stream,int no_msg,BODY * corps,char *
>pfx,long i)
>{
>   char tmp[10];
>   PART *part=NULL;
>   if (corps->type == TYPEMULTIPART) {/* "multipart"*/
>
>     /*pfx gives the section of the mail*/
>     if (pfx) sprintf (tmp,"%s%ld.",pfx,++i);
>     else tmp[0] = '\0';
>	       /*we examine each part*/
>     for (i = 0,part = corps->nested.part; part!=NULL; part=part->next)
>       {
>	parse_body(stream,no_msg,&part->body,tmp,++i);
>       }
>   }else{
>
>     switch(corps->type)
>       {/*NO MULTIPART HERE*/
>       case TYPETEXT:
>	{
>	  /*let's show that*/
>	   char * init_mesg=NULL;
>	   char * decode_msg=NULL;
>	   char section[100];
>	   long decode_length=0;
>	   printf("ii:%ld\n",i);
>	   if( pfx==NULL || !strcmp(pfx,"") )
>	     sprintf(section,"%ld",i);
>	   else sprintf(section,"%s.%ld",pfx,i);
>
>	   init_mesg=mail_fetchbody(stream,no_msg,section,
>                                     &(corps->size.bytes));
>
>	   /*if encoded*/
>	   switch(corps->encoding)
>	     {
>
>	     case ENCBASE64:
>
>decode_msg=(char*)rfc822_base64(init_mesg,corps->size.bytes,
>                                &decode_length);
>	       break;
>	     case ENCQUOTEDPRINTABLE:
>	       decode_msg=(char *)
>rfc822_qprint(init_mesg,corps->size.bytes,&decode_length);
>	       break;
>
>	     case ENC7BIT:
>	     case ENC8BIT:
>	     case ENCBINARY:
>	     case ENCOTHER:
>	     default:
>	       decode_msg=init_mesg;
>	     }
>
>	  printf("%s\n",decode_msg );
>	  break;
>	}
>       case TYPEMESSAGE: /*encapsulated message*/
>	{
>	  /*I'm not very sure*/
>
>	  if (!strcmp (corps->subtype,"RFC822")
>	      && (corps = corps->nested.msg->body))
>	    {
>	      if (corps->type == TYPEMULTIPART)
>parse_body(stream,no_msg,corps,pfx,i-1);
>	      else {
>		sprintf(tmp,"%s%ld.",pfx,i);
>		parse_body(stream,no_msg,corps,tmp,(long) 0);
>	      }
>	    }
>
>	  break;
>	case TYPEAPPLICATION:
>	  break;
>	case TYPEAUDIO:
>	  break;
>	case TYPEIMAGE: /*GIF,JPEG, ...........*/
>	  {
>	    long  F=-1; /*FILE descriptor*/
>	    char * init_mesg=NULL;
>	    char * decode_msg=NULL;
>	    char section[100];
>	    long decode_length=0;
>
>	    if( pfx==NULL || !strcmp(pfx,"") )
>	      sprintf(section,"%ld",i);
>	    else sprintf(section,"%s.%ld",pfx,i);
>
>	    /*GET the message*/
>	    init_mesg=mail_fetchbody(stream, no_msg,
>section,&(corps->size.bytes));
>
>	    /*if encoded*/
>	    switch(corps->encoding)
>	      {
>
>	      case ENCBASE64:
>		decode_msg=(char *)
>rfc822_base64(init_mesg,corps->size.bytes,&decode_length);
>		break;
>	      case ENCQUOTEDPRINTABLE:
>		decode_msg=(char *)
>rfc822_qprint(init_mesg,corps->size.bytes,&decode_length);
>		break;
>
>	      case ENC7BIT:
>
>	      case ENC8BIT:
>
>	      case ENCBINARY:
>
>	      case ENCOTHER: /*default*/
>
>	      default:
>	      decode_msg=init_mesg;
>  }
>
>	    if(!strcmp(corps->parameter->attribute,"NAME")) /*we have the
>name of the
>file*/
>F=open(corps->parameter->value,O_WRONLY|O_CREAT|O_TRUNC,0660);
>     else F=open("/tmp/tmpaux",O_WRONLY|O_CREAT|O_TRUNC,0660);
>
>	  if(F==-1)
>	    {
>	      fprintf(stderr,"Problems with the image_file\n");
>	    }
>	  else{
>	    write(F,decode_msg,decode_length);
>	    close(F);
>	  }
>	  break;
>	}
>	case TYPEVIDEO: /*type video */
>	case TYPEOTHER:
>	default:
>	}
>       }
>   }
>  return 1;
>}
>
>amaury.
>
>
>-------------------------------------------------
>A L i N T o - Restez joignable - www.alinto.com

________________________________________________________________________
Get Your Private, Free E-mail from MSN Hotmail at http://www.hotmail.com

---

From: amaury habrard <[email-masked]>
Date: Tue, 04 Jul 2000 14:17:15 +0200

>----End of File

Ok I see,
I don't know, if the c-client library is very useful fot this.

Try the libwww library, I know it provides function for MIME parsing
(headers and bodys).
http://www.w3.org/Library/

I hope it helps

Amaury.

---

From: John-Paul Robinson <[email-masked]>
Date: Fri, 07 Jul 2000 18:15:54 -0500

rfc822_parse_msg() is the function you need.  Keep in mind that any
poper MIME file, like  the template below, does have a header.  MIME
entities are composed of a header and a body.  Their structure is
identical to RFC822 messages, this is why they play together so nicely. 
A MIME entity sent via email is simply "layed over" an RFC822 message,
the result is that the MIME entity header and RFC822 header are merged. 
The resulting object effectively becomes a routable MIME entity.

Having said all that, you can combine some peices of the IMAP toolkit
and IMAP utils to get the job done. rfc822_parse_msg() will return a
populated BODY and ENVELOPE structure given a header and STRING
containing the body.  For the cases when the MIME entity's header
doesn't contain any RFC822 header fields, the ENVELOPE structure will be
unpopulated.

To build a program, you can take the "file_string" STRING driver from
the tmail.c code and use it to hold the MIME file of interest. This
STRING is prepared in the regular way.  In order to get the MIME header
out of your file, look to the rfc822_parse_content() function's section
on parsing a MIME MESSAGE part. If you want a nice listing of the
structure, check out the imapd.c code and see how it generates its
response to a BODYSTRUCTURE command (see the pbodystructure() function). 

I've attached an example program which covers these parts (except for
pretty printing the body structure).  You'll need to add callbacks to
make it work.  I hope you'll find it useful.

I find it helpful to keep in mind that the toolkit contains both an IMAP
server and the mailbox drivers, including an IMAP client. The server and
drivers combine to show us how to interpret the information generated by
the c-client API and how that information is populated from known
sources. Taking a look on both sides of the the c-client library can be
educational.

Please let me know if I've made any grave errors.

John-Paul

> >complete but it works on some multipart message): 

<yada yada yada>

/*
 * Parse given MIME file 
 */

/* standard include files */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "mail.h"           /* interface for toolkit library */
#include "osdep.h"          /* helps isolate us from platform */
#include "rfc822.h"
#include "misc.h"

extern int errno;

/*
  void local_searched (MAILSTREAM *s,unsigned long msgno);
  void local_exists (MAILSTREAM *s,unsigned long number);
*/
void file_string_init (STRING *s,void *data,unsigned long size);
char file_string_next (STRING *s);
void file_string_setpos (STRING *s,unsigned long i);
/*
  int opt_handler(char c, char* optarg);
*/
void show_msg_struct(char *prefix, ENVELOPE *envelope, BODY *body); 
void show_body_struct(char *prefix, BODY *body, int text);

/* File string driver for file stringstructs */

STRINGDRIVER file_string = {
  file_string_init,		/* initialize string structure */
  file_string_next,		/* get next byte in string structure */
  file_string_setpos		/* set position in string structure */
};

/* Cache buffer for file stringstructs */

#define CHUNKLEN 16384
char chunk[CHUNKLEN];

/* Initialize file string structure for file stringstruct
 * Accepts: string structure
 *	    pointer to string
 *	    size of string
 */

void file_string_init (STRING *s,void *data,unsigned long size)
{
  s->data = data;		/* note fd */
  s->size = size;		/* note size */
  s->chunk = chunk;
  s->chunksize = (unsigned long) CHUNKLEN;
  SETPOS (s,0);			/* set initial position */
}

/* Get next character from file stringstruct
 * Accepts: string structure
 * Returns: character, string structure chunk refreshed
 */

char file_string_next (STRING *s)
{
  char c = *s->curpos++;	/* get next byte */
				/* move to next chunk */
  SETPOS (s,s->offset + s->chunksize);
  return c;			/* return the byte */
}

/* Set string pointer position for file stringstruct
 * Accepts: string structure
 *	    new position
 */

void file_string_setpos (STRING *s,unsigned long i)
{
  s->offset = i;		/* set new offset */
				/* set size of data */
  s->cursize = min ((long) CHUNKLEN,s->size);
  s->curpos = s->chunk;		/* reset position */
				/* move to that position in the file */
  fseek ((FILE *) s->data,s->offset,SEEK_SET);
  fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
}

int main(int argc, char *argv[]) {
  MAILSTREAM *stream = NIL;
  FILE *mfile;
  STRING st;
  ENVELOPE *env;
  BODY *body;
  long msglen;
  unsigned long i, j, k;
  char c, c1, *s, *s1;

  /* prepare string driver */
  if (!(mfile =  fopen (argv[1], "r"))) {
    perror(argv[0]);
    exit(1);
  }
  if (fseek(mfile, 0, SEEK_END)) {
    perror(argv[0]);
    exit(1);
  }
  if (!(msglen = ftell(mfile))) {
    perror(argv[0]);
    exit(1);
  }
  INIT (&st,file_string,(void *) mfile,msglen);

  /* hunt for blank line */

  /* get message size, really same as above, from st for good measure */
  i = SIZE(&st);
  /*
   * this for loop will be stopped by either a CRLFCRLF or a LFLF
   * the OR in the loop end cares only about the LF*LF sequence.
   * the c and c1 logic is to "skip" the CR's and look for a LFLF combo
   * in either case we are left with the st set to the end of the header,
   * i.e. the LF of the blank line.
   */
  for (c = '\012',j = 0; (i > j) && ((c != '\012') || (CHR(&st) != '\012'));
       j++) if ((c1 = SNX (&st)) != '\015') c = c1;
  /*
   * if we only had a header we're at the end of the string
   * otherwise, the body starts at the next character
   * j++ because the size is 1 + the index.
   */
  if (i > j) {		/* unless no more text */
    c1 = SNX (&st);		/* body starts here */
    j++;			/* advance count */
  }

  /* copy header string */

  /* rewind to the beginning of the string 
   * note: in rfc822.c this goes to the current body->content.offset
   * but here we can just start at the front of the string since we
   * won't be in a nested part.
   * This copy leaves the STRING pointer at the start of body
   * so we're all ready for the parse.
   */
  SETPOS (&st,0);
  s = (char *) fs_get ((size_t) j + 1);
  for (s1 = s,k = j; k--; *s1++ = SNX (&st));
  s[j] = '\0';		/* tie off string (not really necessary) */

  rfc822_parse_msg_full(&env, &body,s,j, &st,"whats.this.do", 0, 0);
  fs_give((void *)s);
 
  /* inspect body and env here */ 

  /*
   * you might use code similar to that provided by [email-masked]
   * or look ot imapd.c and see how it parses the body to generate the 
   * IMAP BODYSTRUCTURE result.
   */

  /* free the body & envelope structs created by mail_fetchstructure */
  mail_free_body(&body);
  mail_free_envelope(&env);
  
  return(0);
}

/* put call backs here */