/*************************************************************
 * WebMailFolder                                             *
 * COPYRIGHT 1998 by Norbert Kuemin <norbert.kuemin@gmx.net> *
 *************************************************************/
   
#define MIME_FILE
#include "wmf.h"

const char hex_alphabet[] = "0123456789ABCDEF";
const char base64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

static char *MIME_TEXT_BOUNDERY_START[] = {"boundary=\"","BOUNDARY=\"","boundary=",NULL};
static char *MIME_TEXT_BOUNDERY_END[] =   {"\"","\"","\n",NULL};

static char *MIME_TEXT_ATTACH_START[] = {"name=\"","NAME=\"","filename=",
    "name=",NULL};
static char *MIME_TEXT_ATTACH_END[] =   {"\"","\"","\n",";",NULL};

int IsUrlChar(char Zeichen) {
    if ((Zeichen >= 'A') && (Zeichen <= 'Z')) return TRUE;
    if ((Zeichen >= 'a') && (Zeichen <= 'z')) return TRUE;
    if ((Zeichen >= '0') && (Zeichen <= '9')) return TRUE;
    if (Zeichen == '.') return TRUE;
    if (Zeichen == ':') return TRUE;
    if (Zeichen == '/') return TRUE;
    if (Zeichen == '-') return TRUE;
    if (Zeichen == '_') return TRUE;
    if (Zeichen == '~') return TRUE;
    if (Zeichen == '?') return TRUE;
    if (Zeichen == '+') return TRUE;
    if (Zeichen == '&') return TRUE;
    if (Zeichen == ',') return TRUE;
    return FALSE;
}


int HeaderIsMimeEncoded(char *Zeile) {

    if (Zeile == NULL) return FALSE;
    if ((strstr(Zeile,"=?") != NULL) && (strstr(Zeile,"?=") != NULL)) {
	return TRUE;
    } else {
	return FALSE;
    }
}


int GetContentType(char *Zeile) {
    char *tmpstr;
    int n, retvalue;
    
    if (config.DebugLevel & 1)
	printf(".GetContentType(%s)\n",Zeile);

    /* Init */
    tmpstr = Zeile;
    retvalue = MIME_TYPE_UNKNOWN;

    if (tmpstr != NULL) {
	/* Remove leading tabs and spaces */
	while (((tmpstr[0] == '\t') || (tmpstr[0] == ' ')) && (tmpstr[0] != '\0'))
	    tmpstr++;
	tmpstr = dupchar(tmpstr);

	/* Convert to uppercase */
	for (n=0;n<strlen(tmpstr);n++) tmpstr[n] = toupper(tmpstr[n]);

	if (strncmp(tmpstr,"TEXT/PLAIN",10) == 0) retvalue = MIME_TYPE_TEXT_PLAIN;
	if (strncmp(tmpstr,"TEXT/ENRICHED",13) == 0) retvalue = MIME_TYPE_TEXT_ENRICHED;
	if (strncmp(tmpstr,"TEXT/HTML",9) == 0) retvalue = MIME_TYPE_TEXT_HTML;
	if (strncmp(tmpstr,"MULTIPART/MIXED",15) == 0) retvalue = MIME_TYPE_MULTIPART_MIXED;
	if (strncmp(tmpstr,"MULTIPART/SIGNED",15) == 0) retvalue = MIME_TYPE_MULTIPART_SIGNED;
	if (strncmp(tmpstr,"APPLICATION/OCTET-STREAM",24) == 0) retvalue = MIME_TYPE_APPLICATION_OCTETSTREAM;
	if (strncmp(tmpstr,"APPLICATION/PGP",15) == 0) retvalue = MIME_TYPE_APPLICATION_PGP;
	if (strncmp(tmpstr,"IMAGE/GIF",9) == 0) retvalue = MIME_TYPE_IMAGE_GIF;
	if (strncmp(tmpstr,"IMAGE/X-XBITMAP",15) == 0) retvalue = MIME_TYPE_IMAGE_XBITMAP;

	/* Cleanup */
	freechar(tmpstr);
	tmpstr = NULL;
    }
    if ((retvalue == MIME_TYPE_UNKNOWN) && (tmpstr != NULL)) {
	fprintf(stderr,"warning: unknown mime type \"%s\"\n",tmpstr);
    }

    return retvalue;
}

