X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/4e3c586d24d33c0d0f6b1f16a84cf9b1a08076d7..e2ffa5756f261ab2f0c5bd4199b3f4021a5eca38:/uip/sendsbr.c diff --git a/uip/sendsbr.c b/uip/sendsbr.c index e3a8195f..bfd65157 100644 --- a/uip/sendsbr.c +++ b/uip/sendsbr.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -64,6 +63,7 @@ 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 *); /* @@ -419,30 +419,35 @@ make_mime_composition_file_entry(char *file_name, int attachformat, { 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); + } } /* @@ -468,7 +473,8 @@ make_mime_composition_file_entry(char *file_name, int attachformat, (void)fclose(fp); - content_type = binary ? "application/octet-stream" : "text/plain"; + content_type = + strdup (binary ? "application/octet-stream" : "text/plain"); } /* @@ -534,13 +540,18 @@ make_mime_composition_file_entry(char *file_name, int attachformat, (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; @@ -553,13 +564,19 @@ make_mime_composition_file_entry(char *file_name, int attachformat, (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)); } @@ -568,6 +585,8 @@ make_mime_composition_file_entry(char *file_name, int attachformat, adios ((char *)0, "unsupported attachformat %d", attachformat); } + free (content_type); + /* * Finish up with the file name. */ @@ -577,6 +596,75 @@ make_mime_composition_file_entry(char *file_name, int attachformat, 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 :, 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. @@ -616,15 +704,15 @@ splitmsg (char **vec, int vecp, char *program, char *drft, /* * 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 @@ -633,7 +721,7 @@ splitmsg (char **vec, int vecp, char *program, char *drft, * 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); @@ -865,7 +953,6 @@ sendaux (char **vec, int vecp, char *program, char *drft, struct stat *st) fprintf (stderr, "unable to exec "); perror (postproc); _exit (-1); - break; /* NOT REACHED */ default: /* @@ -1064,7 +1151,7 @@ annoaux (int fd) 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; @@ -1135,6 +1222,4 @@ static void armed_done (int status) { longjmp (env, status ? status : NOTOK); - - exit (status); }