#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>
/*
* external prototypes
*/
-int sendsbr (char **, int, char *, struct stat *, int, char *, int);
+int sendsbr (char **, int, char *, char *, struct stat *, int, char *, int);
char *getusername (void);
/*
static int tmp_fd (void);
static void anno (int, struct stat *);
static void annoaux (int);
-static int splitmsg (char **, int, char *, struct stat *, int);
-static int sendaux (char **, int, char *, struct stat *);
+static int splitmsg (char **, int, char *, char *, struct stat *, int);
+static int sendaux (char **, int, char *, char *, struct stat *);
static int attach(char *, char *, int);
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
-sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft, char *attachment_header_field_name, int attachformat)
+sendsbr (char **vec, int vecp, char *program, char *drft, struct stat *st,
+ int rename_drft, char *attachment_header_field_name, int attachformat)
{
int status;
char buffer[BUFSIZ], file[BUFSIZ];
*/
if (splitsw >= 0 && !distfile && stat (drft, &sts) != NOTOK
&& sts.st_size >= CPERMSG) {
- status = splitmsg (vec, vecp, drft, st, splitsw) ? NOTOK : OK;
+ status = splitmsg (vec, vecp, program, drft, st, splitsw) ? NOTOK : OK;
} else {
- status = sendaux (vec, vecp, drft, st) ? NOTOK : OK;
+ status = sendaux (vec, vecp, program, drft, st) ? NOTOK : OK;
}
/* rename the original draft */
{
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.
*/
static int
-splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay)
+splitmsg (char **vec, int vecp, char *program, char *drft,
+ struct stat *st, int delay)
{
int compnum, nparts, partno, state, status;
long pos, start;
/*
* 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);
free (dp);
fclose (in);
- return sendaux (vec, vecp, drft, st);
+ return sendaux (vec, vecp, program, drft, st);
}
if (!pushsw) {
}
snprintf (partnum, sizeof(partnum), "%d", partno);
- status = sendaux (vec, vecp, tmpdrf, st);
+ status = sendaux (vec, vecp, program, tmpdrf, st);
unlink (tmpdrf);
if (status != OK)
break;
/*
* Annotate original message, and
- * call `postproc' to send message.
+ * call `postproc' (which is passed down in "program") to send message.
*/
static int
-sendaux (char **vec, int vecp, char *drft, struct stat *st)
+sendaux (char **vec, int vecp, char *program, char *drft, struct stat *st)
{
pid_t child_id;
int i, status, fd, fd2;
done (1);
vec[vecp] = NULL;
- for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
+ for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
sleep (5);
switch (child_id) {
dup2 (fd, fileno (stderr));
close (fd);
}
- execvp (postproc, vec);
+ execvp (program, vec);
fprintf (stderr, "unable to exec ");
perror (postproc);
_exit (-1);
- break; /* NOT REACHED */
default:
/*
alert (char *file, int out)
{
pid_t child_id;
- int i, in;
+ int i, in, argp;
char buf[BUFSIZ];
+ char *program;
+ char **arglist;
for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
sleep (5);
snprintf (buf, sizeof(buf), "send failed on %s",
forwsw ? "enclosed draft" : file);
- execlp (mailproc, r1bindex (mailproc, '/'), getusername (),
- "-subject", buf, NULL);
+ arglist = argsplit(mailproc, &program, &argp);
+
+ arglist[argp++] = getusername();
+ arglist[argp++] = "-subject";
+ arglist[argp++] = buf;
+ arglist[argp] = NULL;
+
+ execvp (program, arglist);
fprintf (stderr, "unable to exec ");
perror (mailproc);
_exit (-1);
admonish (maildir, "unable to change directory to");
return;
}
- if (!(mp = folder_read (folder))) {
+ if (!(mp = folder_read (folder, 0))) {
if (debugsw)
admonish (NULL, "unable to read folder %s", folder);
return;
armed_done (int status)
{
longjmp (env, status ? status : NOTOK);
-
- exit (status);
}