int GetContentEncode(char *Zeile) {
    char *tmpstr;
    int n, retvalue;
    
    if (config.DebugLevel & 1)
	printf(".GetContentEncode(%s)\n",Zeile);

    /* Init */
    tmpstr = Zeile;
    retvalue = MIME_ENCODE_UNKNOWN;

    if (tmpstr !=  NULL) {
	/* Remove leading tabs and spaces */
	while (((tmpstr[0] == '\t') || (tmpstr[0] == ' ')) && (tmpstr[0] != '\0'))
	    tmpstr++;
	tmpstr = dupchar(tmpstr);

	/* Convert to uppercase */
	for (n=0;n<strlen(tmpstr);n++) tmpstr[n] = toupper(tmpstr[n]);

	if (strncmp(tmpstr,"7BIT",4) == 0) retvalue = MIME_ENCODE_7BIT;
	if (strncmp(tmpstr,"8BIT",4) == 0) retvalue = MIME_ENCODE_8BIT;
	if (strncmp(tmpstr,"QUOTED-PRINTABLE",16) == 0) retvalue = MIME_ENCODE_QUOTED;
	if (strncmp(tmpstr,"BASE64",6) == 0) retvalue = MIME_ENCODE_BASE64;
	if (strncmp(tmpstr,"X-UUENCODE",6) == 0) retvalue = MIME_ENCODE_UUENCODE;

	/* Cleanup */
	freechar(tmpstr);
	tmpstr = NULL;
    }
    if ((retvalue == MIME_ENCODE_UNKNOWN) && (tmpstr !=  NULL)) {
	fprintf(stderr,"warning: unknown mime encode \"%s\"\n",tmpstr);
    }
    return retvalue;
}

char *GetBoundary(char *Zeile) {
    char *tmpstr, *retstr;
    int n;
    
    if (config.DebugLevel & 1)
	printf(".GetBoundary(%s)\n",Zeile);

    tmpstr = NULL;
    for(n = 0; MIME_TEXT_BOUNDERY_START[n] != NULL; n++) {
	tmpstr = strstr(Zeile,MIME_TEXT_BOUNDERY_START[n]);
	if (tmpstr != NULL) {
	    tmpstr = tmpstr + strlen(MIME_TEXT_BOUNDERY_START[n]);
	    break;
	}
    }
    if (tmpstr == NULL) return NULL;
    if (strstr(tmpstr,MIME_TEXT_BOUNDERY_END[n]) != NULL) {
	n = strstr(tmpstr,MIME_TEXT_BOUNDERY_END[n]) - tmpstr;
    } else {
	n = strlen(tmpstr);
    }
    retstr = newchar(n+1);
    retstr[0] = '\0';
    strncat(retstr,tmpstr,n);
    return retstr;
}


int GetCharset(char *Zeile) {
    char *tmpstr;
    int n, retvalue;
    
    if (config.DebugLevel & 1)
	printf(".GetCharset(%s)\n",Zeile);
    /* Init */
    tmpstr = Zeile;
    retvalue = MIME_CHARSET_UNKNOWN;

    if (tmpstr != NULL) {
	/* Remove leading tabs and spaces */
	while (((tmpstr[0] == '\t') || (tmpstr[0] == ' ')) && (tmpstr[0] != '\0'))
	    tmpstr++;
	tmpstr = dupchar(tmpstr);

	/* Convert to uppercase */
	for (n=0;n<strlen(tmpstr);n++) tmpstr[n] = toupper(tmpstr[n]);

	if (strstr(tmpstr,"US-ASCII") != NULL) retvalue = MIME_CHARSET_USASCII;
	if (strstr(tmpstr,"ISO-8859-1") != NULL) retvalue = MIME_CHARSET_8859_1;

	/* Cleanup */
	freechar(tmpstr);
	tmpstr = NULL;
    }
    /* send warning, if unknown */
    if ((retvalue == MIME_CHARSET_UNKNOWN) && (tmpstr != NULL)) {
	fprintf(stderr,"warning: unknown mime charset \"%s\"\n",tmpstr);
    }
    return retvalue;
}


