a new filter for multipart/base64 encoded messages

Peter Englmaier <[email protected]> Wed, 02 Jul 2003 16:07:54 +0200
Newsgroups gmane.mail.procmail.devel
Message-ID <[email protected]>
--fUYQa+Pmc3FrFX/N
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi, 

I just wrote this little tool for decoding base64 encoded multipart
messages. I use this with procmail to find out if it contains
spam. The procmail code is as follows:

:0 f
* ^Content-Type: multipart/alternative
| base64-decoder

What it does is basically decoding each base64 encoded attachment
it encounters, leaving everything else untouched. The usual spam
checkers can then filter based on its contents.

A sample spam message is included which can be successfully decoded
with this tool. If anybody would like to hack on it and improve it,
I would like to get a copy. Perhaps procmail should include something
like this internally? Would be more efficient. Warning: the program
has not been tested a lot so far. It should probably do more tests
to ensure it won't decode images and such.

Best, Peter.

P.s.: The tool needs the program mimencode from the metamail package.



--fUYQa+Pmc3FrFX/N
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="base64-decoder.c"

// Filter email message and decode base64 attachments
// (c) by Peter Englmaier, 2003. GPL license.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void parsepart(char *endoffile);

int main()
{
  parsepart(NULL);
  return 0;
}


// parse a multi-part section of the input
void parsepart(char *endoffile)
{
  char *buffer, *boundary, *pos, *tfile=NULL, cmd[100];
  FILE *tfp;
  int mode=0, decode64=0, mime=0, multi=0, size=1000;

  buffer=malloc(size);
  boundary=malloc(size);
  if (!buffer || !boundary) {
    fprintf(stdout,"Out of memory\n");
    exit(1);
  }
  while (fgets(buffer, size, stdin)) {
    if (endoffile!=NULL && strstr(buffer, endoffile)!=NULL) {
      //	fprintf(stderr, "**** Detected boundary-return from parsepart\n");
	if (tfile) {
	  //	  fprintf(stderr, "**** closing temp file and decoding to stdout\n");
	  fclose(tfp);
	  sprintf(cmd, "mimencode -u %s", tfile);
	  fflush(stdout);
	  system(cmd);
	  unlink(tfile);
	  fprintf(stdout,"\n");
	}
	fprintf(stdout,"%s",buffer);
	break;
    }
    switch (mode) {
    case 0:// header
      if (buffer[0]=='\n') { // end of header?
	//		fprintf(stderr, "**** detected body\n");
	mode=1+multi; // switch to body mode
      }
      char *header1="Content-Length: ";
      if (strncmp(buffer, header1, strlen(header1))==0) {
	break;
      }
      header1="Lines: ";
      if (strncmp(buffer, header1, strlen(header1))==0) {
	break;
      }
      header1="Content-Transfer-Encoding: base64";
      if (strncmp(buffer, header1, strlen(header1))==0) {
	decode64=1;
	//	fprintf(stderr,"***base64 encoding detected\n");
	sprintf(buffer, "Content-Transfer-Encoding: quoted-printable\n");
      }
      header1="Content-type: multipart/alternative";
      if (strncmp(buffer, header1, strlen(header1))==0) {
	multi=1;
	//		fprintf(stderr, "**** detected multipart \n");
      }
      if (multi) {
	header1=" boundary=";
	pos=strstr(buffer, header1);
	if (pos!=NULL) {
	  if (1==sscanf(pos, " boundary=\"%[^\"]\"", boundary)) {
	    //	    	    fprintf(stderr,"**** Seen boundary >>%s<<\n", boundary);
	  }
	}
      }
      fprintf(stdout,"%s",buffer);
      break;
    case 1: //body 
      if (decode64) {
	if (tfile==NULL) {
	  tfile=tmpnam(NULL);
	  tfp=fopen(tfile, "w");
	}
	fprintf(tfp, "%s", buffer);
      } else {
	fprintf(stdout,"%s", buffer);
      }
      break;
    case 2: //body
      fprintf(stdout,"%s", buffer);
      parsepart(boundary);
    }	
    
  }

  if (buffer) free(buffer);
  if (boundary) free(boundary);
  return;
}

--fUYQa+Pmc3FrFX/N
Content-Type: application/x-gzip
Content-Disposition: attachment; filename="sample.spam.gz"
Content-Transfer-Encoding: base64

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

--fUYQa+Pmc3FrFX/N--