/*
* mhparse.c -- routines to parse the contents of MIME messages
*
- * $Id$
- *
* 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/tws.h>
#include <h/mime.h>
#include <h/mhparse.h>
+#include <h/utils.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
-extern int errno;
extern int debugsw;
extern int endian; /* mhmisc.c */
char *tmp;
/*
- * Structure for mapping types to their internal flags
- */
-struct k2v {
- char *kv_key;
- int kv_value;
-};
-
-/*
* Structures for TEXT messages
*/
-static struct k2v SubText[] = {
+struct k2v SubText[] = {
{ "plain", TEXT_PLAIN },
{ "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
{ "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
{ NULL, TEXT_UNKNOWN } /* this one must be last! */
};
-static struct k2v Charset[] = {
+struct k2v Charset[] = {
{ "us-ascii", CHARSET_USASCII },
{ "iso-8859-1", CHARSET_LATIN },
{ NULL, CHARSET_UNKNOWN } /* this one must be last! */
/*
* Structures for MULTIPART messages
*/
-static struct k2v SubMultiPart[] = {
+struct k2v SubMultiPart[] = {
{ "mixed", MULTI_MIXED },
{ "alternative", MULTI_ALTERNATE },
{ "digest", MULTI_DIGEST },
/*
* Structures for MESSAGE messages
*/
-static struct k2v SubMessage[] = {
+struct k2v SubMessage[] = {
{ "rfc822", MESSAGE_RFC822 },
{ "partial", MESSAGE_PARTIAL },
{ "external-body", MESSAGE_EXTERNAL },
/*
* Structure for APPLICATION messages
*/
-static struct k2v SubApplication[] = {
+struct k2v SubApplication[] = {
{ "octet-stream", APPLICATION_OCTETS },
{ "postscript", APPLICATION_POSTSCRIPT },
{ NULL, APPLICATION_UNKNOWN } /* this one must be last! */
void free_encoding (CT, int);
/*
- * prototypes
- */
-int pidcheck (int);
-CT parse_mime (char *);
-
-/*
* static prototypes
*/
static CT get_content (FILE *, char *, int);
-static int add_header (CT, char *, char *);
-static int get_ctinfo (char *, CT);
-static int get_comment (CT, char **, int);
+static int get_comment (CT, unsigned char **, int);
+
static int InitGeneric (CT);
static int InitText (CT);
static int InitMultiPart (CT);
static void reverse_parts (CT);
static int InitMessage (CT);
-static int params_external (CT, int);
static int InitApplication (CT);
static int init_encoding (CT, OpenCEFunc);
-static void close_encoding (CT);
static unsigned long size_encoding (CT);
static int InitBase64 (CT);
static int openBase64 (CT, char **);
static int InitQuoted (CT);
static int openQuoted (CT, char **);
static int Init7Bit (CT);
-static int open7Bit (CT, char **);
static int openExternal (CT, CT, CE, char **, int *);
static int InitFile (CT);
static int openFile (CT, char **);
static int openMail (CT, char **);
static int readDigest (CT, char *);
-/*
- * Structures for mapping (content) types to
- * the functions to handle them.
- */
-struct str2init {
- char *si_key;
- int si_val;
- InitFunc si_init;
-};
-
-static struct str2init str2cts[] = {
+struct str2init str2cts[] = {
{ "application", CT_APPLICATION, InitApplication },
{ "audio", CT_AUDIO, InitGeneric },
{ "image", CT_IMAGE, InitGeneric },
{ NULL, CT_UNKNOWN, NULL },
};
-static struct str2init str2ces[] = {
+struct str2init str2ces[] = {
{ "base64", CE_BASE64, InitBase64 },
{ "quoted-printable", CE_QUOTED, InitQuoted },
{ "8bit", CE_8BIT, Init7Bit },
*
* si_key is 1 if access method is anonymous.
*/
-static struct str2init str2methods[] = {
+struct str2init str2methods[] = {
{ "afs", 1, InitFile },
{ "anon-ftp", 1, InitFTP },
{ "ftp", 0, InitFTP },
fflush (stdout);
fflush (stderr);
- return done (1);
+ done (1);
+ return 1;
}
* Check if file is actually standard input
*/
if ((is_stdin = !(strcmp (file, "-")))) {
- file = add (m_tmpfil (invo_name), NULL);
- if ((fp = fopen (file, "w+")) == NULL) {
- advise (file, "unable to fopen for writing and reading");
- return NULL;
- }
+ char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
+ if (tfile == NULL) {
+ advise("mhparse", "unable to create temporary file");
+ return NULL;
+ }
+ file = add (tfile, NULL);
chmod (file, 0600);
+
while (fgets (buffer, sizeof(buffer), stdin))
fputs (buffer, fp);
fflush (fp);
if (!(ct = get_content (fp, file, 1))) {
if (is_stdin)
unlink (file);
- fclose (fp);
advise (NULL, "unable to decode %s", file);
return NULL;
}
* toplevel = 0 # we are inside message type or multipart type
* # other than multipart/digest
* toplevel = -1 # we are inside multipart/digest
+ * NB: on failure we will fclose(in)!
*/
static CT
hp = ct->c_first_hf; /* start at first header field */
while (hp) {
/* Get MIME-Version field */
- if (!strcasecmp (hp->name, VRSN_FIELD)) {
+ if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
int ucmp;
- char c, *cp, *dp;
+ char c;
+ unsigned char *cp, *dp;
if (ct->c_vrsn) {
advise (NULL, "message %s has multiple %s: fields",
continue;
c = *dp;
*dp = '\0';
- ucmp = !strcasecmp (cp, VRSN_VALUE);
+ ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
*dp = c;
if (!ucmp) {
admonish (NULL, "message %s has unknown value for %s: field (%s)",
ct->c_file, VRSN_FIELD, cp);
}
}
- else if (!strcasecmp (hp->name, TYPE_FIELD)) {
+ else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
/* Get Content-Type field */
struct str2init *s2i;
CI ci = &ct->c_ctinfo;
}
/* Parse the Content-Type field */
- if (get_ctinfo (hp->value, ct) == NOTOK)
+ if (get_ctinfo (hp->value, ct, 0) == NOTOK)
goto out;
/*
* flag for this content type.
*/
for (s2i = str2cts; s2i->si_key; s2i++)
- if (!strcasecmp (ci->ci_type, s2i->si_key))
+ if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
break;
if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
s2i++;
ct->c_type = s2i->si_val;
ct->c_ctinitfnx = s2i->si_init;
}
- else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
+ else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
/* Get Content-Transfer-Encoding field */
- char c, *cp, *dp;
+ char c;
+ unsigned char *cp, *dp;
struct str2init *s2i;
/*
* for this transfer encoding.
*/
for (s2i = str2ces; s2i->si_key; s2i++)
- if (!strcasecmp (cp, s2i->si_key))
+ if (!mh_strcasecmp (cp, s2i->si_key))
break;
if (!s2i->si_key && !uprf (cp, "X-"))
s2i++;
if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
goto out;
}
- else if (!strcasecmp (hp->name, MD5_FIELD)) {
+ else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
/* Get Content-MD5 field */
- char *cp, *dp, *ep;
+ unsigned char *cp, *dp;
+ char *ep;
if (!checksw)
goto next_header;
free (ep);
ct->c_digested++;
}
- else if (!strcasecmp (hp->name, ID_FIELD)) {
+ else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
/* Get Content-ID field */
ct->c_id = add (hp->value, ct->c_id);
}
- else if (!strcasecmp (hp->name, DESCR_FIELD)) {
+ else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
/* Get Content-Description field */
ct->c_descr = add (hp->value, ct->c_descr);
}
+ else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
+ /* Get Content-Disposition field */
+ ct->c_dispo = add (hp->value, ct->c_dispo);
+ }
next_header:
hp = hp->next; /* next header field */
* so default type is message/rfc822
*/
if (toplevel < 0) {
- if (get_ctinfo ("message/rfc822", ct) == NOTOK)
+ if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
goto out;
ct->c_type = CT_MESSAGE;
ct->c_ctinitfnx = InitMessage;
/*
* Else default type is text/plain
*/
- if (get_ctinfo ("text/plain", ct) == NOTOK)
+ if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
goto out;
ct->c_type = CT_TEXT;
ct->c_ctinitfnx = InitText;
* small routine to add header field to list
*/
-static int
+int
add_header (CT ct, char *name, char *value)
{
HF hp;
/* allocate header field structure */
- if (!(hp = malloc (sizeof(*hp))))
- adios (NULL, "out of memory");
+ hp = mh_xmalloc (sizeof(*hp));
/* link data into header structure */
hp->name = name;
}
+/* Make sure that buf contains at least one appearance of name,
+ followed by =. If not, insert both name and value, just after
+ first semicolon, if any. Note that name should not contain a
+ trailing =. And quotes will be added around the value. Typical
+ usage: make sure that a Content-Disposition header contains
+ filename="foo". If it doesn't and value does, use value from
+ that. */
+static char *
+incl_name_value (unsigned char *buf, char *name, char *value) {
+ char *newbuf = buf;
+
+ /* Assume that name is non-null. */
+ if (buf && value) {
+ char *name_plus_equal = concat (name, "=", NULL);
+
+ if (! strstr (buf, name_plus_equal)) {
+ char *insertion;
+ unsigned char *cp;
+ char *prefix, *suffix;
+
+ /* Trim trailing space, esp. newline. */
+ for (cp = &buf[strlen (buf) - 1];
+ cp >= buf && isspace (*cp);
+ --cp) {
+ *cp = '\0';
+ }
+
+ insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
+
+ /* Insert at first semicolon, if any. If none, append to
+ end. */
+ prefix = add (buf, NULL);
+ if ((cp = strchr (prefix, ';'))) {
+ suffix = concat (cp, NULL);
+ *cp = '\0';
+ newbuf = concat (prefix, insertion, suffix, "\n", NULL);
+ free (suffix);
+ } else {
+ /* Append to end. */
+ newbuf = concat (buf, insertion, "\n", NULL);
+ }
+
+ free (prefix);
+ free (insertion);
+ free (buf);
+ }
+
+ free (name_plus_equal);
+ }
+
+ return newbuf;
+}
+
+/* Extract just name_suffix="foo", if any, from value. If there isn't
+ one, return the entire value. Note that, for example, a name_suffix
+ of name will match filename="foo", and return foo. */
+static char *
+extract_name_value (char *name_suffix, char *value) {
+ char *extracted_name_value = value;
+ char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
+ char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
+ char *cp;
+
+ free (name_suffix_plus_quote);
+ if (name_suffix_equals) {
+ char *name_suffix_begin;
+
+ /* Find first \". */
+ for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
+ name_suffix_begin = ++cp;
+ /* Find second \". */
+ for (; *cp != '"'; ++cp) /* empty */;
+
+ extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
+ memcpy (extracted_name_value,
+ name_suffix_begin,
+ cp - name_suffix_begin);
+ extracted_name_value[cp - name_suffix_begin] = '\0';
+ }
+
+ return extracted_name_value;
+}
+
/*
- * Parse Content-Type line and fill in the
- * information of the CTinfo structure.
+ * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
+ * directives. Fills in the information of the CTinfo structure.
*/
-
-static int
-get_ctinfo (char *cp, CT ct)
+int
+get_ctinfo (unsigned char *cp, CT ct, int magic)
{
int i;
- char *dp, **ap, **ep;
+ unsigned char *dp;
+ char **ap, **ep;
char c;
CI ci;
return NOTOK;
if (*cp != '/') {
- ci->ci_subtype = add ("", NULL);
+ if (!magic)
+ ci->ci_subtype = add ("", NULL);
goto magic_skip;
}
*/
ep = (ap = ci->ci_attrs) + NPARMS;
while (*cp == ';') {
- char *vp, *up;
+ char *vp;
+ unsigned char *up;
if (ap >= ep) {
advise (NULL,
}
/*
+ * Get any <Content-Id> given in buffer
+ */
+ if (magic && *cp == '<') {
+ if (ct->c_id) {
+ free (ct->c_id);
+ ct->c_id = NULL;
+ }
+ if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
+ advise (NULL, "invalid ID in message %s", ct->c_file);
+ return NOTOK;
+ }
+ c = *dp;
+ *dp = '\0';
+ if (*ct->c_id)
+ ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
+ else
+ ct->c_id = NULL;
+ *dp++ = c;
+ cp = dp;
+
+ while (isspace (*cp))
+ cp++;
+ }
+
+ /*
+ * Get any [Content-Description] given in buffer.
+ */
+ if (magic && *cp == '[') {
+ ct->c_descr = ++cp;
+ for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+ if (*dp == ']')
+ break;
+ if (dp < cp) {
+ advise (NULL, "invalid description in message %s", ct->c_file);
+ ct->c_descr = NULL;
+ return NOTOK;
+ }
+
+ c = *dp;
+ *dp = '\0';
+ if (*ct->c_descr)
+ ct->c_descr = concat (ct->c_descr, "\n", NULL);
+ else
+ ct->c_descr = NULL;
+ *dp++ = c;
+ cp = dp;
+
+ while (isspace (*cp))
+ cp++;
+ }
+
+ /*
+ * Get any {Content-Disposition} given in buffer.
+ */
+ if (magic && *cp == '{') {
+ ct->c_dispo = ++cp;
+ for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
+ if (*dp == '}')
+ break;
+ if (dp < cp) {
+ advise (NULL, "invalid disposition in message %s", ct->c_file);
+ ct->c_dispo = NULL;
+ return NOTOK;
+ }
+
+ c = *dp;
+ *dp = '\0';
+ if (*ct->c_dispo)
+ ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
+ else
+ ct->c_dispo = NULL;
+ *dp++ = c;
+ cp = dp;
+
+ while (isspace (*cp))
+ cp++;
+ }
+
+ /*
* Check if anything is left over
*/
if (*cp) {
- advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
- ct->c_file, TYPE_FIELD, i, i, "", cp);
+ if (magic) {
+ ci->ci_magic = add (cp, NULL);
+
+ /* If there is a Content-Disposition header and it doesn't
+ have a *filename=, extract it from the magic contents.
+ The r1bindex call skips any leading directory
+ components. */
+ if (ct->c_dispo)
+ ct->c_dispo =
+ incl_name_value (ct->c_dispo,
+ "filename",
+ r1bindex (extract_name_value ("name",
+ ci->
+ ci_magic),
+ '/'));
+ }
+ else
+ advise (NULL,
+ "extraneous information in message %s's %s: field\n%*.*s(%s)",
+ ct->c_file, TYPE_FIELD, i, i, "", cp);
}
return OK;
static int
-get_comment (CT ct, char **ap, int istype)
+get_comment (CT ct, unsigned char **ap, int istype)
{
int i;
- char *bp, *cp;
+ char *bp;
+ unsigned char *cp;
char c, buffer[BUFSIZ], *dp;
CI ci;
InitText (CT ct)
{
char buffer[BUFSIZ];
- char *chset;
+ char *chset = NULL;
char **ap, **ep, *cp;
struct k2v *kv;
struct text *t;
/* match subtype */
for (kv = SubText; kv->kv_key; kv++)
- if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+ if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
break;
ct->c_subtype = kv->kv_value;
- /* allocate text structure */
+ /* allocate text character set structure */
if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) t;
/* scan for charset parameter */
for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
- if (!strcasecmp (*ap, "charset"))
+ if (!mh_strcasecmp (*ap, "charset"))
break;
- if (*ap)
- chset = *ep;
- else
- chset = "US-ASCII"; /* default for text */
-
- /* match character set, or set to unknown */
- for (kv = Charset; kv->kv_key; kv++)
- if (!strcasecmp (chset, kv->kv_key))
- break;
- t->tx_charset = kv->kv_value;
+ /* check if content specified a character set */
+ if (*ap) {
+ /* match character set or set to CHARSET_UNKNOWN */
+ for (kv = Charset; kv->kv_key; kv++) {
+ if (!mh_strcasecmp (*ep, kv->kv_key)) {
+ chset = *ep;
+ break;
+ }
+ }
+ t->tx_charset = kv->kv_value;
+ } else {
+ t->tx_charset = CHARSET_UNSPECIFIED;
+ }
/*
* If we can not handle character set natively,
* then check profile for string to modify the
* terminal or display method.
+ *
+ * termproc is for mhshow, though mhlist -debug prints it, too.
*/
- if (!check_charset (chset, strlen (chset))) {
+ if (chset != NULL && !check_charset (chset, strlen (chset))) {
snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
if ((cp = context_find (buffer)))
ct->c_termproc = getcpy (cp);
{
int inout;
long last, pos;
- char *cp, *dp, **ap, **ep;
+ unsigned char *cp, *dp;
+ char **ap, **ep;
char *bp, buffer[BUFSIZ];
struct multipart *m;
struct k2v *kv;
/* match subtype */
for (kv = SubMultiPart; kv->kv_key; kv++)
- if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+ if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
break;
ct->c_subtype = kv->kv_value;
*/
bp = 0;
for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (!strcasecmp (*ap, "boundary")) {
+ if (!mh_strcasecmp (*ap, "boundary")) {
bp = *ep;
break;
}
if (!(p = get_content (fp, ct->c_file,
ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
- fclose (ct->c_fp);
ct->c_fp = NULL;
return NOTOK;
}
char partnam[BUFSIZ];
if (ct->c_partno) {
- snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
+ snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
pp = partnam + strlen (partnam);
} else {
pp = partnam;
/* match subtype */
for (kv = SubMessage; kv->kv_key; kv++)
- if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+ if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
break;
ct->c_subtype = kv->kv_value;
/* scan for parameters "id", "number", and "total" */
for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (!strcasecmp (*ap, "id")) {
+ if (!mh_strcasecmp (*ap, "id")) {
p->pm_partid = add (*ep, NULL);
continue;
}
- if (!strcasecmp (*ap, "number")) {
+ if (!mh_strcasecmp (*ap, "number")) {
if (sscanf (*ep, "%d", &p->pm_partno) != 1
|| p->pm_partno < 1) {
invalid_param:
}
continue;
}
- if (!strcasecmp (*ap, "total")) {
+ if (!mh_strcasecmp (*ap, "total")) {
if (sscanf (*ep, "%d", &p->pm_maxno) != 1
|| p->pm_maxno < 1)
goto invalid_param;
fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
if (!(p = get_content (fp, ct->c_file, 0))) {
- fclose (ct->c_fp);
ct->c_fp = NULL;
return NOTOK;
}
goto no_body;
}
- if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
- adios (NULL, "out of memory");
+ e->eb_body = bp = mh_xmalloc ((unsigned) size);
fseek (p->c_fp, p->c_begin, SEEK_SET);
while (size > 0)
switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
}
-static int
+int
params_external (CT ct, int composing)
{
char **ap, **ep;
CI ci = &ct->c_ctinfo;
for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (!strcasecmp (*ap, "access-type")) {
+ if (!mh_strcasecmp (*ap, "access-type")) {
struct str2init *s2i;
CT p = e->eb_content;
for (s2i = str2methods; s2i->si_key; s2i++)
- if (!strcasecmp (*ep, s2i->si_key))
+ if (!mh_strcasecmp (*ep, s2i->si_key))
break;
if (!s2i->si_key) {
e->eb_access = *ep;
return NOTOK;
continue;
}
- if (!strcasecmp (*ap, "name")) {
+ if (!mh_strcasecmp (*ap, "name")) {
e->eb_name = *ep;
continue;
}
- if (!strcasecmp (*ap, "permission")) {
+ if (!mh_strcasecmp (*ap, "permission")) {
e->eb_permission = *ep;
continue;
}
- if (!strcasecmp (*ap, "site")) {
+ if (!mh_strcasecmp (*ap, "site")) {
e->eb_site = *ep;
continue;
}
- if (!strcasecmp (*ap, "directory")) {
+ if (!mh_strcasecmp (*ap, "directory")) {
e->eb_dir = *ep;
continue;
}
- if (!strcasecmp (*ap, "mode")) {
+ if (!mh_strcasecmp (*ap, "mode")) {
e->eb_mode = *ep;
continue;
}
- if (!strcasecmp (*ap, "size")) {
+ if (!mh_strcasecmp (*ap, "size")) {
sscanf (*ep, "%lu", &e->eb_size);
continue;
}
- if (!strcasecmp (*ap, "server")) {
+ if (!mh_strcasecmp (*ap, "server")) {
e->eb_server = *ep;
continue;
}
- if (!strcasecmp (*ap, "subject")) {
+ if (!mh_strcasecmp (*ap, "subject")) {
e->eb_subject = *ep;
continue;
}
- if (composing && !strcasecmp (*ap, "body")) {
+ if (composing && !mh_strcasecmp (*ap, "body")) {
e->eb_body = getcpy (*ep);
continue;
}
/* match subtype */
for (kv = SubApplication; kv->kv_key; kv++)
- if (!strcasecmp (ci->ci_subtype, kv->kv_key))
+ if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
break;
ct->c_subtype = kv->kv_value;
}
-static void
+void
close_encoding (CT ct)
{
CE ce;
int fd, len, skip;
unsigned long bits;
unsigned char value, *b, *b1, *b2, *b3;
- char *cp, *ep, buffer[BUFSIZ];
- /* sbeck -- handle prefixes */
+ unsigned char *cp, *ep;
+ char buffer[BUFSIZ];
+ /* sbeck -- handle suffixes */
CI ci;
CE ce;
MD5_CTX mdContext;
}
if (*file == NULL) {
- ce->ce_file = add (m_scratch ("", tmp), NULL);
+ ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
ce->ce_unlink = 1;
} else {
ce->ce_file = add (*file, NULL);
ci->ci_type);
cp = context_find (buffer);
}
- if (cp != NULL && *cp != '\0')
- ce->ce_file = add (cp, ce->ce_file);
+ if (cp != NULL && *cp != '\0') {
+ if (ce->ce_unlink) {
+ // Temporary file already exists, so we rename to
+ // version with extension.
+ char *file_org = strdup(ce->ce_file);
+ ce->ce_file = add (cp, ce->ce_file);
+ if (rename(file_org, ce->ce_file)) {
+ adios (ce->ce_file, "unable to rename %s to ", file_org);
+ }
+ free(file_org);
+
+ } else {
+ ce->ce_file = add (cp, ce->ce_file);
+ }
+ }
if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
openQuoted (CT ct, char **file)
{
int cc, digested, len, quoted;
- char *cp, *ep;
+ unsigned char *cp, *ep;
char buffer[BUFSIZ];
unsigned char mask;
CE ce;
- /* sbeck -- handle prefixes */
+ /* sbeck -- handle suffixes */
CI ci;
MD5_CTX mdContext;
}
if (*file == NULL) {
- ce->ce_file = add (m_scratch ("", tmp), NULL);
+ ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
ce->ce_unlink = 1;
} else {
ce->ce_file = add (*file, NULL);
ci->ci_type);
cp = context_find (buffer);
}
- if (cp != NULL && *cp != '\0')
- ce->ce_file = add (cp, ce->ce_file);
+ if (cp != NULL && *cp != '\0') {
+ if (ce->ce_unlink) {
+ // Temporary file already exists, so we rename to
+ // version with extension.
+ char *file_org = strdup(ce->ce_file);
+ ce->ce_file = add (cp, ce->ce_file);
+ if (rename(file_org, ce->ce_file)) {
+ adios (ce->ce_file, "unable to rename %s to ", file_org);
+ }
+ free(file_org);
+
+ } else {
+ ce->ce_file = add (cp, ce->ce_file);
+ }
+ }
if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
fseek (ct->c_fp, ct->c_begin, SEEK_SET);
while (len > 0) {
- char *dp;
-
if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
content_error (NULL, ct, "premature eof");
goto clean_up;
*++ep = '\n', ep++;
for (; cp < ep; cp++) {
- if (quoted) {
- if (quoted > 1) {
- if (!isxdigit (*cp)) {
-invalid_hex:
- dp = "expecting hexidecimal-digit";
- goto invalid_encoding;
- }
+ if (quoted > 0) {
+ /* in an escape sequence */
+ if (quoted == 1) {
+ /* at byte 1 of an escape sequence */
+ mask = hex2nib[*cp & 0x7f];
+ /* next is byte 2 */
+ quoted = 2;
+ } else {
+ /* at byte 2 of an escape sequence */
mask <<= 4;
mask |= hex2nib[*cp & 0x7f];
putc (mask, ce->ce_fp);
if (digested)
MD5Update (&mdContext, &mask, 1);
- } else {
- switch (*cp) {
- case ':':
- putc (*cp, ce->ce_fp);
- if (digested)
- MD5Update (&mdContext, (unsigned char *) ":", 1);
- break;
-
- default:
- if (!isxdigit (*cp))
- goto invalid_hex;
- mask = hex2nib[*cp & 0x7f];
- quoted = 2;
- continue;
+ if (ferror (ce->ce_fp)) {
+ content_error (ce->ce_file, ct, "error writing to");
+ goto clean_up;
}
+ /* finished escape sequence; next may be literal or a new
+ * escape sequence */
+ quoted = 0;
}
-
- if (ferror (ce->ce_fp)) {
- content_error (ce->ce_file, ct, "error writing to");
- goto clean_up;
- }
- quoted = 0;
+ /* on to next byte */
continue;
}
- switch (*cp) {
- default:
- if (*cp < '!' || *cp > '~') {
- int i;
- dp = "expecting character in range [!..~]";
-
-invalid_encoding:
- i = strlen (invo_name) + 2;
- content_error (NULL, ct,
- "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
- dp, i, i, "", *cp);
- goto clean_up;
+ /* not in an escape sequence */
+ if (*cp == '=') {
+ /* starting an escape sequence, or invalid '='? */
+ if (cp + 1 < ep && cp[1] == '\n') {
+ /* "=\n" soft line break, eat the \n */
+ cp++;
+ continue;
}
- /* and fall...*/
- case ' ':
- case '\t':
- case '\n':
- putc (*cp, ce->ce_fp);
- if (digested) {
- if (*cp == '\n')
- MD5Update (&mdContext, (unsigned char *) "\r\n",2);
- else
- MD5Update (&mdContext, (unsigned char *) cp, 1);
- }
- if (ferror (ce->ce_fp)) {
- content_error (ce->ce_file, ct, "error writing to");
- goto clean_up;
+ if (cp + 1 >= ep || cp + 2 >= ep) {
+ /* We don't have 2 bytes left, so this is an invalid
+ * escape sequence; just show the raw bytes (below). */
+ } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
+ /* Next 2 bytes are hex digits, making this a valid escape
+ * sequence; let's decode it (above). */
+ quoted = 1;
+ continue;
+ } else {
+ /* One or both of the next 2 is out of range, making this
+ * an invalid escape sequence; just show the raw bytes
+ * (below). */
}
- break;
+ }
- case '=':
- if (*++cp != '\n') {
- quoted = 1;
- cp--;
+ /* Just show the raw byte. */
+ putc (*cp, ce->ce_fp);
+ if (digested) {
+ if (*cp == '\n') {
+ MD5Update (&mdContext, (unsigned char *) "\r\n",2);
+ } else {
+ MD5Update (&mdContext, (unsigned char *) cp, 1);
}
- break;
+ }
+ if (ferror (ce->ce_fp)) {
+ content_error (ce->ce_file, ct, "error writing to");
+ goto clean_up;
}
}
}
}
-static int
+int
open7Bit (CT ct, char **file)
{
int cc, fd, len;
char buffer[BUFSIZ];
- /* sbeck -- handle prefixes */
+ /* sbeck -- handle suffixes */
char *cp;
CI ci;
CE ce;
}
if (*file == NULL) {
- ce->ce_file = add (m_scratch ("", tmp), NULL);
+ ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
ce->ce_unlink = 1;
} else {
ce->ce_file = add (*file, NULL);
ci->ci_type);
cp = context_find (buffer);
}
- if (cp != NULL && *cp != '\0')
- ce->ce_file = add (cp, ce->ce_file);
+ if (cp != NULL && *cp != '\0') {
+ if (ce->ce_unlink) {
+ // Temporary file already exists, so we rename to
+ // version with extension.
+ char *file_org = strdup(ce->ce_file);
+ ce->ce_file = add (cp, ce->ce_file);
+ if (rename(file_org, ce->ce_file)) {
+ adios (ce->ce_file, "unable to rename %s to ", file_org);
+ }
+ free(file_org);
+
+ } else {
+ ce->ce_file = add (cp, ce->ce_file);
+ }
+ }
if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
if (ct->c_descr)
fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
+ if (ct->c_dispo)
+ fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
fprintf (ce->ce_fp, "\n");
}
return NOTOK;
}
- if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
+ if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
&& find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
cachefile, sizeof(cachefile)) != NOTOK) {
int mask;
ce->ce_unlink = (*file == NULL);
caching = 0;
cachefile[0] = '\0';
- if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
+ if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
&& find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
cachefile, sizeof(cachefile)) != NOTOK) {
if (*file == NULL) {
else if (caching)
ce->ce_file = add (cachefile, NULL);
else
- ce->ce_file = add (m_scratch ("", tmp), NULL);
+ ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
vec[vecp++] = e->eb_dir;
vec[vecp++] = e->eb_name;
vec[vecp++] = ce->ce_file,
- vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
+ vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
? "ascii" : "binary";
vec[vecp] = NULL;
else
if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
ce->ce_file,
- e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
+ e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
== NOTOK)
goto losing_ftp;
#endif
}
if (*file == NULL) {
- ce->ce_file = add (m_scratch ("", tmp), NULL);
+ ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
ce->ce_unlink = 1;
} else {
ce->ce_file = add (*file, NULL);
return NOTOK;
}
+ /* showproc is for mhshow and mhstore, though mhlist -debug
+ * prints it, too. */
if (ct->c_showproc)
free (ct->c_showproc);
ct->c_showproc = add ("true", NULL);
while (*cp)
cp++;
fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
- cp - bp);
+ (int)(cp - bp));
}
return NOTOK;