char *GetAttachmentName(char *Zeile) {
    char *tmpstr, *retstr;
    int n;
    
    if (config.DebugLevel & 1) {
	printf(".GetAttachmentName(%s)\n",Zeile);
    }
    tmpstr = NULL;
    for(n = 0; MIME_TEXT_ATTACH_START[n] != NULL; n++) {
	tmpstr = strstr(Zeile,MIME_TEXT_ATTACH_START[n]);
	if (tmpstr != NULL) {
	    tmpstr = tmpstr + strlen(MIME_TEXT_ATTACH_START[n]);
	    break;
	}
    }
    if (tmpstr == NULL) {
	retstr = NULL;
    } else {
	if (strstr(tmpstr,MIME_TEXT_ATTACH_END[n]) != NULL) {
	    n = strstr(tmpstr,MIME_TEXT_ATTACH_END[n]) - tmpstr;
	} else {
	    n = strlen(tmpstr);
	}
	retstr = newchar(n+1);
	retstr[0] = '\0';
	strncat(retstr,tmpstr,n);
    }
    return retstr;
}

char *Quoted_2_Text(char *Zeile) {
    char *retstr, *tmpstr;
    int m;

    if (config.DebugLevel & 1)
	printf(".Quoted_2_Text(%s)\n",Zeile);

    if (Zeile == NULL) return dupchar("");

    /* quoted printable will NEVER be greater then the original text */
    retstr = newchar(strlen(Zeile)+10);

    m=0;
    retstr[m] = '\0';
    tmpstr = Zeile;

    while (tmpstr[0] != '\0') {
	switch (tmpstr[0]) {
	    case '=':
		/* Coded characters beginn with a = */
		if (tmpstr[1] == '\n') {
		    /* A = at the end of the line means no new line */
		    tmpstr++;
		}
		if ((strchr(hex_alphabet,tmpstr[1]) != NULL) &&
		    (strchr(hex_alphabet,tmpstr[2]) != NULL)) {
		    /* hex found after the = */
		    retstr[m] = 16 * (strchr(hex_alphabet,tmpstr[1])-hex_alphabet);
		    retstr[m] = retstr[m] + (strchr(hex_alphabet,tmpstr[2])-hex_alphabet);
		    if (config.DebugLevel & 2)
			printf("..CodeConvert (%c%c -> %d)\n",tmpstr[1],tmpstr[2],
			    retstr[m]);
		    m++;
		    tmpstr++;
		    tmpstr++;
		}
		break;
	    case '_':
		retstr[m] = ' ';
		m++;
		break;
	    default:
		/* normal character */
		retstr[m] = tmpstr[0];
		m++;
		break;
	}
	if (tmpstr[0] != '\0') tmpstr++;
    }
    retstr[m] = '\0';
    return retstr;
}


