2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * Copyright © 2006 Pierre Habouzit
20 * Copyright notice from original mutt:
21 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
23 * This file is part of mutt-ng, see http://www.muttng.org/.
24 * It's licensed under the GNU General Public License,
25 * please see the file GPL in the top level source directory.
29 * rfc1524 defines a format for the Multimedia Mail Configuration, which
30 * is the standard mailcap file format under Unix which specifies what
31 * external programs should be used to view/compose/edit multimedia files
32 * based on content type.
34 * This file contains various functions for implementing a fair subset of
51 #include <lib-lib/mem.h>
52 #include <lib-lib/str.h>
53 #include <lib-lib/ascii.h>
54 #include <lib-lib/macros.h>
55 #include <lib-lib/file.h>
57 #include <lib-sys/unix.h>
63 /* The command semantics include the following:
64 * %s is the filename that contains the mail body data
65 * %t is the content type, like text/plain
66 * %{parameter} is replaced by the parameter value from the content-type field
68 * Unsupported rfc1524 parameters: these would probably require some doing
69 * by mutt, and can probably just be done by piping the message to metamail
70 * %n is the integer number of sub-parts in the multipart
71 * %F is "content-type filename" repeated for each sub-part
73 * In addition, this function returns a 0 if the command works on a file,
74 * and 1 if the command works on a pipe.
76 int rfc1524_expand_command(BODY *a, const char *filename, const char *mtype,
77 char *command, int clen)
81 char buf[LONG_STRING];
82 char type[LONG_STRING];
84 m_strcpy(type, sizeof(type), mtype);
86 if (option(OPTMAILCAPSANITIZE))
87 mutt_sanitize_filename(type, 0);
89 while (command[x] && x < clen && y < ssizeof(buf)) {
93 buf[y++] = command[x++];
98 if (command[x] == '{') {
104 while (command[x] && command[x] != '}' && z < ssizeof (param))
105 param[z++] = command[x++];
108 m_strcpy(pval, sizeof(pval),
109 mutt_get_parameter(param, a->parameter));
111 if (option(OPTMAILCAPSANITIZE))
112 mutt_sanitize_filename(pval, 0);
114 y += mutt_quote_filename(buf + y, sizeof(buf) - y, pval);
117 if (command[x] == 's' && filename) {
118 y += mutt_quote_filename(buf + y, sizeof(buf) - y, filename);
122 if (command[x] == 't') {
123 y += mutt_quote_filename(buf + y, sizeof(buf) - y, type);
129 buf[y++] = command[x++];
135 m_strcpy(command, clen, buf);
140 static char *parse_field(char *p, char **field, char **value)
156 p += 1 + (p[1] != 0);
162 p = *value = vskipspaces(p);
175 parse_field_error(const char *type, const char *filename, int line)
177 mutt_error(_("Improperly formated entry for type %s in \"%s\" line %d"),
178 type, filename, line);
181 /* rfc1524 mailcap file is of the format:
182 * base/type; command; extradefs
183 * type can be * for matching all
184 * base with no /type is an implicit wild
185 * command contains a %s for the filename to pass, default to pipe on stdin
186 * extradefs are of the form:
187 * def1="definition"; def2="define \;";
188 * line wraps with a \ at the end of the line
192 rfc1524_mailcap_parse(BODY *a, const char *filename, const char *type,
193 rfc1524_entry *entry, int opt)
208 /* find length of basetype */
209 if ((ch = strchr (type, '/')) == NULL)
213 fp = fopen(filename, "r");
217 while (!found && (buf = mutt_read_line(buf, &buflen, fp, &line)) != NULL) {
218 /* ignore comments */
223 ch = parse_field(buf, &field, &value);
224 if (ascii_strcasecmp(field, type)
225 && (ascii_strncasecmp(field, type, btlen)
226 || (buf[btlen] != 0 && m_strcmp(buf + btlen, "/*"))))
229 /* next field is the viewcommand */
230 ch = parse_field(ch, &field, &value);
232 entry->command = m_strdup(field);
234 /* parse the optional fields */
236 copiousoutput = FALSE;
237 composecommand = FALSE;
239 printcommand = FALSE;
242 ch = parse_field(ch, &field, &value);
244 switch (mime_which_token(field, -1)) {
245 #define DO_CASE(token, field, expr) \
249 m_strreplace(&entry->field, value); \
252 parse_field_error(type, filename, line); \
256 case MIME_NEEDSTERMINAL:
258 entry->needsterminal = TRUE;
261 case MIME_COPIOUSOUTPUT:
262 copiousoutput = TRUE;
264 entry->copiousoutput = TRUE;
267 DO_CASE(MIME_COMPOSETYPED, composecommand, composecommand = TRUE);
268 DO_CASE(MIME_COMPOSE, composecommand, composecommand = TRUE);
269 DO_CASE(MIME_PRINT, printcommand, printcommand = TRUE);
270 DO_CASE(MIME_EDIT, editcommand, editcommand = TRUE);
271 DO_CASE(MIME_NAMETEMPLATE, nametemplate, );
272 DO_CASE(MIME_X_CONVERT, convert, );
276 * This routine executes the given test command to determine
277 * if this is the right entry. a non-zero exit code means
281 ssize_t len = m_strlen(value) + STRING;
282 char *testcmd = p_new(char, len);
284 strcpy(testcmd, value);
285 rfc1524_expand_command(a, a->filename, type, testcmd, len);
286 found = !mutt_system(testcmd);
289 parse_field_error(type, filename, line);
299 if (opt == M_AUTOVIEW) {
303 else if (opt == M_COMPOSE) {
307 else if (opt == M_EDIT) {
311 else if (opt == M_PRINT) {
316 if (!found && entry) {
317 rfc1524_entry_wipe(entry);
318 rfc1524_entry_init(entry);
320 } /* while (!found && (buf = mutt_read_line ())) */
329 /************** READ MARK **********************/
332 * rfc1524_mailcap_lookup attempts to find the given type in the
333 * list of mailcap files. On success, this returns the entry information
334 * in *entry, and returns 1. On failure (not found), returns 0.
335 * If entry == NULL just return 1 if the given type is found.
337 int rfc1524_mailcap_lookup (BODY * a, char *type, rfc1524_entry * entry,
340 char path[_POSIX_PATH_MAX];
343 char *curr = MailcapPath;
345 /* rfc1524 specifies that a path of mailcap files should be searched.
347 * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc
348 * and overriden by the MAILCAPS environment variable, and, just to be nice,
349 * we'll make it specifiable in .muttrc
351 if (!curr || !*curr) {
352 mutt_error _("No mailcap path specified");
357 mutt_check_lookup_list (a, type, SHORT_STRING);
359 while (!found && *curr) {
361 while (*curr && *curr != ':' && x < ssizeof (path) - 1) {
372 mutt_expand_path (path, sizeof (path));
374 found = rfc1524_mailcap_parse (a, path, type, entry, opt);
378 mutt_error (_("mailcap entry for type %s not found"), type);
384 /* This routine will create a _temporary_ filename matching the
385 * name template given if this needs to be done.
387 * Please note that only the last path element of the
388 * template and/or the old file name will be used for the
389 * comparison and the temporary file name.
391 * Returns 0 if oldfile is fine as is.
392 * Returns 1 if newfile specified
395 int rfc1524_expand_filename (char *nametemplate,
396 char *oldfile, char *newfile, ssize_t nflen)
400 short lmatch = 0, rmatch = 0;
401 char left[_POSIX_PATH_MAX];
402 char right[_POSIX_PATH_MAX];
406 /* first, ignore leading path components.
409 if (nametemplate && (s = strrchr (nametemplate, '/')))
410 nametemplate = s + 1;
412 if (oldfile && (s = strrchr (oldfile, '/')))
417 m_strcpy(newfile, nflen, oldfile);
420 mutt_expand_fmt (newfile, nflen, nametemplate, "mutt");
422 else { /* oldfile && nametemplate */
425 /* first, compare everything left from the "%s"
431 for (i = 0; nametemplate[i]; i++) {
432 if (nametemplate[i] == '%' && nametemplate[i + 1] == 's') {
437 /* note that the following will _not_ read beyond oldfile's end. */
439 if (lmatch && nametemplate[i] != oldfile[i])
445 /* If we had a "%s", check the rest. */
447 /* now, for the right part: compare everything right from
448 * the "%s" to the final part of oldfile.
450 * The logic here is as follows:
452 * - We start reading from the end.
453 * - There must be a match _right_ from the "%s",
455 * - If there was a left hand match, this stuff
456 * must not be counted again. That's done by the
457 * condition (j >= (lmatch ? i : 0)).
462 for (r = 0, j = m_strlen(oldfile) - 1, k =
463 m_strlen(nametemplate) - 1;
464 j >= (lmatch ? i : 0) && k >= i + 2; j--, k--) {
465 if (nametemplate[k] != oldfile[j]) {
471 /* Now, check if we had a full match. */
479 m_strncpy(left, sizeof(left), nametemplate, i);
484 m_strcpy(right, sizeof(right), nametemplate + i + 2);
486 snprintf (newfile, nflen, "%s%s%s", left, oldfile, right);
489 /* no "%s" in the name template. */
490 m_strcpy(newfile, nflen, nametemplate);
494 mutt_adv_mktemp (NULL, newfile, nflen);
496 return !(rmatch && lmatch);