/*
** This program reads a raw email file and attempts to decode it into
** a more human-readable format. The following decodings are done:
**
** (1) Header values are prefixed by "| " at the left margin.
**
** (2) Content-Transfer-Encoding is recognized and the content is
** decoded for display.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define BINARY 0
#define BASE64 1
#define QUOTED 2
static int decode_hex(char c){
if( c>='0' && c<='9' ) return c - '0';
if( c>='A' && c<='F' ) return c - 'A' + 10;
if( c>='a' && c<='f' ) return c - 'a' + 10;
return -1;
}
static void convert_file(const char *zFilename, FILE *in){
int inHdr = 1;
int n;
int nBoundary;
int decodeType = 0;
int textMimetype = 1;
char *zB;
char zBoundary[200];
char zLine[5000];
char zOut[5000];
while( fgets(zLine, sizeof(zLine), in) ){
if( !inHdr
&& zLine[0]=='-'
&& zLine[1]=='-'
&& strncmp(zLine+2,zBoundary,nBoundary)==0
){
printf("|----------------- end of body section ---------|\n");
inHdr = 1;
}
if( !inHdr ){
if( textMimetype && decodeType==BASE64 ){
int ii, jj, c, x, y;
int bits = 0;
for(ii=jj=0; (c = zLine[ii])!=0; ii++){
if( c>='A' && c<='Z' ){
x = c - 'A';
}else if( c>='a' && c<='z' ){
x = c - 'a' + 26;
}else if( c>='0' && c<='9' ){
x = c - '0' + 52;
}else if( c=='+' ){
x = 62;
}else if( c=='/' ){
x = 63;
}else if( c=='=' ){
x = 0;
}else{
continue;
}
if( bits==0 ){
y = x;
bits = 6;
}else if( bits==6 ){
zOut[jj++] = ((y<<2) & 0xfc) | ((x>>4) & 0x03);
y = x & 0xf;
bits = 4;
}else if( bits==4 ){
zOut[jj++] = ((y<<4) & 0xf0) | ((x>>2) & 0x0f);
y = x & 0x3;
bits = 2;
}else if( bits==2 ){
zOut[jj++] = ((y<<6) & 0xc0) | (x & 0x3f);
bits = 0;
}
}
zOut[jj] = 0;
printf("%s", zOut);
}else if( textMimetype && decodeType==QUOTED ){
int ii, jj, c;
for(ii=jj=0; (c = zLine[ii])!=0; ii++){
if( c=='=' ){
int x1 = decode_hex(zLine[ii+1]);
int x2 = decode_hex(zLine[ii+2]);
if( x1>=0 && x2>=0 ){
zOut[jj++] = (x1<<4) | x2;
ii += 2;
}else if( zLine[ii+1]=='\r' && zLine[ii+2]=='\n' ){
ii += 2;
}
}else{
zOut[jj++] = c;
}
}
zOut[jj] = 0;
printf("%s", zOut);
}else{
printf("%s", zLine);
}
continue;
}
n = (int)strlen(zLine);
while( n>0 && isspace(zLine[n-1]) ){ n--; }
zLine[n] = 0;
if( n==0 ){
inHdr = 0;
printf("|----------------- end of header ---------------|\n");
continue;
}
printf("| %s\n", zLine);
if( strncasecmp(zLine,"Content-Type:", 13)==0 ){
textMimetype = strstr(zLine, "text/")!=0;
printf("|** %s content type **|\n",
textMimetype ? "Text" : "Non-text");
}
if( strncasecmp(zLine,"Content-Transfer-Encoding:", 26)==0 ){
if( strcasestr(zLine, "base64") ){
decodeType = BASE64;
}else if( strcasestr(zLine, "quoted-printable") ){
decodeType = QUOTED;
}else{
decodeType = BINARY;
}
printf("|** Content encoding %s **|\n",
decodeType==BASE64 ? "BASE64" :
decodeType==QUOTED ? "QUOTED" : "BINARY");
}
zB = strstr(zLine, "boundary=\"");
if( zB ){
int kk;
zB += 10;
for(kk=0; zB[kk] && zB[kk]!='"' && kk<sizeof(zBoundary)-1; kk++){
zBoundary[kk] = zB[kk];
}
zBoundary[kk] = 0;
nBoundary = kk;
printf("|** boundary [%s] **|\n", zBoundary);
}
}
}
int main(int argc, char **argv){
if( argc==1 ){
convert_file("<stdin>", stdin);
return 0;
}else{
int i;
for(i=1; i<argc; i++){
FILE *in = fopen(argv[i], "rb");
if( in==0 ){
fprintf(stderr, "cannot open \"%s\"", argv[i]);
}else{
convert_file(argv[i], in);
fclose(in);
}
}
}
return 0;
}