char *Text_2_Html(char *Zeile) {
    char *retstr, *tmpstr;

    if (config.DebugLevel & 1)
	printf(".Text_2_Html(%s)\n",Zeile);
    if (config.DebugLevel & 2) {
	if (Zeile != NULL) {
	    printf("..InputSize = %d\n",strlen(Zeile));
	} else {
	    printf("..InputSize = 0 (null)\n");
	}
    }

    if (Zeile == NULL) return dupchar("");

    retstr = newchar(strlen(Zeile)*10+1);
    retstr[0] = '\0';
    tmpstr = Zeile;

    while (tmpstr[0] != '\0') {
	switch (tmpstr[0]) {
	    case '<':
		strcat(retstr,"&lt;");
		break;
	    case '>':
		strcat(retstr,"&gt;");
		break;
	    case 'ä':
		strcat(retstr,"&auml;");
		break;
	    case 'ö':
		strcat(retstr,"&ouml;");
		break;
	    case 'ü':
		strcat(retstr,"&uuml;");
		break;
	    case 'Ä':
		strcat(retstr,"&Auml;");
		break;
	    case 'Ö':
		strcat(retstr,"&Ouml;");
		break;
	    case 'Ü':
		strcat(retstr,"&Uuml;");
		break;
	    case '©':
		strcat(retstr,"&copy;");
		break;
	    case 'ß':
		strcat(retstr,"&szlig;");
		break;
	    default:
		retstr[strlen(retstr)+1] = '\0';
		retstr[strlen(retstr)] = tmpstr[0];
		break;
	}
	tmpstr++;
    }
    return retstr;
}


char *Base64Decode(char *fourchars, int *length) {
    /* Converting max 4 chars (=4x6Bit -> 24Bit) to 3 chars(=3x8Bit) */

    char *retstr, *p1, *p2, *p3, *p4;
    int z1, z2, z3, z4;
    
    if (config.DebugLevel & 1)
	printf(".Base64Decode(%s)\n",fourchars);

    /* Don't accept strings greater as 4 and smaller as 2 chars */
    *length = 0;
    if ((strlen(fourchars) > 4) || (strlen(fourchars) < 2)) return NULL;

    /* Initialize */
    p1 = p2 = p3 = p4 = NULL;
    z1 = z2 = z3 = z4 = 64; /* 64 marks the end */
  
    /* Check, if every char is in the base64 alphabeth */
    switch (strlen(fourchars)) {
	case 4 :
	    if ((p4 = strchr(base64_alphabet,fourchars[3])) == NULL)
		return NULL;
	    z4 = p4 - base64_alphabet;
	case 3 :
	    if ((p3 = strchr(base64_alphabet,fourchars[2])) == NULL)
		return NULL;
	    z3 = p3 - base64_alphabet;
	case 2 :
	    if ((p2 = strchr(base64_alphabet,fourchars[1])) == NULL)
		return NULL;
	    z2 = p2 - base64_alphabet;
	case 1 :
	    if ((p1 = strchr(base64_alphabet,fourchars[0])) == NULL)
		return NULL;
	    z1 = p1 - base64_alphabet;
	case 0:
	    break;
    }

    /* We need at least 2 valid chars for a real char. */
    /* If a char (Nr. 3 or 4) is = 64 it marks the end */
    if ((z1 > 63) || (z2 > 63) || (z3 > 64) || (z4 > 64))
	return NULL;

    /* Calculating the length */
    *length = 3;
    if (z4 == 64) {
	if (z3 == 64)
	    *length = 1;
	else
	    *length = 2;
    }

    /* Time to creat and init the return string */
    retstr = newchar(3);
    retstr[0] =  retstr[1] =  retstr[2] = '\0';

    /* AAAAAA AABBBB BBBBCC CCCCCC -> AAAAAAAA BBBBBBBB CCCCCCCC */
    /*   3f   30  0F 3C  03   3F */
    switch (*length) {
	case 3:
	    retstr[2] = (z4 & 0x3f) | ((z3 & 0x03) << 6);
	case 2:
	    retstr[1] = ((z3 & 0x3c) >> 2) | ((z2 & 0x0f) << 4);
	case 1:
	    retstr[0] = ((z2 & 0x30) >> 4) | ((z1 & 0x3f) << 2);
	    break;
    }
    return retstr;
}


