/*
* mhoutsbr.c -- routines to output MIME messages
* -- given a Content structure
*
* $Id: mhoutsbr.c,v 1.7 2003/09/30 16:58:43 gbburkhardt Exp $
*
* This code is Copyright (c) 2002, by the authors of nmh. See the
* COPYRIGHT file in the root directory of the nmh distribution for
* complete copyright information.
*/
#include <h/mh.h>
#include <fcntl.h>
#include <h/signals.h>
#include <h/md5.h>
#include <errno.h>
#include <signal.h>
#include <h/mts.h>
#include <h/tws.h>
#include <h/mime.h>
#include <h/mhparse.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
extern int ebcdicsw;
static char ebcdicsafe[0x100] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static char nib2b64[0x40+1] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
* prototypes
*/
int output_message (CT, char *);
int writeBase64aux (FILE *, FILE *);
/*
* static prototypes
*/
static int output_content (CT, FILE *);
static void output_headers (CT, FILE *);
static int writeExternalBody (CT, FILE *);
static int write8Bit (CT, FILE *);
static int writeQuoted (CT, FILE *);
static int writeBase64 (CT, FILE *);
/*
* Main routine to output a MIME message contained
* in a Content structure, to a file. Any necessary
* transfer encoding is added.
*/
int
output_message (CT ct, char *file)
{
FILE *fp;
if ((fp = fopen (file, "w")) == NULL) {
advise (file, "unable to open for writing");
return NOTOK;
}
if (output_content (ct, fp) == NOTOK)
return NOTOK;
if (fflush (fp)) {
advise (file, "error writing to");
return NOTOK;
}
fclose (fp);
return OK;
}
/*
* Output a Content structure to a file.
*/
static int
output_content (CT ct, FILE *out)
{
int result = 0;
CI ci = &ct->c_ctinfo;
/*
* Output all header fields for this content
*/
output_headers (ct, out);
/*
* If this is the internal content structure for a
* "message/external", then we are done with the
* headers (since it has no body).
*/
if (ct->c_ctexbody)
return OK;
/*
* Now output the content bodies.
*/
switch (ct->c_type) {
case CT_MULTIPART:
{
struct multipart *m;
struct part *part;
if (ct->c_rfc934)
putc ('\n', out);
m = (struct multipart *) ct->c_ctparams;
for (part = m->mp_parts; part; part = part->mp_next) {
CT p = part->mp_part;
fprintf (out, "\n--%s\n", ci->ci_values[0]);
if (output_content (p, out) == NOTOK)
return NOTOK;
}
fprintf (out, "\n--%s--\n", ci->ci_values[0]);
}
break;
case CT_MESSAGE:
putc ('\n', out);
if (ct->c_subtype == MESSAGE_EXTERNAL) {
struct exbody *e;
e = (struct exbody *) ct->c_ctparams;
if (output_content (e->eb_content, out) == NOTOK)
return NOTOK;
/* output phantom body for access-type "mail-server" */
if (e->eb_body)
writeExternalBody (ct, out);
} else {
result = write8Bit (ct, out);
}
break;
/*
* Handle discrete types (text/application/audio/image/video)
*/
default:
switch (ct->c_encoding) {
case CE_7BIT:
putc ('\n', out);
result = write8Bit (ct, out);
break;
case CE_8BIT:
putc ('\n', out);
result = write8Bit (ct, out);
break;
case CE_QUOTED:
putc ('\n', out);
result = writeQuoted (ct, out);
break;
case CE_BASE64:
putc ('\n', out);
result = writeBase64 (ct, out);
break;
case CE_BINARY:
advise (NULL, "can't handle binary transfer encoding in content");
result = NOTOK;
break;
default:
advise (NULL, "unknown transfer encoding in content");
result = NOTOK;
break;
}
break;
}
return result;
}
/*
* Output all the header fields for a content
*/
static void
output_headers (CT ct, FILE *out)
{
HF hp;
hp = ct->c_first_hf;
while (hp) {
fprintf (out, "%s:%s", hp->name, hp->value);
hp = hp->next;
}
}
/*
* Write the phantom body for access-type "mail-server".
*/
static int
writeExternalBody (CT ct, FILE *out)
{
char **ap, **ep, *cp;
struct exbody *e = (struct exbody *) ct->c_ctparams;
putc ('\n', out);
for (cp = e->eb_body; *cp; cp++) {
CT ct2 = e->eb_content;
CI ci2 = &ct2->c_ctinfo;
if (*cp == '\\') {
switch (*++cp) {
case 'I':
if (ct2->c_id) {
char *dp = trimcpy (ct2->c_id);
fputs (dp, out);
free (dp);
}
continue;
case 'N':
for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
if (!strcasecmp (*ap, "name")) {
fprintf (out, "%s", *ep);
break;
}
continue;
case 'T':
fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
fprintf (out, "; %s=\"%s\"", *ap, *ep);
continue;
case 'n':
putc ('\n', out);
continue;
case 't':
putc ('\t', out);
continue;
case '\0':
cp--;
break;
case '\\':
case '"':
break;
default:
putc ('\\', out);
break;
}
}
putc (*cp, out);
}
putc ('\n', out);
return OK;
}
/*
* Output a content without any transfer encoding
*/
static int
write8Bit (CT ct, FILE *out)
{
int fd;
char c, *file, buffer[BUFSIZ];
CE ce = ct->c_cefile;
file = NULL;
if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
return NOTOK;
c = '\n';
while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
c = buffer[strlen (buffer) - 1];
fputs (buffer, out);
}
if (c != '\n')
putc ('\n', out);
(*ct->c_ceclosefnx) (ct);
return OK;
}
/*
* Output a content using quoted-printable
*/
static int
writeQuoted (CT ct, FILE *out)
{
int fd;
char *cp, *file;
char c, buffer[BUFSIZ];
CE ce = ct->c_cefile;
file = NULL;
if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
return NOTOK;
while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
int n;
cp = buffer + strlen (buffer) - 1;
if ((c = *cp) == '\n')
*cp = '\0';
if (strncmp (cp = buffer, "From ", sizeof("From ") - 1) == 0) {
fprintf (out, "=%02X", *cp++ & 0xff);
n = 3;
} else {
n = 0;
}
for (; *cp; cp++) {
if (n > CPERLIN - 3) {
fputs ("=\n", out);
n = 0;
}
switch (*cp) {
case ' ':
case '\t':
putc (*cp, out);
n++;
break;
default:
if (*cp < '!' || *cp > '~'
|| (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
goto three_print;
putc (*cp, out);
n++;
break;
case '=':
three_print:
fprintf (out, "=%02X", *cp & 0xff);
n += 3;
break;
}
}
if (c == '\n') {
if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
fputs ("=\n", out);
putc ('\n', out);
} else {
fputs ("=\n", out);
}
}
(*ct->c_ceclosefnx) (ct);
return OK;
}
/*
* Output a content using base64
*/
static int
writeBase64 (CT ct, FILE *out)
{
int fd, result;
char *file;
CE ce = ct->c_cefile;
file = NULL;
if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
return NOTOK;
result = writeBase64aux (ce->ce_fp, out);
(*ct->c_ceclosefnx) (ct);
return result;
}
int
writeBase64aux (FILE *in, FILE *out)
{
int cc, n;
char inbuf[3];
n = BPERLIN;
while ((cc = fread (inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
unsigned long bits;
char *bp;
char outbuf[4];
if (cc < sizeof(inbuf)) {
inbuf[2] = 0;
if (cc < sizeof(inbuf) - 1)
inbuf[1] = 0;
}
bits = (inbuf[0] & 0xff) << 16;
bits |= (inbuf[1] & 0xff) << 8;
bits |= inbuf[2] & 0xff;
for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
*--bp = nib2b64[bits & 0x3f];
if (cc < sizeof(inbuf)) {
outbuf[3] = '=';
if (cc < sizeof inbuf - 1)
outbuf[2] = '=';
}
fwrite (outbuf, sizeof(*outbuf), sizeof(outbuf), out);
if (cc < sizeof(inbuf)) {
putc ('\n', out);
return OK;
}
if (--n <= 0) {
n = BPERLIN;
putc ('\n', out);
}
}
if (n != BPERLIN)
putc ('\n', out);
return OK;
}
syntax highlighted by Code2HTML, v. 0.9.1