X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/afee8ed17a56617618fb7f963b111d7cf0fb56e7..2181cd0442664bf083825fd5b2d789714ba1812d:/uip/sendsbr.c diff --git a/uip/sendsbr.c b/uip/sendsbr.c index 2350ccf4..bfd65157 100644 --- a/uip/sendsbr.c +++ b/uip/sendsbr.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,7 @@ static FILE *composition_file; /* composition file pointer */ /* * 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); /* @@ -57,13 +56,14 @@ static void alert (char *, int); 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 *); /* @@ -71,7 +71,8 @@ static void make_mime_composition_file_entry(char *, int, 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]; @@ -132,9 +133,9 @@ sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft, cha */ 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 */ @@ -418,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); + } } /* @@ -467,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"); } /* @@ -533,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; @@ -552,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)); } @@ -567,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. */ @@ -576,13 +596,83 @@ 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. */ 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; @@ -614,15 +704,15 @@ splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay) /* * 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 @@ -631,7 +721,7 @@ splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay) * 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); @@ -698,7 +788,7 @@ splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay) free (dp); fclose (in); - return sendaux (vec, vecp, drft, st); + return sendaux (vec, vecp, program, drft, st); } if (!pushsw) { @@ -788,7 +878,7 @@ splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay) } 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; @@ -811,11 +901,11 @@ splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay) /* * 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; @@ -838,7 +928,7 @@ sendaux (char **vec, int vecp, char *drft, struct stat *st) 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) { @@ -859,11 +949,10 @@ sendaux (char **vec, int vecp, char *drft, struct stat *st) 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: /* @@ -1062,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; @@ -1133,6 +1222,4 @@ static void armed_done (int status) { longjmp (env, status ? status : NOTOK); - - exit (status); }