int IfUuencode(char *line) {

    if (line == NULL) return FALSE;
    if (strncmp(line,"begin ",6) == 0) {
	if (isoctal(line[6]) == TRUE) {
	    if (isoctal(line[7]) == TRUE) {
		if (isoctal(line[8]) == TRUE) {
		    if (isoctal(line[8]) == TRUE) {
			if (line[9] == ' ') return TRUE;
		    } else {
			if (line[8] == ' ') return TRUE;
		    }
		}
	    }
	}
    }
    return FALSE;
}


char *GetUuencodeFile(char *line) {

    char *retstr;
    int fileperm;

    if (config.DebugLevel & 1)
	printf(".GetUuencodeFile(%s)\n",line);

    if (line == NULL) return dupchar("");

    /* Init */
    retstr = NULL;
    fileperm = 0;
    
    retstr = newchar(strlen(line)-10);
    retstr[0] = 0;
    (void) sscanf(line,"begin %o %s", &fileperm, retstr);
    return retstr;
}


char *UuDecode(char *fourchars, int *length) {
    /* Converting max 4 chars (=4x6Bit -> 24Bit) to 3 chars(=3x8Bit) */

    char *retstr;
    int z1, z2, z3, z4;
    
    if (config.DebugLevel & 1)
	printf(".UuDecode(%s)\n",fourchars);

    /* Don't accept strings greater as 4 and smaller as 2 chars */
    *length = 0;
    if ((strlen(fourchars) > 4) || (strlen(fourchars) < 2)) return NULL;

    /* Initialize */
    z1 = z2 = z3 = z4 = 0; /* 64 marks the end */

    /* Check, if every char is in the base64 alphabeth */
    switch (strlen(fourchars)) {
	case 4 :
	    z4 = (fourchars[3] - ' ') & 077;
	case 3 :
	    z3 = (fourchars[2] - ' ') & 077;
	case 2 :
	    z2 = (fourchars[1] - ' ') & 077;
	case 1 :
	    z1 = (fourchars[0] - ' ') & 077;
	case 0:
	    break;
    }

    /* Calculating the length */
    *length = strlen(fourchars) - 1;

    /* Time to creat and init the return string */
    retstr = newchar(3);
    retstr[0] =  retstr[1] =  retstr[2] = 0;

    /* AAAAAA AABBBB BBBBCC CCCCCC -> AAAAAAAA BBBBBBBB CCCCCCCC */
    /*   3f   30  0F 3C  03   3F */
    switch (*length) {
	case 3:
	    retstr[2] = (z4 & 0x3f) | ((z3 & 0x03) << 6);
	case 2:
	    retstr[1] = ((z3 & 0x3c) >> 2) | ((z2 & 0x0f) << 4);
	case 1:
	    retstr[0] = ((z2 & 0x30) >> 4) | ((z1 & 0x3f) << 2);
	    break;
    }

    return retstr;
}

char *ByteConvert4to3(char *line, int *length, int MimeEncode) {
    /* Converting a line to binary (4 chars -> 3 Byte)*/

    char *retstr, *tmpstr, *tmpbin, *tmpline;
    int fourpackets, n, tmplen;

    if (config.DebugLevel & 1)
	printf(".ByteConvert4to3(%s)\n",line);

    if (line == NULL) return dupchar("");

    tmpline = line;
    if (MimeEncode == MIME_ENCODE_UUENCODE) {
	/* Cut away the length */
    	tmpline++;
    }

    /* Calculating amount of 4-char packets */
    fourpackets = (int) strlen(tmpline) / 4;
    if ((strlen(tmpline)/4) != fourpackets) fourpackets++;

    /* Init */
    retstr = newchar(fourpackets*3 + 1);
    retstr[0] = '\0';
    *length = 0;
    tmpbin = "\0";

    tmpstr = newchar(5);
    for(n=0;n<fourpackets;n++) {
	tmplen = 0;
	tmpstr[0] = '\0';
	strncat(tmpstr,(tmpline+n*4),4);
	switch (MimeEncode) {
	    case MIME_ENCODE_BASE64:
		tmpbin = Base64Decode(tmpstr,&tmplen);
		break;
	    case MIME_ENCODE_UUENCODE:
		tmpbin = UuDecode(tmpstr,&tmplen);
		break;
	}
	switch (tmplen) {
	    case 3: retstr[*length + 2] = tmpbin[2];
	    case 2: retstr[*length + 1] = tmpbin[1];
	    case 1: retstr[*length] = tmpbin[0];
	}
	*length = *length + tmplen;
	freechar(tmpbin);
    }
    freechar(tmpstr);
    return retstr;
}


