#include <h/mh.h>
#include <h/signals.h>
#include <setjmp.h>
-#include <signal.h>
#include <fcntl.h>
#include <h/mime.h>
#include <h/tws.h>
static void clean_up_temporary_files(void);
static int get_line(void);
static void make_mime_composition_file_entry(char *, int, char *);
+static char *mime_type (const char *);
/*
{
int binary; /* binary character found flag */
int c; /* current character */
- char cmd[PATH_MAX + 6]; /* file command buffer */
+ char cmd[PATH_MAX + 8]; /* file command buffer */
char *content_type; /* mime content type */
FILE *fp; /* content and pipe file pointer */
struct node *np; /* context scan node pointer */
char *p; /* miscellaneous string pointer */
struct stat st; /* file status buffer */
- content_type = default_content_type;
-
- /*
- * Check the file name for a suffix. Scan the context for that suffix on a
- * mhshow-suffix- entry. We use these entries to be compatible with mhnshow,
- * and there's no reason to make the user specify each suffix twice. Context
- * entries of the form "mhshow-suffix-contenttype" in the name have the suffix
- * in the field, including the dot.
- */
+ if ((content_type = mime_type (file_name)) == NULL) {
+ /*
+ * Check the file name for a suffix. Scan the context for
+ * that suffix on a mhshow-suffix- entry. We use these
+ * entries to be compatible with mhnshow, and there's no
+ * reason to make the user specify each suffix twice. Context
+ * entries of the form "mhshow-suffix-contenttype" in the name
+ * have the suffix in the field, including the dot.
+ */
+ if ((p = strrchr(file_name, '.')) != (char *)0) {
+ for (np = m_defs; np; np = np->n_next) {
+ if (strncasecmp(np->n_name, "mhshow-suffix-", 14) == 0 &&
+ strcasecmp(p, np->n_field ? np->n_field : "") == 0) {
+ content_type = strdup (np->n_name + 14);
+ break;
+ }
+ }
+ }
- if ((p = strrchr(file_name, '.')) != (char *)0) {
- for (np = m_defs; np; np = np->n_next) {
- if (strncasecmp(np->n_name, "mhshow-suffix-", 14) == 0 && mh_strcasecmp(p, np->n_field) == 0) {
- content_type = np->n_name + 14;
- break;
- }
- }
+ if (content_type == NULL && default_content_type != NULL) {
+ content_type = strdup (default_content_type);
+ }
}
/*
(void)fclose(fp);
- content_type = binary ? "application/octet-stream" : "text/plain";
+ content_type =
+ strdup (binary ? "application/octet-stream" : "text/plain");
}
/*
(void) fprintf (composition_file, "#%s <>", content_type);
} else {
/* Suppress Content-Id, insert simple Content-Disposition
- and Content-Description with filename. */
+ and Content-Description with filename.
+ The Content-Disposition type needs to be "inline" for
+ MS Outlook and BlackBerry calendar programs to properly
+ handle a text/calendar attachment. */
p = strrchr(file_name, '/');
(void) fprintf (composition_file,
- "#%s; name=\"%s\" <> [%s]{attachment}",
+ "#%s; name=\"%s\" <> [%s]{%s}",
content_type,
(p == (char *)0) ? file_name : p + 1,
- (p == (char *)0) ? file_name : p + 1);
+ (p == (char *)0) ? file_name : p + 1,
+ strcmp ("text/calendar", content_type)
+ ? "attachment" : "inline");
}
break;
(void) fprintf (composition_file, "#%s <>", content_type);
} else {
/* Suppress Content-Id, insert Content-Disposition with
- modification date and Content-Description wtih filename. */
+ modification date and Content-Description wtih filename.
+ The Content-Disposition type needs to be "inline" for
+ MS Outlook and BlackBerry calendar programs to properly
+ handle a text/calendar attachment. */
p = strrchr(file_name, '/');
(void) fprintf (composition_file,
- "#%s; name=\"%s\" <>[%s]{attachment; modification-date=\"%s\"}",
+ "#%s; name=\"%s\" <>[%s]{%s; "
+ "modification-date=\"%s\"}",
content_type,
(p == (char *)0) ? file_name : p + 1,
(p == (char *)0) ? file_name : p + 1,
+ strcmp ("text/calendar", content_type)
+ ? "attachment" : "inline",
dtime (&st.st_mtime, 0));
}
adios ((char *)0, "unsupported attachformat %d", attachformat);
}
+ free (content_type);
+
/*
* Finish up with the file name.
*/
return;
}
+/*
+ * Try to use external command to determine mime type, and possibly
+ * encoding. Caller is responsible for free'ing returned memory.
+ */
+static char *
+mime_type (const char *file_name) {
+ char *content_type = NULL; /* mime content type */
+
+#ifdef MIMETYPEPROC
+ char cmd[2 * PATH_MAX + 2]; /* file command buffer */
+ char buf[BUFSIZ >= 2048 ? BUFSIZ : 2048];
+ FILE *fp; /* content and pipe file pointer */
+ char mimetypeproc[] = MIMETYPEPROC " '%s'";
+
+ if ((int) snprintf (cmd, sizeof cmd, mimetypeproc, file_name) <
+ (int) sizeof cmd) {
+ if ((fp = popen (cmd, "r")) != NULL) {
+ /* Make sure that buf has space for one additional
+ character, the semicolon that might be added below. */
+ if (fgets (buf, sizeof buf - 1, fp)) {
+ char *cp, *space;
+
+ /* Skip leading <filename>:<whitespace>, if present. */
+ if ((content_type = strchr (buf, ':')) != NULL) {
+ ++content_type;
+ while (*content_type && isblank (*content_type)) {
+ ++content_type;
+ }
+ } else {
+ content_type = buf;
+ }
+
+ /* Truncate at newline (LF or CR), if present. */
+ if ((cp = strpbrk (content_type, "\n\n")) != NULL) {
+ *cp = '\0';
+ }
+
+ /* If necessary, insert semicolon between content type
+ and charset. Assume that the first space is between
+ them. */
+ if ((space = strchr (content_type, ' ')) != NULL) {
+ ssize_t len = strlen (content_type);
+
+ if (space - content_type > 0 &&
+ len > space - content_type + 1) {
+ if (*(space - 1) != ';') {
+ /* The +1 is for the terminating NULL. */
+ memmove (space + 1, space,
+ len - (space - content_type) + 1);
+ *space = ';';
+ }
+ }
+ }
+ } else {
+ advise (NULL, "unable to read mime type");
+ }
+ } else {
+ advise (NULL, "unable to run %s", buf);
+ }
+ } else {
+ advise (NULL, "filename to large to deduce mime type");
+ }
+#else
+ NMH_UNUSED (file_name);
+#endif
+
+ return content_type ? strdup (content_type) : NULL;
+}
+
/*
* Split large message into several messages of
* type "message/partial" and send them.
/*
* This header field is discarded.
*/
- if (!mh_strcasecmp (name, "Message-ID")) {
+ if (!strcasecmp (name, "Message-ID")) {
while (state == FLDPLUS) {
bufsz = sizeof buffer;
state = m_getfld (&gstate, name, buffer, &bufsz, in);
}
} else if (uprf (name, XXX_FIELD_PRF)
- || !mh_strcasecmp (name, VRSN_FIELD)
- || !mh_strcasecmp (name, "Subject")
- || !mh_strcasecmp (name, "Encrypted")) {
+ || !strcasecmp (name, VRSN_FIELD)
+ || !strcasecmp (name, "Subject")
+ || !strcasecmp (name, "Encrypted")) {
/*
* These header fields are copied to the enclosed
* header of the first message in the collection
* version of it, can be copied to the header
* of each messsage/partial in the collection.
*/
- if (!mh_strcasecmp (name, "Subject")) {
+ if (!strcasecmp (name, "Subject")) {
size_t sublen;
strncpy (subject, buffer, BUFSIZ);
fprintf (stderr, "unable to exec ");
perror (postproc);
_exit (-1);
- break; /* NOT REACHED */
default:
/*
armed_done (int status)
{
longjmp (env, status ? status : NOTOK);
-
- exit (status);
}