mime.c

来自「LINUX下的收发E-MAIL的程序」· C语言 代码 · 共 569 行

C
569
字号

#include "mime.h"

static char base64_chars[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

char *eml_mime_decode_base64(char *data, int *length)
{
    // This function is Copyright (C) 1999 by Jussi Junni of GnoMail 
    char *workspace, *p;
    unsigned long pos = 0;
    int i, a[4], len = 0;
    unsigned int kk;
    if (data == NULL)
	return NULL;

    kk = (unsigned) sizeof(char) * ((strlen(data) / 4) * 3 + 1);
    workspace = (char *) malloc(kk);
    memset(workspace, 0, (strlen(data) / 4) * 3 + 1);

    while (*data && len < *length) {
	for (i = 0; i < 4; i++, data++, len++) {
	    if ((p = strchr(base64_chars, *data)))
		a[i] = (int) (p - base64_chars);
	    else
		i--;
	}
	workspace[pos] = (((a[0] << 2) & 0xfc) | ((a[1] >> 4) & 0x03));
	workspace[pos + 1] = (((a[1] << 4) & 0xf0) | ((a[2] >> 2) & 0x0f));
	workspace[pos + 2] = (((a[2] << 6) & 0xc0) | (a[3] & 0x3f));
	if (a[2] == 64 && a[3] == 64) {
	    workspace[pos + 1] = 0;
	    pos -= 2;
	} else {
	    if (a[3] == 64) {
		workspace[pos + 2] = 0;
		pos--;
	    }
	}
	pos += 3;
    }
    workspace[kk - 1] = '\0';
    *length = pos;
    return workspace;
}


char *eml_mime_encode_base64(char *message, int *length, int flag)
{
    char *encoded, *index, buffer[3];
    int pos, len;

    encoded = malloc(sizeof(char) * (int) (*length * 1.40));	// it gets 33% larger 
    pos = 0;
    len = 0;
    index = message;
    while (index - message < *length) {
	memcpy(buffer, index, 3);
	*(encoded + pos) = base64_chars[(buffer[0] >> 2) & 0x3f];
	*(encoded + pos + 1) = base64_chars[((buffer[0] << 4) & 0x30) |
					    ((buffer[1] >> 4) & 0xf)];
	*(encoded + pos + 2) = base64_chars[((buffer[1] << 2) & 0x3c) |
					    ((buffer[2] >> 6) & 0x3)];
	*(encoded + pos + 3) = base64_chars[buffer[2] & 0x3f];
	len += 4;
	// base64 can only have 76 chars per line 
	if (len >= 76) {
	    *(encoded + pos + 4) = '\n';
	    pos++;
	    len = 0;
	}
	pos += 4;
	index += 3;
    }
    // if there were less then a full triplet left, we pad the remaining
    // encoded bytes with = 
    if (*length % 3 == 1) {
	*(encoded + pos - 1) = '=';
	*(encoded + pos - 2) = '=';
    }
    if (*length % 3 == 2) {
	*(encoded + pos - 1) = '=';
    }

    if (flag) {
	*(encoded + pos) = '\n';
	*(encoded + pos + 1) = '\0';
    } else
	*(encoded + pos) = '\0';

    *length = strlen(encoded);
    return encoded;
}


char *eml_mime_decode_quoted_printable(char *message, int *length)
{
    char *buffer, *index, ch[2];
    int i = 0, temp;
    if ((buffer = malloc(*length + 1)) == NULL) {
	printf("Cann't malloc memory\n");
	return NULL;
    }
    index = message;

    while (index - message < *length) {
	if (*index == '=') {
	    index++;
	    if (*index != '\n') {
		sscanf(index, "%2x", &temp);
		sprintf(ch, "%c", temp);
		buffer[i] = ch[0];
		i++;
		index += 2;
	    } else {
		index++;
	    }
	} else {
	    buffer[i] = *index;
	    i++;
	    index++;
	}
    }
    buffer[i] = '\0';
    *length = strlen(buffer);

    return buffer;
}


int eml_mime_detect(char *message)
{
    char *version;
    char *buffer;
    int major, minor;

    // if we detect the MIME-Version: field, this message is mime formatted 
    version = eml_mail_get_header_field(message, "MIME-Version:");
    if (version == NULL)
	return 0;

    buffer = get_field(version, 1, '.');
    major = atoi(buffer);
    if (buffer != NULL)
	free(buffer);
    else
	return 0;
    buffer = get_field(version, 2, '.');
    minor = atoi(buffer);
    if (buffer != NULL)
	free(buffer);
    else
	return 0;

    // if the version isn't 1.0 this is either some old mail or in the future
    // a mail with a newer mime spec that we dont support. We just output
    // a warning and try to parse it anyway. 
    if (major != 1 || minor != 0)
	printf("Warning: unknown Mime-Version %d.%d\n", major, minor);

    return 1;
}


int eml_mime_parse(char *message, Rmail * rmail)
{
    char *content_type, *content_transfer_e, *index, *body, *boundary_pos;
    char *content_disp;
    char type[64], subtype[64], parameter[128], boundary[128],
	filename[20];
    int i, pos, ret, ii, bindex = 0;
    struct mime_part *part;
    char *bps = NULL, *bps1 = NULL;
    int in_len;

    bindex = 1;

    if ((bps = strstr(message, "boundary=")) != NULL) {
	bps += 1;
	bps1 = strstr(bps, "boundary=");
	if (bps1 != NULL)
	    bindex++;
    }
    rmail->attach_file = NULL;
    rmail->content = NULL;
    for (ii = 0; ii < bindex; ii++) {
	if (ii == 0) {
	    content_type =
		eml_mail_get_header_field(message, "Content-Type:");
	} else {
	    content_type =
		eml_mail_get_header_field(bps1 - 50, "Content-Type:");

	}
	if (content_type != NULL) {
	    eml_mime_parse_content_type(content_type, type, subtype,
					parameter);
	} else {
	    strcpy(type, "text");
	    strcpy(subtype, "plain");
	}

	if (!strcasecmp(type, "multipart")) {
	    ret =
		eml_mime_get_parameter_value(parameter, "boundary",
					     boundary);
// no boundary? if this happens someone is sending us broken mail 
	    if (!ret)
		return 0;
	    body = strstr(message, "\n\n") + 2;
	    i = 0;
	    while ((index = strstr(body + i, boundary)) != NULL) {
// this is the end marking boundary, which has two - appended. 
		if (*(index + strlen(boundary)) == '-'
		    && *(index + strlen(boundary) + 1) == '-')
		    break;

		part = g_malloc0(sizeof(struct mime_part));
		part->pos = strstr(index, "\n\n") + 2 - message;
		content_type =
		    eml_mail_get_header_field(index + strlen(boundary),
					      "Content-Type:");
		content_disp =
		    eml_mail_get_header_field(index + strlen(boundary),
					      "Content-Disposition:");

		if (content_type != NULL)
		    eml_mime_parse_content_type(content_type, type,
						subtype, parameter);
		else {
		    strcpy(type, "text");
		    strcpy(subtype, "plain");
		}
		content_transfer_e =
		    eml_mail_get_header_field(index + strlen(boundary),
					      "Content-Transfer-Encoding:");
		if (content_transfer_e != NULL)
		    strncpy(part->encoding, content_transfer_e, 63);
		else
		    strcpy(part->encoding, "7bits");
		strncpy(part->type, type, 63);
		strncpy(part->subtype, subtype, 63);
		strncpy(part->parameter, parameter, 127);
		i = index - body + strlen(boundary);
		boundary_pos = strstr(body + i, boundary);
// some mailers apperently doesn't put an end boundary marker 
		if (boundary_pos == NULL) {
		    boundary_pos = body + i;
		    while (*boundary_pos != '\0')
			boundary_pos++;
		}
		part->len = boundary_pos - (strstr(index, "\n\n") + 2) - 2;
		if (content_disp != NULL) {
		    part->attachment = 1;
		} else
		    part->attachment = 0;
		if (strstr(part->type, "attachment")) {
		    if (search_filename_from_buffer(filename, part->type)
			>= 0)
			if (ii == 0) {
			    rmail->attach_file =
				malloc(strlen(filename) + 1);
			    strcpy(rmail->attach_file, filename);
			} else {
			    ret = strlen(rmail->attach_file);
			    rmail->attach_file =
				realloc(rmail->attach_file,
					ret + strlen(filename) + 2);
			    strcat(rmail->attach_file, ";");
			    strcat(rmail->attach_file, filename);
		    } else;
		} else if ((strstr(part->type, "text"))
			   && (strstr(part->subtype, "plain"))
			   && (part->len > 0)) {
		    rmail->content =
			eml_mime_get_part(message, &in_len, part);
		}
	    }

	} else {
	    part = g_malloc0(sizeof(struct mime_part));
	    pos = strstr(message, "\n\n") - message;
	    strncpy(part->type, type, 63);
	    strncpy(part->subtype, subtype, 63);
	    strncpy(part->parameter, parameter, 127);
	    content_transfer_e =
		eml_mail_get_header_field(message,
					  "Content-Transfer-Encoding:");

	    if (content_transfer_e != NULL)
		strncpy(part->encoding, content_transfer_e, 63);
	    else
		strcpy(part->encoding, "7bits");
	    part->pos = pos + 2;
	    index = message + part->pos;
	    i = 0;
	    while (*index != '\0') {
		i++;
		index++;
	    }
	    part->len = i;
	    part->attachment = 0;
	    if (part->len > 0)
		rmail->content = eml_mime_get_part(message, &in_len, part);
	}
    }
    return 0;
}


char *eml_mime_get_part(char *message, int *in_len, struct mime_part *tmp)
{
    char *part, *index, *temp, *ptr, *decoded;
    int i, len = 0, loop = 0;
    if (tmp == NULL)
	return (char *) NULL;

    part = malloc(tmp->len + 1);
    index = message + tmp->pos;
    if (!strcasecmp(tmp->encoding, "base64")) {
	len = tmp->len;
	printf("have enter:\n%s\n",index);
// remove all enters, decode_base64 doesn't like them 
	for (temp = index, i = 0; *temp != '\0' && i < tmp->len;
	     temp++, i++) {
	    while (*temp == '\n') {
		for (ptr = temp; *ptr != '\0'; ptr++)
		    *ptr = *(ptr + 1);
		len--;
	    }
	}
	decoded = eml_mime_decode_base64(index, &len);
	memcpy(part, decoded, len);
	part[len] = '\0';
	printf("have not enter:\n%s\n",part);
	free(decoded);
	*in_len = len;
    } else if (!strcasecmp(tmp->encoding, "quoted-printable")) {
	len = tmp->len;
	decoded = eml_mime_decode_quoted_printable(index, &len);
	strcpy(part, decoded);
	free(decoded);
	*in_len = len;
    } else {
	i = 0;
	while (*index != '\0' && i < tmp->len) {
	    part[i] = *index;
	    index++;
	    i++;
	}
	part[i] = '\0';
	*in_len = strlen(part);
    }
    return (char *) (part);
}


int eml_mime_parse_content_type(char *content_type, char *type,
				char *subtype, char *parameter)
{
    char *index, *start;
    int len, i;

    if (content_type == NULL)
	return 0;

    len = strlen(content_type);

    start = index = content_type;

    // copy the type 
    i = 0;
    while (*index != '/' && index - start < len) {
	type[i] = *index;
	i++;
	index++;
    }
    type[i] = '\0';

    // copy the subtype 
    index++;
    i = 0;
    while (*index != ';' && index - start < len) {
	subtype[i] = *index;
	index++;
	i++;
    }
    subtype[i] = '\0';

    index++;
    while (isspace(*index))
	index++;

    // copy the parameters 
    i = 0;
    while (*index != '\0' && index - start < len) {
	parameter[i] = *index;
	index++;
	i++;
    }
    parameter[i] = '\0';

    return 1;
}


int eml_mime_get_parameter_value(char *parameter, char *in_name, char *out)
{
    char *index, name[64], val[128];
    int i, found, len;

    index = parameter;
    len = strlen(parameter);
    while (*index != '\0' && index - parameter < len) {
	found = 0;
	// skip until a ascii char 
	while (!isalnum(*index)) {
	    index++;
	}

	i = 0;
	while (*index != '=' && i < 63) {
	    name[i] = *index;
	    index++;
	    i++;
	}
	name[i] = '\0';

	if (!strcasecmp(in_name, name)) {
	    found = 1;
	}

	index++;
	// parameters can be either name=val or name="val", we can handle both 
	if (*index != '"') {
	    i = 0;
	    while (*index != ';' && i < 127) {
		val[i] = *index;
		index++;
		i++;
	    }
	    val[i] = '\0';
	    if (found) {
		strncpy(out, val, 127);
		out[127] = '\0';
		parse_8bit(out);
		return 1;
	    }
	} else {
	    index++;
	    i = 0;
	    while (*index != '"' && i < 127) {
		val[i] = *index;
		index++;
		i++;
	    }
	    val[i] = '\0';

	    if (found) {
		strncpy(out, val, 127);
		out[127] = '\0';
		parse_8bit(out);
		return 1;
	    }


	}
	index += 2;
    }

    return 0;
}


// generates a random boundary 
char *eml_mime_get_boundary(void)
{
    static char boundary[128];
    char *index;
    int i;

    srand(time(NULL) + getpid());
    memset(boundary, 0, sizeof(boundary));

    sprintf(boundary, "animail");
    index = boundary + 7;
    i = 0;
    while (i < 20) {
	*(index + i) = (rand() % 26) + 65;
	i++;
    }
    *(index + i) = '\0';

    return boundary;
}


int eml_mime_insert_part(char **message, int index, char *boundary,
			 char *file)
{
    FILE *fp;
    struct stat st;
    int ret, i, x;
    unsigned int size;
    char *buffer, *encoded;
    char content_type[64];
    char content_transfer_encoding[64];

    ret = stat(file, &st);
    if (ret < 0) {
	printf("Couldn't stat %s", file);
	return 0;
    }

    size = st.st_size;

    // this should be autodetected , so we would use text/plain and quoted-printable
    // for attached text files and so on 
    snprintf(content_type, 64, "application/octet-stream; name=\"%s\"",
	     g_basename(file));
    strcpy(content_transfer_encoding, "base64");

    fp = fopen(file, "rt");
    if (fp == NULL) {
	printf("Couldn't open %s\n", file);
	return 0;
    }
    // todo: it isn't very convinient to load all the file into memory at once 
    // if the file is very large, 10 meg or something. (But who 
    // would attach such a file?) 
    buffer = g_malloc0(sizeof(char) * (size + 1));

    fread(buffer, 1, size, fp);
    fclose(fp);
    buffer[size] = '\0';

    encoded = eml_mime_encode_base64(buffer, &size, 0);
    free(buffer);


    // we realloc the string so it can hold the encoded data + the boundary +
    // the part header 
    message[0] =
	realloc(message[0],
		strlen(message[0]) + strlen(boundary) + strlen(encoded) +
		strlen(content_type) + strlen(content_transfer_encoding) +
		strlen(g_basename(file)) + 100);

    i = x = 0;
    // ok, here goes the part header 
    i += sprintf(message[0] + index + i, "--%s\n", boundary);
    i += sprintf(message[0] + index + i,
		 "Content-Type: %s\nContent-Transfer-Encoding: %s\n",
		 content_type, content_transfer_encoding);
    i += sprintf(message[0] + index + i,
		 "Content-Disposition: attachment; filename=\"%s\"\n\n",
		 g_basename(file));

    while (*(encoded + x) != '\0') {
	*(message[0] + index + i) = *(encoded + x);
	i++;
	x++;
    }
    *(message[0] + index + i) = '\0';
    free(encoded);

    return strlen(message[0] + index);
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?