char *DecodeMimeHeader(char *line) {
    char *ls_charset, *ls_mimedecode, *ls_text_decode, *ls_text_before;
    char *ls_text_after, *ls_return, *ls_temp, *ls_decode;
    int li_temp;

    if (config.DebugLevel & 1)
	printf(".DecodeMimeHeader(%s)\n",line);

    if (line == NULL) return dupchar("");

    /* Init */
    ls_return = NULL;

    /* get text before encoding */
    ls_text_before = dupchar(line);
    ls_temp = strstr(ls_text_before,"=?");
    ls_temp[0] = 0;
	
    /* get text encoding */
    ls_text_decode = dupchar(strstr(line,"=?")+2);
    ls_temp = ls_text_decode;
    while (strstr(ls_temp,"?=") != NULL) {
	ls_temp = strstr(ls_temp,"?=")+2;
    }
    ls_temp = ls_temp - 2;
    ls_temp[0] = 0;
	
    /* get text after encoding */
    ls_temp = line;
    while (strstr(ls_temp,"?=") != NULL) {
	ls_temp = strstr(ls_temp,"?=")+2;
    }
    ls_text_after = dupchar(ls_temp);

    /* split decode string */
    ls_charset = dupchar(ls_text_decode);
    ls_decode = dupchar(strstr(ls_text_decode,"?")+1);
    ls_mimedecode = dupchar(strstr(ls_decode,"?")+1);
    ls_temp = strstr(ls_charset,"?");
    ls_temp[0] = 0;
    ls_temp = strstr(ls_decode,"?");
    ls_temp[0] = 0;

    /* Convert decodestring to uppercase */
    for (li_temp=0;li_temp<strlen(ls_decode);li_temp++)
	ls_decode[li_temp] = toupper(ls_decode[li_temp]);

    if (strncmp(ls_decode,"Q",1) == 0) {
	/* quoted printable is indicated by a Q */
	ls_temp = Quoted_2_Text(ls_mimedecode);
	ls_return = newchar(strlen(ls_text_before)+strlen(ls_temp)+
	    strlen(ls_text_after)+1);
	sprintf(ls_return,"%s%s%s",ls_text_before,ls_temp,ls_text_after);
	freechar(ls_temp);
    }
    if (strncmp(ls_decode,"B",1) == 0) {
	/* base64 is indicated by a B */
	ls_temp = ByteConvert4to3(ls_mimedecode,&li_temp,MIME_ENCODE_BASE64);
	ls_return = newchar(strlen(ls_text_before)+strlen(ls_temp)+
	    strlen(ls_text_after)+1);
	sprintf(ls_return,"%s%s%s",ls_text_before,ls_temp,ls_text_after);
	freechar(ls_temp);
    }

    if (ls_return == NULL) ls_return = dupchar(line);

    freechar(ls_charset);
    freechar(ls_decode);
    freechar(ls_mimedecode);
    freechar(ls_text_before);
    freechar(ls_text_decode);
    freechar(ls_text_after);

    if (config.DebugLevel & 2)
	printf("..DecodeMimeHeader_Return(%s)\n",ls_return);

    return ls_return;
}
