From: Pierre Habouzit Date: Tue, 14 Nov 2006 01:01:25 +0000 (+0100) Subject: begin to rework mailcap parsing a "bit". X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=commitdiff_plain;h=49081299c673ceebc38d9a4f0985608cb3aecfce begin to rework mailcap parsing a "bit". the current way is really really really completely naïve (the mailcap parse is done many times whereas it just should be put into structs once for all !). Signed-off-by: Pierre Habouzit --- diff --git a/Makefile.am b/Makefile.am index 62c888c..7b1775c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,7 +23,7 @@ madmutt_SOURCES = $(BUILT_SOURCES) \ flags.c filter.c from.c handler.c hcache.c hdrline.c headers.c help.c hook.c \ main.c mbox.c mh.c muttlib.c mutt_idna.c mx.c \ pager.c pattern.c postpone.c query.c \ - recvattach.c recvcmd.c rfc1524.c rfc3676.c \ + recvattach.c recvcmd.c rfc3676.c \ score.c send.c sendlib.c sidebar.c sort.c state.c status.c \ thread.c account.c @@ -70,7 +70,7 @@ EXTRA_DIST = config.rpath COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO \ dotlock.h functions.def gen_defs \ recvattach.h handler.h thread.h \ globals.h init.h keymap.h mutt_crypt.h \ - mime.h mutt.h mutt_sasl.h mbox.h mh.h mx.h pager.h protos.h rfc1524.h \ + mime.h mutt.h mutt_sasl.h mbox.h mh.h mx.h pager.h protos.h \ rfc3676.h sort.h mime.types autogen.sh \ OPS.MIX remailer.c remailer.h browser.h state.h \ lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h madmuttrc.head madmuttrc \ diff --git a/attach.c b/attach.c index 63723dc..5e43321 100644 --- a/attach.c +++ b/attach.c @@ -38,7 +38,6 @@ #include "handler.h" #include "recvattach.h" #include "keymap.h" -#include "rfc1524.h" #include "pager.h" #include "copy.h" #include "mx.h" @@ -48,7 +47,7 @@ int mutt_get_tmp_attachment (BODY * a) { char type[STRING]; char tempfile[_POSIX_PATH_MAX]; - rfc1524_entry *entry = rfc1524_new_entry (); + rfc1524_entry *entry = rfc1524_entry_new(); FILE *fpin = NULL, *fpout = NULL; struct stat st; @@ -60,7 +59,7 @@ int mutt_get_tmp_attachment (BODY * a) rfc1524_expand_filename (entry->nametemplate, a->filename, tempfile, sizeof (tempfile)); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); if (stat (a->filename, &st) == -1) return -1; @@ -91,7 +90,7 @@ int mutt_compose_attachment (BODY * a) char type[STRING]; char command[STRING]; char newfile[_POSIX_PATH_MAX] = ""; - rfc1524_entry *entry = rfc1524_new_entry (); + rfc1524_entry *entry = rfc1524_entry_new(); short unlink_newfile = 0; int rc = 0; @@ -183,7 +182,7 @@ int mutt_compose_attachment (BODY * a) } } else { - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); mutt_message (_("No mailcap compose entry for %s, creating empty file."), type); return 1; @@ -196,7 +195,7 @@ bailout: if (unlink_newfile) unlink (newfile); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return rc; } @@ -214,7 +213,7 @@ int mutt_edit_attachment (BODY * a) char type[STRING]; char command[STRING]; char newfile[_POSIX_PATH_MAX] = ""; - rfc1524_entry *entry = rfc1524_new_entry (); + rfc1524_entry *entry = rfc1524_entry_new(); short unlink_newfile = 0; int rc = 0; @@ -256,7 +255,7 @@ int mutt_edit_attachment (BODY * a) mutt_edit_file (NONULL (Editor), a->filename); } else { - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); mutt_error (_("No mailcap edit entry for %s"), type); return 0; } @@ -268,7 +267,7 @@ bailout: if (unlink_newfile) unlink (newfile); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return rc; } @@ -405,11 +404,11 @@ int mutt_view_attachment (FILE * fp, BODY * a, int flag, HEADER * hdr, snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype); if (use_mailcap) { - entry = rfc1524_new_entry (); + entry = rfc1524_entry_new(); if (!rfc1524_mailcap_lookup (a, type, entry, 0)) { if (flag == M_REGULAR) { /* fallback to view as text */ - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); mutt_error _("No matching mailcap entry found. Viewing as text."); flag = M_AS_TEXT; @@ -588,7 +587,7 @@ int mutt_view_attachment (FILE * fp, BODY * a, int flag, HEADER * hdr, return_error: if (entry) - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); if (fp && tempfile[0]) mutt_unlink (tempfile); else if (unlink_tempfile) @@ -895,7 +894,7 @@ int mutt_print_attachment (FILE * fp, BODY * a) rfc1524_entry *entry; int piped = FALSE; - entry = rfc1524_new_entry (); + entry = rfc1524_entry_new(); rfc1524_mailcap_lookup (a, type, entry, M_PRINT); if (rfc1524_expand_filename (entry->nametemplate, a->filename, newfile, sizeof (newfile))) { @@ -903,7 +902,7 @@ int mutt_print_attachment (FILE * fp, BODY * a) if (safe_symlink (a->filename, newfile) == -1) { if (mutt_yesorno (_("Can't match nametemplate, continue?"), M_YES) != M_YES) { - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return 0; } m_strcpy(newfile, sizeof(newfile), a->filename); @@ -927,14 +926,14 @@ int mutt_print_attachment (FILE * fp, BODY * a) if (piped) { if ((ifp = fopen (newfile, "r")) == NULL) { mutt_perror ("fopen"); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return (0); } if ((thepid = mutt_create_filter (command, &fpout, NULL, NULL)) < 0) { mutt_perror (_("Can't create filter")); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); safe_fclose (&ifp); return 0; } @@ -954,7 +953,7 @@ int mutt_print_attachment (FILE * fp, BODY * a) else if (unlink_newfile) unlink (newfile); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return (1); } diff --git a/commands.c b/commands.c index e64c8c2..2054d12 100644 --- a/commands.c +++ b/commands.c @@ -44,7 +44,6 @@ #include "pager.h" #include #include "mutt_idna.h" -#include "rfc1524.h" #include #include #include diff --git a/compose.c b/compose.c index 23ba10f..72e28f3 100644 --- a/compose.c +++ b/compose.c @@ -39,7 +39,6 @@ #include "mutt.h" #include "alias.h" #include "mutt_idna.h" -#include "rfc1524.h" #include "attach.h" #include "recvattach.h" #include "sort.h" diff --git a/handler.c b/handler.c index 4129fda..fc80372 100644 --- a/handler.c +++ b/handler.c @@ -31,7 +31,6 @@ #include "mutt.h" #include "recvattach.h" #include "handler.h" -#include "rfc1524.h" #include "rfc3676.h" #include "keymap.h" #include "copy.h" @@ -840,12 +839,12 @@ static int alternative_handler (BODY * a, STATE * s) while (b) { snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype); if (mutt_is_autoview (b, buf)) { - rfc1524_entry *entry = rfc1524_new_entry (); + rfc1524_entry *entry = rfc1524_entry_new(); if (rfc1524_mailcap_lookup (b, buf, entry, M_AUTOVIEW)) { choice = b; } - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); } b = b->next; } @@ -1057,7 +1056,7 @@ static int multipart_handler (BODY * a, STATE * s) static int autoview_handler (BODY * a, STATE * s) { - rfc1524_entry *entry = rfc1524_new_entry (); + rfc1524_entry *entry = rfc1524_entry_new(); char buffer[LONG_STRING]; char type[STRING]; char command[LONG_STRING]; @@ -1094,7 +1093,7 @@ static int autoview_handler (BODY * a, STATE * s) if ((fpin = safe_fopen (tempfile, "w+")) == NULL) { mutt_perror ("fopen"); - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return (-1); } @@ -1170,7 +1169,7 @@ static int autoview_handler (BODY * a, STATE * s) if (s->flags & M_DISPLAY) mutt_clear_error (); } - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); return (rc); } @@ -1321,13 +1320,13 @@ int mutt_body_handler (BODY * b, STATE * s) snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype); if (mutt_is_autoview (b, type)) { - rfc1524_entry *entry = rfc1524_new_entry (); + rfc1524_entry *entry = rfc1524_entry_new(); if (rfc1524_mailcap_lookup (b, type, entry, M_AUTOVIEW)) { handler = autoview_handler; s->flags &= ~M_CHARCONV; } - rfc1524_free_entry (&entry); + rfc1524_entry_delete(&entry); } else if (b->type == TYPETEXT) { if (ascii_strcasecmp ("plain", b->subtype) == 0) { diff --git a/lib-lib/file.c b/lib-lib/file.c index b51b1ff..67b1d74 100644 --- a/lib-lib/file.c +++ b/lib-lib/file.c @@ -252,6 +252,43 @@ int safe_fclose(FILE **f) return r; } +/* If rfc1524_expand_command() is used on a recv'd message, then + * the filename doesn't exist yet, but if its used while sending a message, + * then we need to rename the existing file. + * + * This function returns 0 on successful move, 1 on old file doesn't exist, + * 2 on new file already exists, and 3 on other failure. + */ + +/* note on access(2) use: No dangling symlink problems here due to + * safe_fopen(). + */ +int mutt_rename_file(char *oldfile, char *newfile) +{ + FILE *ofp, *nfp; + + if (access(oldfile, F_OK) != 0) + return 1; + if (access(newfile, F_OK) == 0) + return 2; + + ofp = fopen(oldfile, "r"); + if (!ofp) + return 3; + + nfp = safe_fopen(newfile, "w"); + if (!nfp) { + fclose (ofp); + return 3; + } + + mutt_copy_stream(ofp, nfp); + fclose(nfp); + fclose(ofp); + mutt_unlink(oldfile); + return 0; +} + /* Read a line from ``fp'' into the dynamically allocated ``s'', * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed. * If a line ends with "\", this char and the linefeed is removed, diff --git a/lib-lib/file.h b/lib-lib/file.h index 2b73551..9d6ca73 100644 --- a/lib-lib/file.h +++ b/lib-lib/file.h @@ -50,6 +50,7 @@ void mutt_unlink(const char *); /****************************************************************************/ FILE *safe_fopen(const char *, const char *); +int mutt_rename_file(char *, char *); int safe_fclose(FILE **); char *mutt_read_line(char *, ssize_t *, FILE *, int *); diff --git a/lib-mime/Makefile.am b/lib-mime/Makefile.am index 003aaf3..c332223 100644 --- a/lib-mime/Makefile.am +++ b/lib-mime/Makefile.am @@ -1,10 +1,10 @@ BUILT_SOURCES = mime-token.h mime-token.c -CLEANFILES = $(BUILT_SOURCES) +DISTCLEANFILES = $(BUILT_SOURCES) noinst_LIBRARIES = libmime.a libmime_a_SOURCES = mime.h mime-types.h $(BUILT_SOURCES) \ - mime.c rfc822address.c rfc822parse.c rfc2047.c rfc2231.c + mime.c rfc822address.c rfc822parse.c rfc1524.c rfc2047.c rfc2231.c noinst_HEADERS = mime.h mime-types.h diff --git a/lib-mime/mime-token.def b/lib-mime/mime-token.def index 06b8ca7..44d9c9e 100644 --- a/lib-mime/mime-token.def +++ b/lib-mime/mime-token.def @@ -9,21 +9,23 @@ base64 bcc binary cc +compose +composetyped content-description content-disposition content-length content-transfer-encoding content-type +copiousoutput date digest +edit expires external-body followup-to from image in-reply-to -news -rfc822 iso-2022-jp lines list-post @@ -34,18 +36,24 @@ message-id mime-version model multipart +nametemplate +needsterminal +news newsgroups organization +print quoted-printable received references reply-to return-path +rfc822 sender status subject supercedes supersedes +test text to unknown @@ -53,6 +61,7 @@ us-ascii utf-8 video x-comment-to +x-convert x-label xref x-status diff --git a/lib-mime/mime.c b/lib-mime/mime.c index 95dabad..3a8a9ed 100644 --- a/lib-mime/mime.c +++ b/lib-mime/mime.c @@ -48,6 +48,18 @@ const char *BodyEncodings[] = { "x-uuencoded", }; +void rfc1524_entry_wipe(rfc1524_entry *p) +{ + p_delete(&p->command); + p_delete(&p->testcommand); + p_delete(&p->composecommand); + p_delete(&p->composetypecommand); + p_delete(&p->editcommand); + p_delete(&p->printcommand); + p_delete(&p->nametemplate); + p_delete(&p->convert); +} + void envelope_wipe(ENVELOPE *p) { address_list_wipe(&p->return_path); diff --git a/lib-mime/mime.h b/lib-mime/mime.h index 8eb4511..8b8b893 100644 --- a/lib-mime/mime.h +++ b/lib-mime/mime.h @@ -16,7 +16,6 @@ * * Copyright © 2006 Pierre Habouzit */ - /* * Copyright notice from original mutt: * Copyright (C) 1996-2000 Michael R. Elkins @@ -53,6 +52,37 @@ extern const char *BodyEncodings[]; int url_parse_mailto(ENVELOPE *e, char **body, const char *src); +/****************************************************************************/ +/* RFC 1524 */ +/* A User Agent Configuration Mechanism */ +/* For Multimedia Mail Format Information */ +/****************************************************************************/ + +typedef struct rfc1524_entry { + /* char *contenttype; *//* we don't need this, as we search for it */ + char *command; + char *testcommand; + char *composecommand; + char *composetypecommand; + char *editcommand; + char *printcommand; + char *nametemplate; + char *convert; + + unsigned needsterminal:1; /* endwin() and system */ + unsigned copiousoutput:1; /* needs pager, basically */ +} rfc1524_entry; + +DO_INIT(rfc1524_entry, rfc1524_entry); +void rfc1524_entry_wipe(rfc1524_entry *); + +DO_NEW(rfc1524_entry, rfc1524_entry); +DO_DELETE(rfc1524_entry, rfc1524_entry); + +int rfc1524_expand_command(BODY *, const char *, const char *, char *, int); +int rfc1524_expand_filename(char *, char *, char *, ssize_t); +int rfc1524_mailcap_lookup (BODY *, char *, rfc1524_entry *, int); + /****************************************************************************/ /* RFC 822 */ /* Standard for ARPA Internet Text Messages */ diff --git a/lib-mime/rfc1524.c b/lib-mime/rfc1524.c new file mode 100644 index 0000000..c2c8058 --- /dev/null +++ b/lib-mime/rfc1524.c @@ -0,0 +1,498 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Copyright © 2006 Pierre Habouzit + */ +/* + * Copyright notice from original mutt: + * Copyright (C) 1996-2000 Michael R. Elkins + * + * This file is part of mutt-ng, see http://www.muttng.org/. + * It's licensed under the GNU General Public License, + * please see the file GPL in the top level source directory. + */ + +/* + * rfc1524 defines a format for the Multimedia Mail Configuration, which + * is the standard mailcap file format under Unix which specifies what + * external programs should be used to view/compose/edit multimedia files + * based on content type. + * + * This file contains various functions for implementing a fair subset of + * rfc1524. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "mutt.h" +#include "mime.h" +#include "attach.h" + +/* The command semantics include the following: + * %s is the filename that contains the mail body data + * %t is the content type, like text/plain + * %{parameter} is replaced by the parameter value from the content-type field + * \% is % + * Unsupported rfc1524 parameters: these would probably require some doing + * by mutt, and can probably just be done by piping the message to metamail + * %n is the integer number of sub-parts in the multipart + * %F is "content-type filename" repeated for each sub-part + * + * In addition, this function returns a 0 if the command works on a file, + * and 1 if the command works on a pipe. + */ +int rfc1524_expand_command(BODY *a, const char *filename, const char *mtype, + char *command, int clen) +{ + int x = 0, y = 0; + int needspipe = TRUE; + char buf[LONG_STRING]; + char type[LONG_STRING]; + + m_strcpy(type, sizeof(type), mtype); + + if (option(OPTMAILCAPSANITIZE)) + mutt_sanitize_filename(type, 0); + + while (command[x] && x < clen && y < ssizeof(buf)) { + switch (command[x]) { + case '\\': + x++; + buf[y++] = command[x++]; + break; + + case '%': + x++; + if (command[x] == '{') { + char param[STRING]; + char pval[STRING]; + int z = 0; + + x++; + while (command[x] && command[x] != '}' && z < ssizeof (param)) + param[z++] = command[x++]; + param[z] = '\0'; + + m_strcpy(pval, sizeof(pval), + mutt_get_parameter(param, a->parameter)); + + if (option(OPTMAILCAPSANITIZE)) + mutt_sanitize_filename(pval, 0); + + y += mutt_quote_filename(buf + y, sizeof(buf) - y, pval); + } + + if (command[x] == 's' && filename) { + y += mutt_quote_filename(buf + y, sizeof(buf) - y, filename); + needspipe = FALSE; + } + + if (command[x] == 't') { + y += mutt_quote_filename(buf + y, sizeof(buf) - y, type); + } + x++; + break; + + default: + buf[y++] = command[x++]; + break; + } + } + + buf[y] = '\0'; + m_strcpy(command, clen, buf); + + return needspipe; +} + +static char *parse_field(char *p, char **field, char **value) +{ + p = vskipspaces(p); + *field = p; + *value = NULL; + + for (;;) { + switch (*p) { + case ';': + *p++ = '\0'; + case '\0': + m_strrtrim(*field); + m_strrtrim(*value); + return p; + + case '\\': + p += 1 + (p[1] != 0); + break; + + case '=': + if (!*value) { + *p++ = '\0'; + p = *value = vskipspaces(p); + continue; + } + /* falltrhrough */ + + default: + p++; + break; + } + } +} + +static inline void +parse_field_error(const char *type, const char *filename, int line) +{ + mutt_error(_("Improperly formated entry for type %s in \"%s\" line %d"), + type, filename, line); +} + +/* rfc1524 mailcap file is of the format: + * base/type; command; extradefs + * type can be * for matching all + * base with no /type is an implicit wild + * command contains a %s for the filename to pass, default to pipe on stdin + * extradefs are of the form: + * def1="definition"; def2="define \;"; + * line wraps with a \ at the end of the line + * # for comments + */ +static int +rfc1524_mailcap_parse(BODY *a, const char *filename, const char *type, + rfc1524_entry *entry, int opt) +{ + FILE *fp; + char *buf = NULL; + ssize_t buflen; + char *ch; + char *field, *value; + int found = FALSE; + int copiousoutput; + int composecommand; + int editcommand; + int printcommand; + int btlen; + int line = 0; + + /* find length of basetype */ + if ((ch = strchr (type, '/')) == NULL) + return FALSE; + btlen = ch - type; + + fp = fopen(filename, "r"); + if (!fp) + goto error; + + while (!found && (buf = mutt_read_line(buf, &buflen, fp, &line)) != NULL) { + /* ignore comments */ + if (*buf == '#') + continue; + + /* check type */ + ch = parse_field(buf, &field, &value); + if (ascii_strcasecmp(field, type) + && (ascii_strncasecmp(field, type, btlen) + || (buf[btlen] != 0 && m_strcmp(buf + btlen, "/*")))) + continue; + + /* next field is the viewcommand */ + ch = parse_field(ch, &field, &value); + if (entry) + entry->command = m_strdup(field); + + /* parse the optional fields */ + found = TRUE; + copiousoutput = FALSE; + composecommand = FALSE; + editcommand = FALSE; + printcommand = FALSE; + + while (*ch) { + ch = parse_field(ch, &field, &value); + + switch (mime_which_token(field, -1)) { +#define DO_CASE(token, field, expr) \ + case token: \ + if (value) { \ + if (entry) \ + m_strreplace(&entry->field, value); \ + expr; \ + } else { \ + parse_field_error(type, filename, line); \ + } \ + break; + + case MIME_NEEDSTERMINAL: + if (entry) + entry->needsterminal = TRUE; + break; + + case MIME_COPIOUSOUTPUT: + copiousoutput = TRUE; + if (entry) + entry->copiousoutput = TRUE; + break; + + DO_CASE(MIME_COMPOSETYPED, composecommand, composecommand = TRUE); + DO_CASE(MIME_COMPOSE, composecommand, composecommand = TRUE); + DO_CASE(MIME_PRINT, printcommand, printcommand = TRUE); + DO_CASE(MIME_EDIT, editcommand, editcommand = TRUE); + DO_CASE(MIME_NAMETEMPLATE, nametemplate, ); + DO_CASE(MIME_X_CONVERT, convert, ); + + case MIME_TEST: + /* + * This routine executes the given test command to determine + * if this is the right entry. a non-zero exit code means + * test failed + */ + if (value) { + ssize_t len = m_strlen(value) + STRING; + char *testcmd = p_new(char, len); + + strcpy(testcmd, value); + rfc1524_expand_command(a, a->filename, type, testcmd, len); + found = !mutt_system(testcmd); + p_delete(&testcmd); + } else { + parse_field_error(type, filename, line); + } + break; + + default: + break; +#undef DO_CASE + } + } + + if (opt == M_AUTOVIEW) { + if (!copiousoutput) + found = FALSE; + } + else if (opt == M_COMPOSE) { + if (!composecommand) + found = FALSE; + } + else if (opt == M_EDIT) { + if (!editcommand) + found = FALSE; + } + else if (opt == M_PRINT) { + if (!printcommand) + found = FALSE; + } + + if (!found && entry) { + rfc1524_entry_wipe(entry); + rfc1524_entry_init(entry); + } + } /* while (!found && (buf = mutt_read_line ())) */ + fclose (fp); + + error: + p_delete(&buf); + return found; +} + + +/************** READ MARK **********************/ + +/* + * rfc1524_mailcap_lookup attempts to find the given type in the + * list of mailcap files. On success, this returns the entry information + * in *entry, and returns 1. On failure (not found), returns 0. + * If entry == NULL just return 1 if the given type is found. + */ +int rfc1524_mailcap_lookup (BODY * a, char *type, rfc1524_entry * entry, + int opt) +{ + char path[_POSIX_PATH_MAX]; + int x; + int found = FALSE; + char *curr = MailcapPath; + + /* rfc1524 specifies that a path of mailcap files should be searched. + * joy. They say + * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc + * and overriden by the MAILCAPS environment variable, and, just to be nice, + * we'll make it specifiable in .muttrc + */ + if (!curr || !*curr) { + mutt_error _("No mailcap path specified"); + + return 0; + } + + mutt_check_lookup_list (a, type, SHORT_STRING); + + while (!found && *curr) { + x = 0; + while (*curr && *curr != ':' && x < ssizeof (path) - 1) { + path[x++] = *curr; + curr++; + } + if (*curr) + curr++; + + if (!x) + continue; + + path[x] = '\0'; + mutt_expand_path (path, sizeof (path)); + + found = rfc1524_mailcap_parse (a, path, type, entry, opt); + } + + if (entry && !found) + mutt_error (_("mailcap entry for type %s not found"), type); + + return found; +} + + +/* This routine will create a _temporary_ filename matching the + * name template given if this needs to be done. + * + * Please note that only the last path element of the + * template and/or the old file name will be used for the + * comparison and the temporary file name. + * + * Returns 0 if oldfile is fine as is. + * Returns 1 if newfile specified + */ + +int rfc1524_expand_filename (char *nametemplate, + char *oldfile, char *newfile, ssize_t nflen) +{ + int i, j, k, ps, r; + char *s; + short lmatch = 0, rmatch = 0; + char left[_POSIX_PATH_MAX]; + char right[_POSIX_PATH_MAX]; + + newfile[0] = 0; + + /* first, ignore leading path components. + */ + + if (nametemplate && (s = strrchr (nametemplate, '/'))) + nametemplate = s + 1; + + if (oldfile && (s = strrchr (oldfile, '/'))) + oldfile = s + 1; + + if (!nametemplate) { + if (oldfile) + m_strcpy(newfile, nflen, oldfile); + } + else if (!oldfile) { + mutt_expand_fmt (newfile, nflen, nametemplate, "mutt"); + } + else { /* oldfile && nametemplate */ + + + /* first, compare everything left from the "%s" + * (if there is one). + */ + + lmatch = 1; + ps = 0; + for (i = 0; nametemplate[i]; i++) { + if (nametemplate[i] == '%' && nametemplate[i + 1] == 's') { + ps = 1; + break; + } + + /* note that the following will _not_ read beyond oldfile's end. */ + + if (lmatch && nametemplate[i] != oldfile[i]) + lmatch = 0; + } + + if (ps) { + + /* If we had a "%s", check the rest. */ + + /* now, for the right part: compare everything right from + * the "%s" to the final part of oldfile. + * + * The logic here is as follows: + * + * - We start reading from the end. + * - There must be a match _right_ from the "%s", + * thus the i + 2. + * - If there was a left hand match, this stuff + * must not be counted again. That's done by the + * condition (j >= (lmatch ? i : 0)). + */ + + rmatch = 1; + + for (r = 0, j = m_strlen(oldfile) - 1, k = + m_strlen(nametemplate) - 1; + j >= (lmatch ? i : 0) && k >= i + 2; j--, k--) { + if (nametemplate[k] != oldfile[j]) { + rmatch = 0; + break; + } + } + + /* Now, check if we had a full match. */ + + if (k >= i + 2) + rmatch = 0; + + if (lmatch) + *left = 0; + else + m_strncpy(left, sizeof(left), nametemplate, i); + + if (rmatch) + *right = 0; + else + m_strcpy(right, sizeof(right), nametemplate + i + 2); + + snprintf (newfile, nflen, "%s%s%s", left, oldfile, right); + } + else { + /* no "%s" in the name template. */ + m_strcpy(newfile, nflen, nametemplate); + } + } + + mutt_adv_mktemp (NULL, newfile, nflen); + + return !(rmatch && lmatch); +} + diff --git a/nntp/newsrc.c b/nntp/newsrc.c index 4d45c0d..3b8e1fa 100644 --- a/nntp/newsrc.c +++ b/nntp/newsrc.c @@ -33,7 +33,6 @@ #include "sort.h" #include "mx.h" #include "nntp.h" -#include "rfc1524.h" void nntp_add_to_list (NNTP_SERVER * s, NNTP_DATA * d) { diff --git a/nntp/nntp.c b/nntp/nntp.c index aa9d915..6e20eef 100644 --- a/nntp/nntp.c +++ b/nntp/nntp.c @@ -26,7 +26,6 @@ #include "sort.h" #include "mx.h" #include "mx_nntp.h" -#include "rfc1524.h" #include "nntp.h" #include "sidebar.h" #include "buffy.h" diff --git a/postpone.c b/postpone.c index 4b91fe8..1436a84 100644 --- a/postpone.c +++ b/postpone.c @@ -31,7 +31,6 @@ #include "mutt.h" #include "handler.h" -#include "rfc1524.h" #include "sort.h" #include "thread.h" #include "mx.h" diff --git a/recvattach.c b/recvattach.c index d9b6d61..bc59c76 100644 --- a/recvattach.c +++ b/recvattach.c @@ -28,7 +28,6 @@ #include "mutt.h" #include "handler.h" #include "recvattach.h" -#include "rfc1524.h" #include "attach.h" #include "mx.h" #include "copy.h" diff --git a/recvcmd.c b/recvcmd.c index 821dfbb..79f42d0 100644 --- a/recvcmd.c +++ b/recvcmd.c @@ -28,7 +28,6 @@ #include "state.h" #include "handler.h" #include "recvattach.h" -#include "rfc1524.h" #include "attach.h" #include "mx.h" #include "copy.h" diff --git a/rfc1524.c b/rfc1524.c deleted file mode 100644 index bef1144..0000000 --- a/rfc1524.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright notice from original mutt: - * Copyright (C) 1996-2000 Michael R. Elkins - * - * This file is part of mutt-ng, see http://www.muttng.org/. - * It's licensed under the GNU General Public License, - * please see the file GPL in the top level source directory. - */ - -/* - * rfc1524 defines a format for the Multimedia Mail Configuration, which - * is the standard mailcap file format under Unix which specifies what - * external programs should be used to view/compose/edit multimedia files - * based on content type. - * - * This file contains various functions for implementing a fair subset of - * rfc1524. - */ - -#if HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "mutt.h" -#include "rfc1524.h" -#include "attach.h" - -/* The command semantics include the following: - * %s is the filename that contains the mail body data - * %t is the content type, like text/plain - * %{parameter} is replaced by the parameter value from the content-type field - * \% is % - * Unsupported rfc1524 parameters: these would probably require some doing - * by mutt, and can probably just be done by piping the message to metamail - * %n is the integer number of sub-parts in the multipart - * %F is "content-type filename" repeated for each sub-part - * - * In addition, this function returns a 0 if the command works on a file, - * and 1 if the command works on a pipe. - */ -int rfc1524_expand_command (BODY * a, char *filename, char *_type, - char *command, int clen) -{ - int x = 0, y = 0; - int needspipe = TRUE; - char buf[LONG_STRING]; - char type[LONG_STRING]; - - m_strcpy(type, sizeof(type), _type); - - if (option (OPTMAILCAPSANITIZE)) - mutt_sanitize_filename (type, 0); - - while (command[x] && x < clen && y < ssizeof (buf)) { - if (command[x] == '\\') { - x++; - buf[y++] = command[x++]; - } - else if (command[x] == '%') { - x++; - if (command[x] == '{') { - char param[STRING]; - char pvalue[STRING]; - char *_pvalue; - int z = 0; - - x++; - while (command[x] && command[x] != '}' && z < ssizeof (param)) - param[z++] = command[x++]; - param[z] = '\0'; - - _pvalue = mutt_get_parameter (param, a->parameter); - m_strcpy(pvalue, sizeof(pvalue), NONULL(_pvalue)); - if (option (OPTMAILCAPSANITIZE)) - mutt_sanitize_filename (pvalue, 0); - - y += mutt_quote_filename (buf + y, sizeof (buf) - y, pvalue); - } - else if (command[x] == 's' && filename != NULL) { - y += mutt_quote_filename (buf + y, sizeof (buf) - y, filename); - needspipe = FALSE; - } - else if (command[x] == 't') { - y += mutt_quote_filename (buf + y, sizeof (buf) - y, type); - } - x++; - } - else - buf[y++] = command[x++]; - } - buf[y] = '\0'; - m_strcpy(command, clen, buf); - - return needspipe; -} - -/* NUL terminates a rfc 1524 field, - * returns start of next field or NULL */ -static char *get_field (char *s) -{ - char *ch; - - if (!s) - return NULL; - - while ((ch = strpbrk (s, ";\\")) != NULL) { - if (*ch == '\\') { - s = ch + 1; - if (*s) - s++; - } - else { - *ch++ = '\0'; - ch = vskipspaces(ch); - break; - } - } - m_strrtrim(s); - return ch; -} - -static int get_field_text (char *field, char **entry, - char *type, char *filename, int line) -{ - field = vskipspaces(field); - if (*field == '=') { - if (entry) { - field = vskipspaces(field + 1); - m_strreplace(entry, field); - } - return 1; - } - else { - mutt_error (_("Improperly formated entry for type %s in \"%s\" line %d"), - type, filename, line); - return 0; - } -} - -static int rfc1524_mailcap_parse (BODY * a, - char *filename, - char *type, rfc1524_entry * entry, int opt) -{ - FILE *fp; - char *buf = NULL; - ssize_t buflen; - char *ch; - char *field; - int found = FALSE; - int copiousoutput; - int composecommand; - int editcommand; - int printcommand; - int btlen; - int line = 0; - - /* rfc1524 mailcap file is of the format: - * base/type; command; extradefs - * type can be * for matching all - * base with no /type is an implicit wild - * command contains a %s for the filename to pass, default to pipe on stdin - * extradefs are of the form: - * def1="definition"; def2="define \;"; - * line wraps with a \ at the end of the line - * # for comments - */ - - /* find length of basetype */ - if ((ch = strchr (type, '/')) == NULL) - return FALSE; - btlen = ch - type; - - if ((fp = fopen (filename, "r")) != NULL) { - while (!found && (buf = mutt_read_line(buf, &buflen, fp, &line)) != NULL) { - /* ignore comments */ - if (*buf == '#') - continue; - - /* check type */ - ch = get_field (buf); - if (ascii_strcasecmp (buf, type) && (ascii_strncasecmp (buf, type, btlen) || (buf[btlen] != 0 && /* implicit wild */ - m_strcmp(buf + btlen, "/*")))) /* wildsubtype */ - continue; - - /* next field is the viewcommand */ - field = ch; - ch = get_field (ch); - if (entry) - entry->command = m_strdup(field); - - /* parse the optional fields */ - found = TRUE; - copiousoutput = FALSE; - composecommand = FALSE; - editcommand = FALSE; - printcommand = FALSE; - - while (ch) { - field = ch; - ch = get_field (ch); - - if (!ascii_strcasecmp (field, "needsterminal")) { - if (entry) - entry->needsterminal = TRUE; - } - else if (!ascii_strcasecmp (field, "copiousoutput")) { - copiousoutput = TRUE; - if (entry) - entry->copiousoutput = TRUE; - } - else if (!ascii_strncasecmp (field, "composetyped", 12)) { - /* this compare most occur before compose to match correctly */ - if (get_field_text - (field + 12, entry ? &entry->composetypecommand : NULL, type, - filename, line)) - composecommand = TRUE; - } - else if (!ascii_strncasecmp (field, "compose", 7)) { - if (get_field_text - (field + 7, entry ? &entry->composecommand : NULL, type, - filename, line)) - composecommand = TRUE; - } - else if (!ascii_strncasecmp (field, "print", 5)) { - if (get_field_text (field + 5, entry ? &entry->printcommand : NULL, - type, filename, line)) - printcommand = TRUE; - } - else if (!ascii_strncasecmp (field, "edit", 4)) { - if (get_field_text (field + 4, entry ? &entry->editcommand : NULL, - type, filename, line)) - editcommand = TRUE; - } - else if (!ascii_strncasecmp (field, "nametemplate", 12)) { - get_field_text (field + 12, entry ? &entry->nametemplate : NULL, - type, filename, line); - } - else if (!ascii_strncasecmp (field, "x-convert", 9)) { - get_field_text (field + 9, entry ? &entry->convert : NULL, - type, filename, line); - } - else if (!ascii_strncasecmp (field, "test", 4)) { - /* - * This routine executes the given test command to determine - * if this is the right entry. - */ - char *test_command = NULL; - ssize_t len; - - if (get_field_text (field + 4, &test_command, type, filename, line) - && test_command) { - len = m_strlen(test_command) + STRING; - p_realloc(&test_command, len); - rfc1524_expand_command (a, a->filename, type, test_command, len); - if (mutt_system (test_command)) { - /* a non-zero exit code means test failed */ - found = FALSE; - } - p_delete(&test_command); - } - } - } /* while (ch) */ - - if (opt == M_AUTOVIEW) { - if (!copiousoutput) - found = FALSE; - } - else if (opt == M_COMPOSE) { - if (!composecommand) - found = FALSE; - } - else if (opt == M_EDIT) { - if (!editcommand) - found = FALSE; - } - else if (opt == M_PRINT) { - if (!printcommand) - found = FALSE; - } - - if (!found) { - /* reset */ - if (entry) { - p_delete(&entry->command); - p_delete(&entry->composecommand); - p_delete(&entry->composetypecommand); - p_delete(&entry->editcommand); - p_delete(&entry->printcommand); - p_delete(&entry->nametemplate); - p_delete(&entry->convert); - entry->needsterminal = 0; - entry->copiousoutput = 0; - } - } - } /* while (!found && (buf = mutt_read_line ())) */ - fclose (fp); - } /* if ((fp = fopen ())) */ - p_delete(&buf); - return found; -} - -rfc1524_entry *rfc1524_new_entry (void) -{ - return p_new(rfc1524_entry, 1); -} - -void rfc1524_free_entry (rfc1524_entry ** entry) -{ - rfc1524_entry *p = *entry; - - p_delete(&p->command); - p_delete(&p->testcommand); - p_delete(&p->composecommand); - p_delete(&p->composetypecommand); - p_delete(&p->editcommand); - p_delete(&p->printcommand); - p_delete(&p->nametemplate); - p_delete(entry); -} - -/* - * rfc1524_mailcap_lookup attempts to find the given type in the - * list of mailcap files. On success, this returns the entry information - * in *entry, and returns 1. On failure (not found), returns 0. - * If entry == NULL just return 1 if the given type is found. - */ -int rfc1524_mailcap_lookup (BODY * a, char *type, rfc1524_entry * entry, - int opt) -{ - char path[_POSIX_PATH_MAX]; - int x; - int found = FALSE; - char *curr = MailcapPath; - - /* rfc1524 specifies that a path of mailcap files should be searched. - * joy. They say - * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc - * and overriden by the MAILCAPS environment variable, and, just to be nice, - * we'll make it specifiable in .muttrc - */ - if (!curr || !*curr) { - mutt_error _("No mailcap path specified"); - - return 0; - } - - mutt_check_lookup_list (a, type, SHORT_STRING); - - while (!found && *curr) { - x = 0; - while (*curr && *curr != ':' && x < ssizeof (path) - 1) { - path[x++] = *curr; - curr++; - } - if (*curr) - curr++; - - if (!x) - continue; - - path[x] = '\0'; - mutt_expand_path (path, sizeof (path)); - - found = rfc1524_mailcap_parse (a, path, type, entry, opt); - } - - if (entry && !found) - mutt_error (_("mailcap entry for type %s not found"), type); - - return found; -} - - -/* This routine will create a _temporary_ filename matching the - * name template given if this needs to be done. - * - * Please note that only the last path element of the - * template and/or the old file name will be used for the - * comparison and the temporary file name. - * - * Returns 0 if oldfile is fine as is. - * Returns 1 if newfile specified - */ - -int rfc1524_expand_filename (char *nametemplate, - char *oldfile, char *newfile, ssize_t nflen) -{ - int i, j, k, ps, r; - char *s; - short lmatch = 0, rmatch = 0; - char left[_POSIX_PATH_MAX]; - char right[_POSIX_PATH_MAX]; - - newfile[0] = 0; - - /* first, ignore leading path components. - */ - - if (nametemplate && (s = strrchr (nametemplate, '/'))) - nametemplate = s + 1; - - if (oldfile && (s = strrchr (oldfile, '/'))) - oldfile = s + 1; - - if (!nametemplate) { - if (oldfile) - m_strcpy(newfile, nflen, oldfile); - } - else if (!oldfile) { - mutt_expand_fmt (newfile, nflen, nametemplate, "mutt"); - } - else { /* oldfile && nametemplate */ - - - /* first, compare everything left from the "%s" - * (if there is one). - */ - - lmatch = 1; - ps = 0; - for (i = 0; nametemplate[i]; i++) { - if (nametemplate[i] == '%' && nametemplate[i + 1] == 's') { - ps = 1; - break; - } - - /* note that the following will _not_ read beyond oldfile's end. */ - - if (lmatch && nametemplate[i] != oldfile[i]) - lmatch = 0; - } - - if (ps) { - - /* If we had a "%s", check the rest. */ - - /* now, for the right part: compare everything right from - * the "%s" to the final part of oldfile. - * - * The logic here is as follows: - * - * - We start reading from the end. - * - There must be a match _right_ from the "%s", - * thus the i + 2. - * - If there was a left hand match, this stuff - * must not be counted again. That's done by the - * condition (j >= (lmatch ? i : 0)). - */ - - rmatch = 1; - - for (r = 0, j = m_strlen(oldfile) - 1, k = - m_strlen(nametemplate) - 1; - j >= (lmatch ? i : 0) && k >= i + 2; j--, k--) { - if (nametemplate[k] != oldfile[j]) { - rmatch = 0; - break; - } - } - - /* Now, check if we had a full match. */ - - if (k >= i + 2) - rmatch = 0; - - if (lmatch) - *left = 0; - else - m_strncpy(left, sizeof(left), nametemplate, i); - - if (rmatch) - *right = 0; - else - m_strcpy(right, sizeof(right), nametemplate + i + 2); - - snprintf (newfile, nflen, "%s%s%s", left, oldfile, right); - } - else { - /* no "%s" in the name template. */ - m_strcpy(newfile, nflen, nametemplate); - } - } - - mutt_adv_mktemp (NULL, newfile, nflen); - - if (rmatch && lmatch) - return 0; - else - return 1; - -} - -/* If rfc1524_expand_command() is used on a recv'd message, then - * the filename doesn't exist yet, but if its used while sending a message, - * then we need to rename the existing file. - * - * This function returns 0 on successful move, 1 on old file doesn't exist, - * 2 on new file already exists, and 3 on other failure. - */ - -/* note on access(2) use: No dangling symlink problems here due to - * safe_fopen(). - */ - -int _mutt_rename_file (char *oldfile, char *newfile, int overwrite) -{ - FILE *ofp, *nfp; - - if (access (oldfile, F_OK) != 0) - return 1; - if (!overwrite && access (newfile, F_OK) == 0) - return 2; - if ((ofp = fopen (oldfile, "r")) == NULL) - return 3; - if ((nfp = safe_fopen (newfile, "w")) == NULL) { - fclose (ofp); - return 3; - } - mutt_copy_stream (ofp, nfp); - fclose (nfp); - fclose (ofp); - mutt_unlink (oldfile); - return 0; -} - -int mutt_rename_file (char *oldfile, char *newfile) -{ - return _mutt_rename_file (oldfile, newfile, 0); -} diff --git a/rfc1524.h b/rfc1524.h deleted file mode 100644 index a0156d7..0000000 --- a/rfc1524.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright notice from original mutt: - * Copyright (C) 1996-2000 Michael R. Elkins - * - * This file is part of mutt-ng, see http://www.muttng.org/. - * It's licensed under the GNU General Public License, - * please see the file GPL in the top level source directory. - */ - -#ifndef _RFC1524_H -#define _RFC1524_H - -typedef struct rfc1524_mailcap_entry { - /* char *contenttype; *//* we don't need this, as we search for it */ - char *command; - char *testcommand; - char *composecommand; - char *composetypecommand; - char *editcommand; - char *printcommand; - char *nametemplate; - char *convert; - /* char *description; *//* we don't need this */ - unsigned int needsterminal:1; /* endwin() and system */ - unsigned int copiousoutput:1; /* needs pager, basically */ -} rfc1524_entry; - -rfc1524_entry *rfc1524_new_entry (void); -void rfc1524_free_entry (rfc1524_entry **); -int rfc1524_expand_command (BODY *, char *, char *, char *, int); -int rfc1524_expand_filename (char *, char *, char *, ssize_t); -int rfc1524_mailcap_lookup (BODY *, char *, rfc1524_entry *, int); -int mutt_rename_file (char *, char *); -int _mutt_rename_file (char *, char *, int); - -#endif /* _RFC1524_H */