#include <h/mh.h>
#include <fcntl.h>
-#include <h/signals.h>
-#include <h/md5.h>
#include <errno.h>
-#include <signal.h>
#include <h/tws.h>
#include <h/mime.h>
#include <h/mhparse.h>
-#include <h/mhcachesbr.h>
#include <h/utils.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <sysexits.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#include <time.h>
static struct swit switches[] = {
-#define CHECKSW 0
- { "check", 0 },
-#define NCHECKSW 1
- { "nocheck", 0 },
-#define EBCDICSW 2
- { "ebcdicsafe", 0 },
-#define NEBCDICSW 3
- { "noebcdicsafe", 0 },
-#define HEADSW 4
- { "headers", 0 },
-#define NHEADSW 5
- { "noheaders", 0 },
-#define LISTSW 6
- { "list", 0 },
-#define NLISTSW 7
- { "nolist", 0 },
-#define SIZESW 8
- { "realsize", 0 },
-#define NSIZESW 9
- { "norealsize", 0 },
-#define RFC934SW 10
- { "rfc934mode", 0 },
-#define NRFC934SW 11
- { "norfc934mode", 0 },
-#define VERBSW 12
+#define VERBSW 0
{ "verbose", 0 },
-#define NVERBSW 13
- { "noverbose", 0 },
-#define RCACHESW 14
- { "rcache policy", 0 },
-#define WCACHESW 15
- { "wcache policy", 0 },
-#define CONTENTIDSW 16
- { "contentid", 0 },
-#define NCONTENTIDSW 17
- { "nocontentid", 0 },
-#define VERSIONSW 18
- { "version", 0 },
-#define HELPSW 19
+#define NVERBSW 1
+ { "noverbose", 2 },
+#define VERSIONSW 2
+ { "Version", 0 },
+#define HELPSW 3
{ "help", 0 },
-#define DEBUGSW 20
+#define DEBUGSW 4
{ "debug", -5 },
{ NULL, 0 }
};
+char *version=VERSION;
/*
** Directory to place tmp files. This must
int make_intermediates(char *);
void content_error(char *, CT, char *, ...);
-/* mhcachesbr.c */
-int find_cache(CT, int, int *, char *, char *, int);
-
-/* ftpsbr.c */
-int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
-
/* mhfree.c */
void free_content(CT);
void free_ctinfo(CT);
static int compose_content(CT);
static int scan_content(CT);
static int build_headers(CT);
-static char *calculate_digest(CT, int);
static CT build_mime(char *);
-
-/* mhcachesbr.c */
-extern int rcachesw;
-extern int wcachesw;
-extern char *cache_public;
-extern char *cache_private;
-
int debugsw = 0;
int verbosw = 0;
-int ebcdicsw = 0;
-int listsw = 0;
-int rfc934sw = 0;
-int contentidsw = 1;
-
/*
** Temporary files
*/
static char outfile[BUFSIZ];
static int unlink_outfile = 0;
-static void unlink_done(int) NORETURN;
+void unlink_done();
/* mhoutsbr.c */
int output_message(CT, char *);
int output_message_fp(CT, FILE *, char*);
-/* mhlistsbr.c */
-int list_all_messages(CT *, int, int, int, int);
-
/* mhmisc.c */
void set_endian(void);
int
main(int argc, char **argv)
{
- int sizesw = 1, headsw = 1;
- int *icachesw;
char *cp, buf[BUFSIZ];
char buffer[BUFSIZ], *compfile = NULL;
char **argp, **arguments;
- CT ct, cts[2];
+ CT ct;
FILE *fp = NULL;
FILE *fp_out = NULL;
- done = unlink_done;
+ if (atexit(unlink_done) != 0) {
+ adios(EX_OSERR, NULL, "atexit failed");
+ }
-#ifdef LOCALE
setlocale(LC_ALL, "");
-#endif
invo_name = mhbasename(argv[0]);
/* read user profile/context */
while ((cp = *argp++)) {
if (cp[0] == '-' && cp[1] == '\0') {
if (compfile)
- adios(NULL, "cannot specify both standard input and a file");
+ adios(EX_USAGE, NULL, "cannot specify both standard input and a file");
else
compfile = cp;
- listsw = 0; /* turn off -list if using std in/out */
verbosw = 0; /* turn off -verbose listings */
break;
}
switch (smatch(++cp, switches)) {
case AMBIGSW:
ambigsw(cp, switches);
- done(1);
+ exit(EX_USAGE);
case UNKWNSW:
- adios(NULL, "-%s unknown", cp);
+ adios(EX_USAGE, NULL, "-%s unknown", cp);
case HELPSW:
snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
print_help(buf, switches, 1);
- done(1);
+ exit(argc == 2 ? EX_OK : EX_USAGE);
case VERSIONSW:
print_version(invo_name);
- done(1);
-
- case RCACHESW:
- icachesw = &rcachesw;
- goto do_cache;
- case WCACHESW:
- icachesw = &wcachesw;
- do_cache: ;
- if (!(cp = *argp++) || *cp == '-')
- adios(NULL, "missing argument to %s",
- argp[-2]);
- switch (*icachesw = smatch(cp, caches)) {
- case AMBIGSW:
- ambigsw(cp, caches);
- done(1);
- case UNKWNSW:
- adios(NULL, "%s unknown", cp);
- default:
- break;
- }
- continue;
-
- case CHECKSW:
- checksw++;
- continue;
- case NCHECKSW:
- checksw = 0;
- continue;
-
- case EBCDICSW:
- ebcdicsw++;
- continue;
- case NEBCDICSW:
- ebcdicsw = 0;
- continue;
-
- case HEADSW:
- headsw++;
- continue;
- case NHEADSW:
- headsw = 0;
- continue;
-
- case LISTSW:
- listsw++;
- continue;
- case NLISTSW:
- listsw = 0;
- continue;
-
- case RFC934SW:
- rfc934sw++;
- continue;
- case NRFC934SW:
- rfc934sw = 0;
- continue;
-
- case SIZESW:
- sizesw++;
- continue;
- case NSIZESW:
- sizesw = 0;
- continue;
-
- case CONTENTIDSW:
- contentidsw = 1;
- continue;
- case NCONTENTIDSW:
- contentidsw = 0;
- continue;
+ exit(argc == 2 ? EX_OK : EX_USAGE);
case VERBSW:
verbosw++;
}
}
if (compfile)
- adios(NULL, "only one composition file allowed");
+ adios(EX_USAGE, NULL, "only one composition file allowed");
else
compfile = cp;
}
set_endian();
- if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0)
- listsw = 0;
-
/*
** Check if we've specified an additional profile
*/
fclose(fp);
}
- /* Check for public cache location */
- if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
- cache_public = NULL;
-
- /* Check for private cache location */
- if (!(cache_private = context_find(nmhprivcache)))
- cache_private = ".cache";
- cache_private = getcpy(toabsdir(cache_private));
-
/*
** Check for storage directory. If defined, we
** will store temporary files there. Else we
if ((cp = context_find(nmhstorage)) && *cp)
tmp = concat(cp, "/", invo_name, NULL);
else
- tmp = getcpy(toabsdir(invo_name));
+ tmp = mh_xstrdup(toabsdir(invo_name));
/* Check if we have a file to process */
if (!compfile)
- adios(NULL, "need to specify a %s composition file",
+ adios(EX_USAGE, NULL, "need to specify a %s composition file",
invo_name);
/*
/* build the content structures for MIME message */
ct = build_mime(infile);
- cts[0] = ct;
- cts[1] = NULL;
/* output MIME message to this temporary file */
strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
/* output the temp file to standard output */
if ((fp = fopen(outfile, "r")) == NULL)
- adios(outfile, "unable to open");
+ adios(EX_IOERR, outfile, "unable to open");
while (fgets(buffer, BUFSIZ, fp))
fputs(buffer, stdout);
fclose(fp);
unlink_outfile = 0;
free_content(ct);
- done(0);
+ exit(EX_OK);
}
/*
/* build the content structures for MIME message */
ct = build_mime(compfile);
- cts[0] = ct;
- cts[1] = NULL;
/* output MIME message to this temporary file */
strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
output_message_fp(ct, fp_out, outfile);
fclose(fp_out);
- /*
- ** List the message info
- */
- if (listsw)
- list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
-
/* Rename composition draft */
- snprintf(buffer, sizeof(buffer), "%s.orig", m_backup(compfile));
+ snprintf(buffer, sizeof(buffer), "%s.orig", compfile);
if (rename(compfile, buffer) == NOTOK) {
- adios(compfile, "unable to rename comp draft %s to", buffer);
+ adios(EX_IOERR, buffer, "unable to rename draft %s to", compfile);
}
/* Rename output file to take its place */
if (rename(outfile, compfile) == NOTOK) {
- advise(outfile, "unable to rename output %s to", compfile);
+ advise(compfile, "unable to rename output %s to", outfile);
rename(buffer, compfile);
- done(1);
+ exit(EX_IOERR);
}
unlink_outfile = 0;
free_content(ct);
- done(0);
- return 1;
+ return 0;
}
-static void
-unlink_done(int status)
+void
+unlink_done()
{
/*
** Check if we need to remove stray temporary files.
*/
- if (unlink_infile)
+ if (unlink_infile) {
unlink(infile);
- if (unlink_outfile)
+ }
+ if (unlink_outfile) {
unlink(outfile);
-
- exit(status);
+ }
}
/*
static CT
build_mime(char *infile)
{
- int compnum, state;
- char buf[BUFSIZ], name[NAMESZ];
+ enum state state;
+ struct field f = {{0}};
+ int compnum;
+ char buf[BUFSIZ];
char *cp, *np, *vp;
struct multipart *m;
struct part **pp;
CT ct;
FILE *in;
+ HF hp;
umask(~m_gmprot());
/* open the composition draft */
- if ((in = fopen(infile, "r")) == NULL)
- adios(infile, "unable to open for reading");
+ if ((in = fopen(infile, "r")) == NULL) {
+ adios(EX_IOERR, infile, "unable to open for reading");
+ }
/*
** Allocate space for primary (outside) content
*/
- if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
- adios(NULL, "out of memory");
+ ct = mh_xcalloc(1, sizeof(*ct));
/*
** Allocate structure for handling decoded content
** draft into the linked list of header fields for
** the new MIME message.
*/
- for (compnum = 1, state = FLD;;) {
- switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
- case FLD:
- case FLDPLUS:
- case FLDEOF:
+ for (compnum = 1, state = FLD2;;) {
+ switch (state = m_getfld2(state, &f, in)) {
+ case FLD2:
compnum++;
/* abort if draft has Mime-Version header field */
- if (!mh_strcasecmp(name, VRSN_FIELD))
- adios(NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
+ if (!mh_strcasecmp(f.name, VRSN_FIELD)) {
+ adios(EX_CONFIG, NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
+ }
/*
** abort if draft has Content-Transfer-Encoding
** header field
*/
- if (!mh_strcasecmp(name, ENCODING_FIELD))
- adios(NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
+ if (!mh_strcasecmp(f.name, ENCODING_FIELD)) {
+ adios(EX_CONFIG, NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
+ }
/* ignore any Content-Type fields in the header */
- if (!mh_strcasecmp(name, TYPE_FIELD)) {
- while (state == FLDPLUS)
- state = m_getfld(state, name, buf,
- sizeof(buf), in);
- goto finish_field;
+ if (!mh_strcasecmp(f.name, TYPE_FIELD)) {
+ continue;
}
- /* get copies of the buffers */
- np = getcpy(name);
- vp = getcpy(buf);
-
- /* if necessary, get rest of field */
- while (state == FLDPLUS) {
- state = m_getfld(state, name, buf,
- sizeof(buf), in);
- vp = add(buf, vp); /* add to prev value */
- }
+ /* add the header data to the list */
+ add_header(ct, mh_xstrdup(f.name), mh_xstrdup(f.value));
- /* Now add the header data to the list */
- add_header(ct, np, vp);
+ continue;
-finish_field:
- /* if this wasn't the last hdr field, then continue */
- if (state != FLDEOF)
- continue;
- /* else fall... */
+ case BODY2:
+ fseek(in, (long) (-strlen(f.value)), SEEK_CUR);
+ break;
- case FILEEOF:
- adios(NULL, "draft has empty body -- no directives!");
+ case FILEEOF2:
+ adios(EX_CONFIG, NULL, "draft has empty body -- no directives!");
/* NOTREACHED */
- case BODY:
- case BODYEOF:
- fseek(in, (long) (-strlen(buf)), SEEK_CUR);
- break;
-
- case LENERR:
- case FMTERR:
- adios(NULL, "message format error in component #%d",
+ case LENERR2:
+ case FMTERR2:
+ case IOERR2:
+ adios(EX_CONFIG, NULL, "message format error in component #%d",
compnum);
default:
- adios(NULL, "getfld() returned %d", state);
+ adios(EX_SOFTWARE, NULL, "getfld() returned %d", state);
}
break;
}
/*
+ ** Iterate through the list of headers and call the function to
+ ** MIME-ify them if required.
+ */
+ for (hp = ct->c_first_hf; hp != NULL; hp = hp->next) {
+ if (encode_rfc2047(hp->name, &hp->value, NULL)) {
+ adios(EX_DATAERR, NULL, "Unable to encode header \"%s\"", hp->name);
+ }
+ }
+
+ /*
** Now add the MIME-Version header field
** to the list of header fields.
*/
- np = getcpy(VRSN_FIELD);
+ np = mh_xstrdup(VRSN_FIELD);
vp = concat(" ", VRSN_VALUE, "\n", NULL);
add_header(ct, np, vp);
/*
- ** We initally assume we will find multiple contents in the
+ ** We initially assume we will find multiple contents in the
** draft. So create a multipart/mixed content to hold everything.
** We can remove this later, if it is not needed.
*/
- if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK)
- done(1);
+ if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK) {
+ exit(EX_DATAERR);
+ }
ct->c_type = CT_MULTIPART;
ct->c_subtype = MULTI_MIXED;
- ct->c_file = getcpy(infile);
+ ct->c_file = mh_xstrdup(infile);
- if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
- adios(NULL, "out of memory");
+ m = (struct multipart *) mh_xcalloc(1, sizeof(*m));
ct->c_ctparams = (void *) m;
pp = &m->mp_parts;
if (!p)
continue;
- if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
- adios(NULL, "out of memory");
+ part = mh_xcalloc(1, sizeof(*part));
*pp = part;
pp = &part->mp_next;
part->mp_part = p;
/* check if any contents were found */
if (!m->mp_parts)
- adios(NULL, "no content directives found");
+ adios(EX_OSERR, NULL, "no content directives found");
/*
** If only one content was found, then remove and
compose_content(ct);
if ((cp = strchr(prefix, 'a')) == NULL)
- adios(NULL, "internal error(4)");
+ adios(EX_SOFTWARE, NULL, "internal error(4)");
/*
** Scan the contents. Choose a transfer encoding, and
(*cp)++;
} else {
if (*++cp == 0)
- adios(NULL, "giving up trying to find a unique delimiter string");
+ adios(EX_SOFTWARE, NULL, "giving up trying to find a unique delimiter string");
else
(*cp)++;
}
{
CE ce;
- if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
- adios(NULL, "out of memory");
+ ce = mh_xcalloc(1, sizeof(*ce));
ct->c_cefile = ce;
ct->c_ceopenfnx = open7Bit; /* since unencoded */
static int
user_content(FILE *in, char *file, char *buf, CT *ctp)
{
- int extrnal, vrsn;
+ int vrsn;
unsigned char *cp;
char **ap;
char buffer[BUFSIZ];
struct multipart *m;
struct part **pp;
- struct stat st;
struct str2init *s2i;
CI ci;
CT ct;
}
/* allocate basic Content structure */
- if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
- adios(NULL, "out of memory");
+ ct = mh_xcalloc(1, sizeof(*ct));
*ctp = ct;
/* allocate basic structure for handling decoded content */
cp = m_mktemp2(NULL, invo_name, NULL, &out);
if (cp == NULL)
- adios("mhbuild", "unable to create temporary file");
+ adios(EX_CANTCREAT, "mhbuild", "unable to create temporary file");
/* use a temp file to collect the plain text lines */
- ce->ce_file = getcpy(cp);
+ ce->ce_file = mh_xstrdup(cp);
ce->ce_unlink = 1;
if (buf[0] == '#' && buf[1] == '<') {
again_descr:
ct->c_descr = add(buffer + i + 1, ct->c_descr);
if (!fgetstr(buffer, sizeof(buffer) - 1, in))
- adios(NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
+ adios(EX_DATAERR, NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
switch (buffer[0]) {
case ' ':
case '\t':
goto again_descr;
case '#':
- adios(NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
+ adios(EX_DATAERR, NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
/* NOTREACHED */
default:
again_dispo:
ct->c_dispo = add(buffer + i + 1, ct->c_dispo);
if (!fgetstr(buffer, sizeof(buffer) - 1, in))
- adios(NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
+ adios(EX_DATAERR, NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
switch (buffer[0]) {
case ' ':
case '\t':
goto again_dispo;
case '#':
- adios(NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
+ adios(EX_DATAERR, NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
/* NOTREACHED */
default:
}
}
- if (listsw)
- ct->c_end = ftell(out);
fclose(out);
/* parse content type */
if (get_ctinfo(content, ct, inlineD) == NOTOK)
- done(1);
+ exit(EX_DATAERR);
for (s2i = str2cts; s2i->si_key; s2i++)
if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
}
/* else fall... */
case CT_MULTIPART:
- adios(NULL, "it doesn't make sense to define an in-line %s content",
+ adios(EX_DATAERR, NULL, "it doesn't make sense to define an in-line %s content",
ct->c_type == CT_MESSAGE ? "message" :
"multipart");
/* NOTREACHED */
** must be some type of explicit directive.
*/
- /* check if directive is external-type */
- extrnal = (buf[1] == '@');
+ if (buf[1] == '@') {
+ adios(EX_DATAERR, NULL, "The #@ directive i.e. message/external-body "
+ "is not supported anymore.");
+ }
/* parse directive */
- if (get_ctinfo(buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
- done(1);
+ if (get_ctinfo(buf+1, ct, 1) == NOTOK)
+ exit(EX_DATAERR);
/* check directive against the list of MIME types */
for (s2i = str2cts; s2i->si_key; s2i++)
** Check if the directive specified a valid type.
** This will happen if it was one of the following forms:
**
- ** #type/subtype (or)
- ** #@type/subtype
+ ** #type/subtype
*/
if (s2i->si_key) {
if (!ci->ci_subtype)
- adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
+ adios(EX_DATAERR, NULL, "missing subtype in \"#%s\"", ci->ci_type);
switch (ct->c_type = s2i->si_val) {
case CT_MULTIPART:
- adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
+ adios(EX_DATAERR, NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
/* NOTREACHED */
case CT_MESSAGE:
- if (!mh_strcasecmp(ci->ci_subtype, "partial"))
- adios(NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
- if (!mh_strcasecmp(ci->ci_subtype, "external-body"))
- adios(NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
+ if (!mh_strcasecmp(ci->ci_subtype, "partial") ||
+ !mh_strcasecmp(ci->ci_subtype,
+ "external-body")) {
+ adios(EX_DATAERR, NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
+ }
use_forw:
- adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
- /* NOTREACHED */
+ admonish(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
+ /* FALL */
default:
if ((ct->c_ctinitfnx = s2i->si_init))
break;
}
- /*
- ** #@type/subtype (external types directive)
- */
- if (extrnal) {
- struct exbody *e;
- CT p;
-
- if (!ci->ci_magic)
- adios(NULL, "need external information for \"#@%s/%s\"", ci->ci_type, ci->ci_subtype);
- p = ct;
-
- snprintf(buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
- free(ci->ci_magic);
- ci->ci_magic = NULL;
-
- /*
- ** Since we are using the current Content structure to
- ** hold information about the type of the external
- ** reference, we need to create another Content
- ** structure for the message/external-body to wrap
- ** it in.
- */
- if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
- adios(NULL, "out of memory");
- *ctp = ct;
- ci = &ct->c_ctinfo;
- if (get_ctinfo(buffer, ct, 0) == NOTOK)
- done(1);
- ct->c_type = CT_MESSAGE;
- ct->c_subtype = MESSAGE_EXTERNAL;
-
- if ((e = (struct exbody *)
- calloc(1, sizeof(*e))) == NULL)
- adios(NULL, "out of memory");
- ct->c_ctparams = (void *) e;
-
- e->eb_parent = ct;
- e->eb_content = p;
- p->c_ctexbody = e;
-
- if (params_external(ct, 1) == NOTOK)
- done(1);
-
- return OK;
- }
-
/* Handle [file] argument */
if (ci->ci_magic) {
/* check if specifies command to execute */
for (cp = ci->ci_magic + 1; isspace(*cp); cp++)
continue;
if (!*cp)
- adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
- cp = getcpy(cp);
- free(ci->ci_magic);
+ adios(EX_DATAERR, NULL, "empty pipe command for #%s directive", ci->ci_type);
+ cp = mh_xstrdup(cp);
+ mh_free0(&(ci->ci_magic));
ci->ci_magic = cp;
} else {
/* record filename of decoded contents */
ce->ce_file = ci->ci_magic;
if (access(ce->ce_file, R_OK) == NOTOK)
- adios("reading", "unable to access %s for", ce->ce_file);
- if (listsw && stat(ce->ce_file, &st) != NOTOK)
- ct->c_end = (long) st.st_size;
+ adios(EX_IOERR, "reading", "unable to access %s for", ce->ce_file);
ci->ci_magic = NULL;
}
return OK;
if ((cp = context_find(buffer)) == NULL ||
*cp == '\0') {
content_error(NULL, ct, "don't know how to compose content");
- done(1);
+ exit(EX_CONFIG);
}
}
- ci->ci_magic = getcpy(cp);
+ ci->ci_magic = mh_xstrdup(cp);
return OK;
}
- if (extrnal)
- adios(NULL, "external definition not allowed for \"#%s\"",
- ci->ci_type);
-
/*
** Message directive
** #forw [+folder] [msgs]
cp = *ap;
if (*cp == '+' || *cp == '@') {
if (folder)
- adios(NULL, "only one folder per #forw directive");
+ adios(EX_USAGE, NULL, "only one folder per #forw directive");
else
- folder = getcpy(expandfol(cp));
+ folder = mh_xstrdup(expandfol(cp));
}
}
/* else, use the current folder */
if (!folder)
- folder = getcpy(getcurfol());
+ folder = mh_xstrdup(getcurfol());
if (!(mp = folder_read(folder)))
- adios(NULL, "unable to read folder %s", folder);
+ adios(EX_IOERR, NULL, "unable to read folder %s", folder);
for (ap = arguments; *ap; ap++) {
cp = *ap;
if (*cp != '+' && *cp != '@')
if (!m_convert(mp, cp))
- done(1);
+ exit(EX_USAGE);
}
- free(folder);
+ mh_free0(&folder);
free_ctinfo(ct);
/*
if (mp->numsel > 1) {
/* we are forwarding multiple messages */
if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
- done(1);
+ exit(EX_DATAERR);
ct->c_type = CT_MULTIPART;
ct->c_subtype = MULTI_DIGEST;
- if ((m = (struct multipart *)
- calloc(1, sizeof(*m))) == NULL)
- adios(NULL, "out of memory");
+ m = mh_xcalloc(1, sizeof(*m));
ct->c_ctparams = (void *) m;
pp = &m->mp_parts;
CT p;
CE pe;
- if ((p = (CT) calloc(1, sizeof(*p)))
- == NULL)
- adios(NULL, "out of memory");
+ p = mh_xcalloc(1, sizeof(*p));
init_decoded_content(p);
pe = p->c_cefile;
if (get_ctinfo("message/rfc822", p, 0)
== NOTOK)
- done(1);
+ exit(EX_DATAERR);
p->c_type = CT_MESSAGE;
p->c_subtype = MESSAGE_RFC822;
snprintf(buffer, sizeof(buffer),
"%s/%d", mp->foldpath,
msgnum);
- pe->ce_file = getcpy(buffer);
- if (listsw && stat(pe->ce_file, &st)
- != NOTOK)
- p->c_end = (long) st.st_size;
+ pe->ce_file = mh_xstrdup(buffer);
- if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
- adios(NULL, "out of memory");
+ part = mh_xcalloc(1, sizeof(*part));
*pp = part;
pp = &part->mp_next;
part->mp_part = p;
} else {
/* we are forwarding one message */
if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
- done(1);
+ exit(EX_DATAERR);
ct->c_type = CT_MESSAGE;
ct->c_subtype = MESSAGE_RFC822;
msgnum = mp->lowsel;
snprintf(buffer, sizeof(buffer), "%s/%d",
mp->foldpath, msgnum);
- ce->ce_file = getcpy(buffer);
- if (listsw && stat(ce->ce_file, &st) != NOTOK)
- ct->c_end = (long) st.st_size;
+ ce->ce_file = mh_xstrdup(buffer);
}
folder_free(mp); /* free folder/message structure */
free_ctinfo(ct);
snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
if (get_ctinfo(buffer, ct, 0) == NOTOK)
- done(1);
+ exit(EX_DATAERR);
ct->c_type = CT_MULTIPART;
ct->c_subtype = vrsn;
- if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
- adios(NULL, "out of memory");
+ m = mh_xcalloc(1, sizeof(*m));
ct->c_ctparams = (void *) m;
pp = &m->mp_parts;
if (user_content(in, file, buffer, &p) == DONE) {
if (!m->mp_parts)
- adios(NULL, "empty \"#begin ... #end\" sequence");
+ adios(EX_DATAERR, NULL, "empty \"#begin ... #end\" sequence");
return OK;
}
if (!p)
continue;
- if ((part = (struct part *)
- calloc(1, sizeof(*part))) == NULL)
- adios(NULL, "out of memory");
+ part = mh_xcalloc(1, sizeof(*part));
*pp = part;
pp = &part->mp_next;
part->mp_part = p;
/*
** Unknown directive
*/
- adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
+ adios(EX_DATAERR, NULL, "unknown directive \"#%s\"", ci->ci_type);
return NOTOK; /* NOT REACHED */
}
snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
(int) getpid(), (long) clock, LocalName());
partno = 0;
- msgfmt = getcpy(msgid);
+ msgfmt = mh_xstrdup(msgid);
}
snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
- ct->c_id = getcpy(msgid);
+ ct->c_id = mh_xstrdup(msgid);
}
-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
-};
-
-
/*
** Fill out, or expand the various contents in the composition
** draft. Read-in any necessary files. Parse and execute any
CT p = part->mp_part;
sprintf(pp, "%d", partnum);
- p->c_partno = getcpy(partnam);
+ p->c_partno = mh_xstrdup(partnam);
if (compose_content(p) == NOTOK)
return NOTOK;
}
-
- /*
- ** If the -rfc934mode switch is given, then check all
- ** the subparts of a multipart/digest. If they are all
- ** message/rfc822, then mark this content and all
- ** subparts with the rfc934 compatibility mode flag.
- */
- if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
- int is934 = 1;
-
- for (part = m->mp_parts; part; part = part->mp_next) {
- CT p = part->mp_part;
-
- if (p->c_subtype != MESSAGE_RFC822) {
- is934 = 0;
- break;
- }
- }
- ct->c_rfc934 = is934;
- for (part = m->mp_parts; part; part = part->mp_next) {
- CT p = part->mp_part;
-
- if ((p->c_rfc934 = is934))
- p->c_end++;
- }
- }
-
- if (listsw) {
- ct->c_end = (partnum = strlen(prefix) + 2) + 2;
- if (ct->c_rfc934)
- ct->c_end += 1;
-
- for (part = m->mp_parts; part; part = part->mp_next)
- ct->c_end += part->mp_part->c_end + partnum;
- }
}
break;
char *tfile = NULL;
if (!(cp = ci->ci_magic))
- adios(NULL, "internal error(5)");
+ adios(EX_SOFTWARE, NULL, "internal error(5)");
tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
if (tfile == NULL) {
- adios("mhbuild", "unable to create temporary file");
+ adios(EX_CANTCREAT, "mhbuild", "unable to create temporary file");
}
- ce->ce_file = getcpy(tfile);
+ ce->ce_file = mh_xstrdup(tfile);
ce->ce_unlink = 1;
xstdout = 0;
vec[3] = NULL;
if ((out = fopen(ce->ce_file, "w")) == NULL)
- adios(ce->ce_file, "unable to open for writing");
+ adios(EX_IOERR, ce->ce_file, "unable to open for writing");
switch (child_id = fork()) {
case NOTOK:
- adios("fork", "unable to fork");
+ adios(EX_OSERR, "fork", "unable to fork");
/* NOTREACHED */
case OK:
execvp("/bin/sh", vec);
fprintf(stderr, "unable to exec ");
perror("/bin/sh");
- _exit(-1);
+ _exit(EX_OSERR);
/* NOTREACHED */
default:
fclose(out);
if (pidXwait(child_id, NULL))
- done(1);
+ exit(EX_SOFTWARE);
break;
}
}
-
- /* Check size of file */
- if (listsw && ct->c_end == 0L) {
- struct stat st;
-
- if (stat(ce->ce_file, &st) != NOTOK)
- ct->c_end = (long) st.st_size;
- }
break;
}
scan_content(CT ct)
{
int len;
- int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
- int checklinelen = 0, linelen = 0; /* check for long lines */
- int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
- int checklinespace = 0, linespace = 0; /* check if any line ends with space */
- int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
+ int check8bit = 0, contains8bit = 0;
+ int checklinelen = 0, linelen = 0;
+ int checkboundary = 0, boundaryclash = 0;
+ int checklinespace = 0, linespace = 0; /* trailing whitespace */
unsigned char *cp = NULL, buffer[BUFSIZ];
struct text *t = NULL;
FILE *in = NULL;
case CT_TEXT:
check8bit = 1;
checkboundary = 1;
- if (ct->c_subtype == TEXT_PLAIN) {
- checkebcdic = 0;
- checklinelen = 0;
- checklinespace = 0;
- } else {
- checkebcdic = ebcdicsw;
- checklinelen = 1;
- checklinespace = 1;
- }
- break;
-
- case CT_APPLICATION:
- check8bit = 1;
- checkebcdic = ebcdicsw;
checklinelen = 1;
checklinespace = 1;
- checkboundary = 1;
break;
case CT_MESSAGE:
check8bit = 0;
- checkebcdic = 0;
checklinelen = 0;
checklinespace = 0;
-
- /* don't check anything for message/external */
- if (ct->c_subtype == MESSAGE_EXTERNAL)
- checkboundary = 0;
- else
- checkboundary = 1;
+ checkboundary = 1;
break;
+ case CT_APPLICATION:
case CT_AUDIO:
case CT_IMAGE:
case CT_VIDEO:
- /*
- ** Don't check anything for these types,
- ** since we are forcing use of base64.
- */
check8bit = 0;
- checkebcdic = 0;
checklinelen = 0;
checklinespace = 0;
checkboundary = 0;
*/
if (check8bit || checklinelen || checklinespace || checkboundary) {
if ((in = fopen(ce->ce_file, "r")) == NULL)
- adios(ce->ce_file, "unable to open for reading");
+ adios(EX_IOERR, ce->ce_file, "unable to open for reading");
len = strlen(prefix);
while (fgets(buffer, sizeof(buffer) - 1, in)) {
- /*
- ** Check for 8bit data.
- */
if (check8bit) {
for (cp = buffer; *cp; cp++) {
if (!isascii(*cp)) {
/* no need to keep checking */
check8bit = 0;
}
- /*
- ** Check if character is ebcdic-safe.
- ** We only check this if also checking
- ** for 8bit data.
- */
- if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
- ebcdicunsafe = 1;
- /* no need to keep checking */
- checkebcdic = 0;
- }
}
}
- /*
- ** Check line length.
- */
if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
linelen = 1;
checklinelen = 0; /* no need to keep checking */
}
- /*
- ** Check if line ends with a space.
- */
if (checklinespace &&
(cp = buffer + strlen(buffer) - 2) >
buffer && isspace(*cp)) {
NULL);
} else {
t->tx_charset = CHARSET_USASCII;
- *ap = getcpy("charset=us-ascii");
+ *ap = mh_xstrdup("charset=us-ascii");
}
cp = strchr(*ap++, '=');
*ep = cp;
}
- if (contains8bit || ebcdicunsafe || linelen || linespace ||
- checksw)
+ if (contains8bit || linelen || linespace)
ct->c_encoding = CE_QUOTED;
else
ct->c_encoding = CE_7BIT;
break;
- case CT_APPLICATION:
- /* For application type, use base64, except when postscript */
- if (contains8bit || ebcdicunsafe || linelen || linespace ||
- checksw)
- ct->c_encoding = (ct->c_subtype ==
- APPLICATION_POSTSCRIPT) ?
- CE_QUOTED : CE_BASE64;
- else
- ct->c_encoding = CE_7BIT;
- break;
-
case CT_MESSAGE:
ct->c_encoding = CE_7BIT;
break;
+ case CT_APPLICATION:
case CT_AUDIO:
case CT_IMAGE:
case CT_VIDEO:
- /* For audio, image, and video contents, just use base64 */
+ /*
+ ** Forcing use of base64, because these types likely
+ ** contain binary data and NUL bytes. Don't care about
+ ** files that would be clean.
+ */
ct->c_encoding = CE_BASE64;
break;
}
static int
build_headers(CT ct)
{
- int cc, mailbody, len;
+ int cc, len;
char **ap, **ep;
char *np, *vp, buffer[BUFSIZ];
CI ci = &ct->c_ctinfo;
ep = ci->ci_values;
snprintf(buffer, sizeof(buffer), "boundary=%s%d",
prefix, level++);
- cp = strchr(*ap++ = getcpy(buffer), '=');
+ cp = strchr(*ap++ = mh_xstrdup(buffer), '=');
*ap = NULL;
*cp++ = '\0';
*ep = cp;
}
/*
- ** Skip the output of Content-Type, parameters, content
- ** description and disposition, and Content-ID if the
- ** content is of type "message" and the rfc934 compatibility
- ** flag is set (which means we are inside multipart/digest
- ** and the switch -rfc934mode was given).
- */
- if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
- goto skip_headers;
-
- /*
** output the content type and subtype
*/
- np = getcpy(TYPE_FIELD);
+ np = mh_xstrdup(TYPE_FIELD);
vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
/* keep track of length of line */
len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
strlen(ci->ci_subtype) + 3;
- mailbody = ct->c_type == CT_MESSAGE &&
- ct->c_subtype == MESSAGE_EXTERNAL &&
- ((struct exbody *) ct->c_ctparams)->eb_body;
-
/*
** Append the attribute/value pairs to
** the end of the Content-Type line.
*/
for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (mailbody && !mh_strcasecmp(*ap, "body"))
- continue;
-
vp = add(";", vp);
len++;
add_header(ct, np, vp);
/*
- ** output the Content-ID, unless disabled by -nocontentid
+ ** output the Content-ID
*/
- if (contentidsw && ct->c_id) {
- np = getcpy(ID_FIELD);
+ if (ct->c_id) {
+ np = mh_xstrdup(ID_FIELD);
vp = concat(" ", ct->c_id, NULL);
add_header(ct, np, vp);
}
** output the Content-Description
*/
if (ct->c_descr) {
- np = getcpy(DESCR_FIELD);
+ np = mh_xstrdup(DESCR_FIELD);
vp = concat(" ", ct->c_descr, NULL);
+ if (encode_rfc2047(DESCR_FIELD, &vp, NULL)) {
+ adios(EX_DATAERR, NULL, "Unable to encode %s header", DESCR_FIELD);
+ }
add_header(ct, np, vp);
}
** output the Content-Disposition
*/
if (ct->c_dispo) {
- np = getcpy(DISPO_FIELD);
+ np = mh_xstrdup(DISPO_FIELD);
vp = concat(" ", ct->c_dispo, NULL);
add_header(ct, np, vp);
}
-skip_headers:
- /*
- ** 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;
-
- /*
- ** output the Content-MD5
- */
- if (checksw) {
- np = getcpy(MD5_FIELD);
- vp = calculate_digest(ct, (ct->c_encoding == CE_QUOTED) ?
- 1 : 0);
- add_header(ct, np, vp);
- }
-
/*
** output the Content-Transfer-Encoding
*/
case CE_8BIT:
if (ct->c_type == CT_MESSAGE)
- adios(NULL, "internal error, invalid encoding");
+ adios(EX_DATAERR, NULL, "internal error, invalid encoding");
- np = getcpy(ENCODING_FIELD);
+ np = mh_xstrdup(ENCODING_FIELD);
vp = concat(" ", "8bit", "\n", NULL);
add_header(ct, np, vp);
break;
case CE_QUOTED:
if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
- adios(NULL, "internal error, invalid encoding");
+ adios(EX_DATAERR, NULL, "internal error, invalid encoding");
- np = getcpy(ENCODING_FIELD);
+ np = mh_xstrdup(ENCODING_FIELD);
vp = concat(" ", "quoted-printable", "\n", NULL);
add_header(ct, np, vp);
break;
case CE_BASE64:
if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
- adios(NULL, "internal error, invalid encoding");
+ adios(EX_DATAERR, NULL, "internal error, invalid encoding");
- np = getcpy(ENCODING_FIELD);
+ np = mh_xstrdup(ENCODING_FIELD);
vp = concat(" ", "base64", "\n", NULL);
add_header(ct, np, vp);
break;
case CE_BINARY:
if (ct->c_type == CT_MESSAGE)
- adios(NULL, "internal error, invalid encoding");
+ adios(EX_DATAERR, NULL, "internal error, invalid encoding");
- np = getcpy(ENCODING_FIELD);
+ np = mh_xstrdup(ENCODING_FIELD);
vp = concat(" ", "binary", "\n", NULL);
add_header(ct, np, vp);
break;
default:
- adios(NULL, "unknown transfer encoding in content");
+ adios(EX_DATAERR, NULL, "unknown transfer encoding in content");
break;
}
}
break;
- case CT_MESSAGE:
- if (ct->c_subtype == MESSAGE_EXTERNAL) {
- struct exbody *e;
-
- e = (struct exbody *) ct->c_ctparams;
- build_headers(e->eb_content);
- }
- break;
-
default:
/* Nothing to do */
break;
return OK;
}
-
-
-static char nib2b64[0x40+1] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static char *
-calculate_digest(CT ct, int asciiP)
-{
- int cc;
- char buffer[BUFSIZ], *vp, *op;
- unsigned char *dp;
- unsigned char digest[16];
- unsigned char outbuf[25];
- FILE *in;
- MD5_CTX mdContext;
- CE ce = ct->c_cefile;
- char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
-
- /* open content */
- if ((in = fopen(infilename, "r")) == NULL)
- adios (infilename, "unable to open for reading");
-
- /* Initialize md5 context */
- MD5Init(&mdContext);
-
- /* calculate md5 message digest */
- if (asciiP) {
- while (fgets(buffer, sizeof(buffer) - 1, in)) {
- char c, *cp;
-
- cp = buffer + strlen(buffer) - 1;
- if ((c = *cp) == '\n')
- *cp = '\0';
-
- MD5Update(&mdContext, (unsigned char *) buffer,
- (unsigned int) strlen(buffer));
-
- if (c == '\n')
- MD5Update(&mdContext, (unsigned char *) "\r\n",
- 2);
- }
- } else {
- while ((cc = fread(buffer, sizeof(*buffer), sizeof(buffer),
- in)) > 0)
- MD5Update(&mdContext, (unsigned char *) buffer,
- (unsigned int) cc);
- }
-
- /* md5 finalization. Write digest and zero md5 context */
- MD5Final(digest, &mdContext);
-
- /* close content */
- fclose(in);
-
- /* print debugging info */
- if (debugsw) {
- unsigned char *ep;
-
- fprintf(stderr, "MD5 digest=");
- for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
- dp < ep; dp++)
- fprintf(stderr, "%02x", *dp & 0xff);
- fprintf(stderr, "\n");
- }
-
- /* encode the digest using base64 */
- for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
- cc > 0; cc -= 3, op += 4) {
- unsigned long bits;
- char *bp;
-
- bits = (*dp++ & 0xff) << 16;
- if (cc > 1) {
- bits |= (*dp++ & 0xff) << 8;
- if (cc > 2)
- bits |= *dp++ & 0xff;
- }
-
- for (bp = op + 4; bp > op; bits >>= 6)
- *--bp = nib2b64[bits & 0x3f];
- if (cc < 3) {
- *(op + 3) = '=';
- if (cc < 2)
- *(op + 2) = '=';
- }
- }
-
- /* null terminate string */
- outbuf[24] = '\0';
-
- /* now make copy and return string */
- vp = concat(" ", outbuf, "\n", NULL);
- return vp;
-}