/* Mixmaster version 2.9 -- (C) 1999 - 2003 Anonymizer Inc. and others.
Mixmaster may be redistributed and modified under certain conditions.
This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
ANY KIND, either express or implied. See the file COPYRIGHT for
details.
MIME functions
$Id: mime.c 665 2003-11-09 01:47:32Z rabbi $ */
#include "mix.h"
#include "mix3.h"
#include <ctype.h>
#include <string.h>
#ifdef USE_PGP
# include "pgp.h"
#endif
#define hex(i) (isdigit(i) ? (i) - '0' : tolower(i) - 'a' + 10)
#define hexdigit(i) ((byte)(i < 10 ? i + '0' : i - 10 + 'A'))
static void encode_word(BUFFER *in)
{
BUFFER *out;
int i;
out = buf_new();
for (i = 0; i < in->length; i++)
if (in->data[i] < 32 || in->data[i] >= 127 || in->data[i] == '='
|| in->data[i] == '?' || in->data[i] == '_') {
buf_appendc(out, '=');
buf_appendc(out, hexdigit(in->data[i] / 16));
buf_appendc(out, hexdigit(in->data[i] % 16));
} else if (in->data[i] == ' ')
buf_appendc(out, '_');
else
buf_appendc(out, in->data[i]);
buf_move(in, out);
buf_free(out);
}
void body_encode(BUFFER *in, int transport, BUFFER *hdr)
{
BUFFER *out;
int c, l=0, encoding = 0;
out = buf_new();
buf_clear(hdr);
l = in->ptr;
while ((c = buf_getc(in)) != -1 && encoding != 2) {
if (c >= 160)
encoding = 1;
else if (c == ' ') {
if (buf_getc(in) == '\n')
encoding = 1;
buf_ungetc(in);
} else if ((c < 32 && c != ' ' && c != '\n' && c != '\t') ||
(c >= 127 && c < 160)) {
encoding = 2;
}
}
in->ptr = l;
if (encoding == 2) {
buf_sets(hdr, "Content-Transfer-Encoding: base64\n");
encode(in, 76);
} else {
#if 0
if (encoding == 0)
buf_sets(hdr, "Content-Transfer-Encoding: 7bit\n");
#endif
if (encoding != 0 && transport == MIME_8BIT)
buf_sets(hdr, "Content-Transfer-Encoding: 8bit\n");
if (encoding == 0 || transport == MIME_8BIT) {
buf_rest(out, in); /* transparent */
buf_move(in, out);
} else {
buf_sets(hdr, "Content-Transfer-Encoding: quoted-printable\n");
l = 0;
while ((c = buf_getc(in)) != -1) {
if (c == '\n') {
buf_nl(out);
l = 0;
}
else if (c < 32 || c >= 127 || c == '=') {
if (l > 73) {
buf_appends(out, "=\n");
l = 0;
}
buf_appendc(out, '=');
buf_appendc(out, hexdigit(c / 16));
buf_appendc(out, hexdigit(c % 16));
l += 3;
} else if (c == ' ') {
if (buf_getc(in) == '\n') {
buf_appendc(out, '=');
buf_appendc(out, hexdigit(c / 16));
buf_appendc(out, hexdigit(c % 16));
buf_nl(out);
l = 0;
} else {
buf_appendc(out, c);
buf_ungetc(in);
l++;
}
} else {
buf_appendc(out, c);
l++;
}
}
buf_move(in, out);
}
}
buf_free(out);
}
int mail_encode(BUFFER *in, int encoding)
{
BUFFER *out, *line, *tmp;
out = buf_new();
line = buf_new();
tmp = buf_new();
while (buf_getline(in, line) == 0) {
hdr_encode(line, 255);
buf_cat(out, line);
buf_nl(out);
}
if (in->ptr < in->length) {
/* no newline if only encoding header lines */
if (encoding == 0) {
buf_nl(out);
buf_rest(out, in);
}
else {
body_encode(in, encoding, line);
buf_cat(out, line);
buf_nl(out);
buf_cat(out, in);
}
}
buf_move(in, out);
buf_free(line);
buf_free(tmp);
buf_free(out);
return (0);
}
int hdr_encode(BUFFER *in, int n)
{
int i;
int encodeword = 0, encode = 0;
BUFFER *out, *word, *space;
out = buf_new();
word = buf_new();
space = buf_new();
for (i = 0; i <= in->length; i++) {
if (isspace(in->data[i]) || in->data[i] == '\0') {
if (word->length) {
if (encodeword) {
if (encode == 0) {
buf_cat(out, space);
buf_clear(space);
buf_appends(out, "=?");
buf_appends(out, MIMECHARSET);
buf_appends(out, "?Q?");
encode = 1;
} else {
buf_cat(space, word);
buf_move(word, space);
}
encode_word(word);
}
if (encode && !encodeword) {
encode = 0;
buf_appends(out, "?=");
}
buf_cat(out, space);
buf_cat(out, word);
encodeword = 0;
buf_clear(space);
buf_clear(word);
}
buf_appendc(space, in->data[i]);
} else {
if (in->data[i] < 32 || in->data[i] >= 127)
encodeword = 1;
buf_appendc(word, in->data[i]);
}
}
if (encode)
buf_appends(out, "?=");
buf_move(in, out);
while (n > 0 && in->length - in->ptr > n) {
for (i = 1; i < in->length - in->ptr; i++)
if (isspace(in->data[in->length - i]))
break;
buf_get(in, out, in->length - i);
buf_appends(out, "\n\t");
}
buf_rest(out, in);
buf_move(in, out);
buf_free(out);
buf_free(space);
buf_free(word);
return (0);
}
void addprintable(BUFFER *out, int c)
{
if (c == '\n')
buf_appendc(out, (char) c);
else if (c == '\t')
buf_appends(out, " ");
else if (c == '\014')
buf_appends(out, "^L");
else if (c == '\r') ;
else if (c <= 31 || (c >= 128 && c <= 128 + 31))
buf_appendc(out, '?');
else
buf_appendc(out, (char) c);
}
void addprintablebuf(BUFFER *out, BUFFER *in)
{
int c;
while ((c = buf_getc(in)) != -1)
addprintable(out, c);
}
int decode_line(BUFFER *line)
{
BUFFER *out;
unsigned int i;
int c, softbreak = 0;
out = buf_new();
for (i = 0; line->data[i] != '\0'; i++) {
if (line->data[i] == '=') {
if (isxdigit(line->data[i + 1]) && isxdigit(line->data[i + 2])) {
c = hex(line->data[i + 1]) * 16 + hex(line->data[i + 2]);
i += 2;
addprintable(out, c);
} else if (line->data[i + 1] == '\0') {
softbreak = 1;
break;
}
} else
addprintable(out, line->data[i]);
}
buf_move(line, out);
buf_free(out);
return (softbreak);
}
int decode_header(BUFFER *in)
{
int encoded = 0;
int c;
int err = 0;
int last = 0;
BUFFER *out;
out = buf_new();
for (in->ptr = 0; in->data[in->ptr] != '\0'; in->ptr++) {
if (encoded == 'q' && in->data[in->ptr] == '=' &&
isxdigit(in->data[in->ptr + 1])
&& isxdigit(in->data[in->ptr + 2])) {
c = hex(in->data[in->ptr + 1]) * 16 + hex(in->data[in->ptr + 2]);
in->ptr += 2;
addprintable(out, c);
} else if (encoded == 'q' && in->data[in->ptr] == '_')
buf_appendc(out, ' ');
else if (in->data[in->ptr] == '=' && in->data[in->ptr + 1] == '?' &&
in->data[in->ptr + 2] != '\0') {
if (last > 0 && out->length > last) {
out->data[last] = '\0';
out->length = last;
}
in->ptr++;
while (in->data[++in->ptr] != '?')
if (in->data[in->ptr] == 0) {
err = -1;
goto end;
}
if (in->data[in->ptr + 1] != '\0' && in->data[in->ptr + 2] == '?') {
encoded = tolower(in->data[in->ptr + 1]);
in->ptr += 2;
if (encoded == 'b') {
BUFFER *tmp;
tmp = buf_new();
decode(in, tmp);
addprintablebuf(out, tmp);
last = out->length;
buf_free(tmp);
} else if (encoded != 'q')
err = 1;
} else {
err = -1;
goto end;
}
} else if (encoded && in->data[in->ptr] == '?' &&
in->data[in->ptr + 1] == '=') {
in->ptr++;
last = out->length;
encoded = 0;
} else {
addprintable(out, in->data[in->ptr]);
if (!encoded || !isspace(in->data[in->ptr]))
last = out->length;
}
}
end:
if (err == -1)
buf_set(out, in);
buf_move(in, out);
buf_free(out);
return (err);
}
#define delimclose 2
int boundary(BUFFER *line, BUFFER *boundary)
{
int c;
if (boundary->length == 0 || !bufleft(line, "--") ||
!strleft(line->data + 2, boundary->data))
return (0);
line->ptr = boundary->length + 2;
for (;;) {
c = buf_getc(line);
if (c == -1)
return (1);
if (c == '-' && buf_getc(line) == '-')
return (delimclose);
if (!isspace(c))
return (0);
}
}
#define pgpenc 1
#define pgpsig 2
/*
* decodes non-multipart quoted printable message
*/
int qp_decode_message(BUFFER *msg)
{
BUFFER *out, *line, *field, *content;
out = buf_new();
line = buf_new();
field = buf_new();
content = buf_new();
buf_rewind(msg);
/* copy over headers without decoding */
while (buf_getheader(msg, field, content) == 0) {
if (bufieq(field, "content-transfer-encoding")
&& bufieq(content, "quoted-printable")) {
continue; /* no longer quoted-printable */
} else {
buf_appendheader(out, field, content);
}
}
buf_nl(out);
/* copy over body, quoted-printable decoding as we go */
while (buf_getline(msg, line) != -1) {
int softbreak;
softbreak = decode_line(line);
buf_cat(out, line);
if (!softbreak)
buf_nl(out);
}
buf_move(msg, out);
buf_free(out);
buf_free(line);
buf_free(field);
buf_free(content);
return 0;
}
int entity_decode(BUFFER *msg, int message, int mptype, BUFFER *data)
{
BUFFER *out, *line, *field, *content, *type, *subtype, *disposition,
*mboundary, *part, *sigdata;
int ret = 0, ptype = 0, partno = 0;
int p, encoded = 0;
out = buf_new();
line = buf_new();
field = buf_new();
content = buf_new();
type = buf_new();
subtype = buf_new();
disposition = buf_new();
mboundary = buf_new();
part = buf_new();
sigdata = buf_new();
if (message && bufileft(msg, "From ")) {
buf_getline(msg, out); /* envelope from */
buf_nl(out);
}
while (buf_getheader(msg, field, content) == 0) {
if (bufieq(field, "content-transfer-encoding") &&
bufieq(content, "quoted-printable"))
encoded = 'q';
if (bufieq(field, "content-type")) {
get_type(content, type, subtype);
if (bufieq(type, "multipart"))
get_parameter(content, "boundary", mboundary);
if (bufieq(type, "multipart") && bufieq(subtype, "encrypted")) {
get_parameter(content, "protocol", line);
if (bufieq(line, "application/pgp-encrypted"))
ptype = pgpenc;
}
if (bufieq(type, "multipart") && bufieq(subtype, "signed")) {
get_parameter(content, "protocol", line);
if (bufieq(line, "application/pgp-signature"))
ptype = pgpsig;
}
}
if (bufieq(field, "content-disposition"))
buf_set(disposition, content);
if (message) {
decode_header(content);
buf_appendheader(out, field, content);
}
}
if (message)
buf_nl(out);
if (bufifind(disposition, "attachment")) {
buf_appendf(out, "[-- %b attachment", type);
get_parameter(disposition, "filename", line);
if (line->length)
buf_appendf(out, " (%b)", line);
buf_appends(out, " --]\n");
}
if (mboundary->length) {
/* multipart */
while (buf_getline(msg, line) > -1 && !boundary(line, mboundary))
; /* ignore preamble */
while (buf_getline(msg, line) != -1) {
p = boundary(line, mboundary);
if (p) {
if (part->data[part->length - 1] == '\n')
part->data[--(part->length)] = '\0';
partno++;
if (ptype == pgpsig && partno == 1)
buf_set(sigdata, part);
ret += entity_decode(part, 0, ptype, sigdata);
buf_cat(out, part);
buf_clear(part);
if (p == delimclose)
break;
if (bufieq(subtype, "alternative") && ret > 0)
break;
if (bufieq(subtype, "mixed"))
buf_appends(out,
"[-------------------------------------------------------------------------]\n");
} else {
buf_cat(part, line);
buf_nl(part);
}
}
} else if (mptype == pgpenc && bufieq(type, "application") &&
bufieq(subtype, "pgp-encrypted")) {
/* application/pgp-encrypted part of multipart/encrypted */
; /* skip */
} else if (mptype == pgpenc && bufieq(type, "application") &&
bufieq(subtype, "octet-stream")) {
/* application/octet-stream part of multipart/encrypted */
int ok = 0;
buf_getline(msg, line);
if (bufleft(line, info_beginpgp)) {
if (buffind(line, "(SIGNED)")) {
buf_getline(msg, line);
buf_appends(out, "[-- OpenPGP message with signature --]\n");
if (bufleft(line, info_pgpsig))
buf_appendf(out, "[%s]\n",
line->data + sizeof(info_pgpsig) - 1);
else
buf_appends(out, "[Signature invalid]\n");
} else
buf_appends(out, "[-- OpenPGP message --]\n");
while (buf_getline(msg, line) != -1) {
if (bufleft(line, info_endpgp)) {
ret += entity_decode(part, 0, 0, NULL);
buf_cat(out, part);
buf_appends(out, "[-- End OpenPGP message --]\n");
ok = 1, ret++;
break;
}
buf_cat(part, line);
buf_nl(part);
}
}
if (!ok) {
buf_appends(out, "[-- Bad OpenPGP message --]\n");
buf_cat(out, msg);
}
} else if (mptype == pgpsig && bufeq(type, "application") &&
bufieq(subtype, "pgp-signature")) {
buf_rest(part, msg);
if (pgp_decrypt(part, NULL, data, PGPPUBRING, NULL) == PGP_SIGOK)
buf_appendf(out, "[-- OpenPGP signature from:\n %b --]\n", data);
else
buf_appends(out, "[-- Invalid OpenPGP signature --]\n");
} else if (type->length == 0 || bufieq(type, "text")) {
while (buf_getline(msg, line) != -1) {
int softbreak;
softbreak = encoded ? decode_line(line) : 0;
buf_cat(out, line);
if (!softbreak)
buf_nl(out);
}
ret++;
} else {
buf_appendf(out, "[-- %b/%b message part --]\n", type, subtype);
buf_cat(out, msg);
}
buf_move(msg, out);
buf_free(line);
buf_free(out);
buf_free(field);
buf_free(content);
buf_free(type);
buf_free(subtype);
buf_free(disposition);
buf_free(mboundary);
buf_free(part);
buf_free(sigdata);
return (0);
}
void mimedecode(BUFFER *msg)
{
entity_decode(msg, 1, 0, NULL);
}
int attachfile(BUFFER *message, BUFFER *filename)
{
BUFFER *type, *attachment;
FILE *f;
int ret = -1;
type = buf_new();
attachment = buf_new();
if ((bufiright(filename, ".txt") || !bufifind(filename, ".")) &&(strlen(DEFLTENTITY) != 0))
buf_sets(type, DEFLTENTITY);
else if (bufiright(filename, ".htm") || bufiright(filename, ".html"))
buf_sets(type, "text/html");
else if (bufiright(filename, ".jpeg"))
buf_sets(type, "image/jpeg");
else if (bufiright(filename, ".gif"))
buf_sets(type, "image/gif");
else if (bufiright(filename, ".pcm"))
buf_sets(type, "audio/basic");
else if (bufiright(filename, ".mpg") || bufiright(filename, ".mpeg"))
buf_sets(type, "video/mpeg");
else if (bufiright(filename, ".ps"))
buf_sets(type, "application/postscript");
else
buf_sets(type, "application/octet-stream");
f = fopen(filename->data, "r");
if (f) {
buf_read(attachment, f);
fclose(f);
ret = mime_attach(message, attachment, type);
}
buf_free(attachment);
buf_free(type);
return(ret);
}
int mime_attach(BUFFER *message, BUFFER *attachment, BUFFER *attachtype)
{
BUFFER *out, *part, *line, *type, *subtype, *mboundary, *field, *content;
int mimeheader = 0, multipart = 0, versionheader = 0;
out = buf_new();
line = buf_new();
part = buf_new();
type = buf_new();
subtype = buf_new();
mboundary = buf_new();
field = buf_new();
content = buf_new();
buf_rewind(message);
while (buf_getheader(message, field, content) == 0) {
if (bufieq(field, "mime-version"))
versionheader = 1;
if (bufieq(field, "content-type")) {
get_type(content, type, subtype);
if (bufieq(type, "multipart") && bufieq(subtype, "mixed")) {
multipart = 1;
get_parameter(content, "boundary", mboundary);
}
}
if (bufileft(field, "content-"))
mimeheader = 1;
}
if (mimeheader && !multipart) {
buf_rewind(message);
while (buf_getheader(message, field, content) == 0) {
if (bufileft(field, "content-"))
buf_appendheader(part, field, content);
else
buf_appendheader(out, field, content);
}
} else {
buf_ungetc(message);
buf_append(out, message->data, message->ptr);
buf_getc(message);
}
if (!versionheader)
buf_appends(out, "MIME-Version: 1.0\n");
if (!multipart) {
buf_setrnd(mboundary, 18);
encode(mboundary, 0);
buf_appendf(out, "Content-Type: multipart/mixed; boundary=\"%b\"\n",
mboundary);
}
buf_nl(out);
if (multipart) {
while (buf_getline(message, line) != -1) {
if (boundary(line, mboundary) == delimclose)
break;
buf_cat(out, line);
buf_nl(out);
}
} else {
buf_appendf(out, "--%b\n", mboundary);
if (part->length) {
buf_cat(out, part); /* body part header */
}
else {
if (strlen(DEFLTENTITY))
buf_appendf(out, "Content-Type: %s\n", DEFLTENTITY);
}
buf_nl(out);
buf_cat(out, message);
buf_nl(out);
}
buf_appendf(out, "--%b\n", mboundary);
buf_appendf(out, "Content-Type: %b\n", attachtype);
body_encode(attachment, MIME_8BIT, line);
buf_cat(out, line);
buf_nl(out);
buf_cat(out, attachment);
buf_appendf(out, "\n--%b--\n", mboundary);
buf_move(message, out);
buf_free(out);
buf_free(line);
buf_free(part);
buf_free(type);
buf_free(subtype);
buf_free(mboundary);
buf_free(field);
buf_free(content);
return (1);
}
static int entity_encode(BUFFER *message, BUFFER *out, BUFFER *messagehdr,
int encoding)
{
BUFFER *field, *content, *mboundary, *part, *line, *line2, *tmp;
field = buf_new();
content = buf_new();
mboundary = buf_new();
part = buf_new();
line = buf_new();
line2 = buf_new();
tmp = buf_new();
buf_rewind(message);
buf_clear(out);
buf_clear(messagehdr);
while (buf_getheader(message, field, content) == 0) {
if (bufileft(field, "content-"))
buf_appendheader(out, field, content);
else if (messagehdr)
buf_appendheader(messagehdr, field, content);
if (bufieq(field, "content-type")) {
get_type(content, line, tmp);
if (bufieq(line, "multipart"))
get_parameter(content, "boundary", mboundary);
}
}
buf_nl(out);
if (mboundary->length) {
while (buf_getline(message, line) != -1) {
buf_cat(out, line);
buf_nl(out);
if (boundary(line, mboundary))
break;
}
while (buf_getline(message, line) != -1) {
if (boundary(line, mboundary)) {
entity_encode(part, tmp, line2, encoding);
buf_cat(out, line2);
buf_cat(out, tmp);
buf_cat(out, line);
buf_nl(out);
buf_clear(part);
if (boundary(line, mboundary) == delimclose)
break;
} else {
buf_cat(part, line);
buf_nl(part);
}
}
} else
buf_rest(out, message);
buf_rewind(out);
mail_encode(out, encoding);
buf_free(field);
buf_free(content);
buf_free(mboundary);
buf_free(part);
buf_free(line);
buf_free(line2);
buf_free(tmp);
return (1);
}
int pgpmime_sign(BUFFER *message, BUFFER *uid, BUFFER *pass, char *secring)
{
BUFFER *out, *body, *mboundary, *algo;
int err;
out = buf_new();
body = buf_new();
mboundary = buf_new();
algo = buf_new();
pgp_signhashalgo(algo, uid, secring, pass);
entity_encode(message, body, out, MIME_7BIT);
buf_setrnd(mboundary, 18);
encode(mboundary, 0);
buf_appendf(out, "Content-Type: multipart/signed; boundary=\"%b\";\n",
mboundary);
buf_appendf(out,
"\tmicalg=pgp-%b; protocol=\"application/pgp-signature\"\n",
algo);
buf_nl(out);
buf_appendf(out, "--%b\n", mboundary);
buf_cat(out, body);
buf_nl(out);
buf_appendf(out, "--%b\n", mboundary);
err = pgp_encrypt(PGP_SIGN | PGP_TEXT | PGP_DETACHEDSIG, body, NULL,
uid, pass, NULL, secring);
buf_appends(out, "Content-Type: application/pgp-signature\n");
buf_nl(out);
buf_cat(out, body);
buf_nl(out);
buf_appendf(out, "--%b--\n", mboundary);
if (err == 0)
buf_move(message, out);
buf_free(out);
buf_free(body);
buf_free(mboundary);
buf_free(algo);
return (err);
}
syntax highlighted by Code2HTML, v. 0.9.1