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/lib-lib.h>
53 #include <lib-sys/unix.h>
59 void rfc1524_entry_wipe(rfc1524_entry *p)
61 p_delete(&p->command);
62 p_delete(&p->testcommand);
63 p_delete(&p->composecommand);
64 p_delete(&p->composetypecommand);
65 p_delete(&p->editcommand);
66 p_delete(&p->printcommand);
67 p_delete(&p->nametemplate);
68 p_delete(&p->convert);
71 /* returns 1 if Mutt can't display this type of data, 0 otherwise */
72 int rfc1524_mailcap_isneeded(BODY * m)
82 return !(mutt_is_application_pgp(m) || mutt_is_application_smime(m));
85 tok = mime_which_token(m->subtype, -1);
87 || tok == MIME_RFC822_HEADERS
88 || tok == MIME_ENRICHED)
96 /* The command semantics include the following:
97 * %s is the filename that contains the mail body data
98 * %t is the content type, like text/plain
99 * %{parameter} is replaced by the parameter value from the content-type field
101 * Unsupported rfc1524 parameters: these would probably require some doing
102 * by mutt, and can probably just be done by piping the message to metamail
103 * %n is the integer number of sub-parts in the multipart
104 * %F is "content-type filename" repeated for each sub-part
106 * In addition, this function returns a 0 if the command works on a file,
107 * and 1 if the command works on a pipe.
109 int rfc1524_expand_command(BODY *a, const char *filename, const char *mtype,
110 char *command, int clen)
113 int needspipe = TRUE;
114 char buf[LONG_STRING];
115 char type[LONG_STRING];
117 m_strcpy(type, sizeof(type), mtype);
119 if (option(OPTMAILCAPSANITIZE))
120 mutt_sanitize_filename(type, 0);
122 while (command[x] && x < clen && y < ssizeof(buf)) {
123 switch (command[x]) {
126 buf[y++] = command[x++];
131 if (command[x] == '{') {
137 while (command[x] && command[x] != '}' && z < ssizeof (param))
138 param[z++] = command[x++];
141 m_strcpy(pval, sizeof(pval),
142 parameter_getval(a->parameter, param));
144 if (option(OPTMAILCAPSANITIZE))
145 mutt_sanitize_filename(pval, 0);
147 y += mutt_quote_filename(buf + y, sizeof(buf) - y, pval);
150 if (command[x] == 's' && filename) {
151 y += mutt_quote_filename(buf + y, sizeof(buf) - y, filename);
155 if (command[x] == 't') {
156 y += mutt_quote_filename(buf + y, sizeof(buf) - y, type);
162 buf[y++] = command[x++];
168 m_strcpy(command, clen, buf);
173 static char *parse_field(char *p, char **field, char **value)
189 p += 1 + (p[1] != 0);
195 p = *value = vskipspaces(p);
208 parse_field_error(const char *type, const char *filename, int line)
210 mutt_error(_("Improperly formated entry for type %s in \"%s\" line %d"),
211 type, filename, line);
214 /* rfc1524 mailcap file is of the format:
215 * base/type; command; extradefs
216 * type can be * for matching all
217 * base with no /type is an implicit wild
218 * command contains a %s for the filename to pass, default to pipe on stdin
219 * extradefs are of the form:
220 * def1="definition"; def2="define \;";
221 * line wraps with a \ at the end of the line
225 rfc1524_mailcap_parse(BODY *a, const char *filename, const char *type,
226 rfc1524_entry *entry, int opt)
241 /* find length of basetype */
242 if ((ch = strchr (type, '/')) == NULL)
246 fp = fopen(filename, "r");
250 while (!found && (buf = mutt_read_line(buf, &buflen, fp, &line)) != NULL) {
251 /* ignore comments */
256 ch = parse_field(buf, &field, &value);
257 if (ascii_strcasecmp(field, type)
258 && (ascii_strncasecmp(field, type, btlen)
259 || (buf[btlen] != 0 && m_strcmp(buf + btlen, "/*"))))
262 /* next field is the viewcommand */
263 ch = parse_field(ch, &field, &value);
265 entry->command = m_strdup(field);
267 /* parse the optional fields */
269 copiousoutput = FALSE;
270 composecommand = FALSE;
272 printcommand = FALSE;
275 ch = parse_field(ch, &field, &value);
277 switch (mime_which_token(field, -1)) {
278 #define DO_CASE(token, field, expr) \
282 m_strreplace(&entry->field, value); \
285 parse_field_error(type, filename, line); \
289 case MIME_NEEDSTERMINAL:
291 entry->needsterminal = TRUE;
294 case MIME_COPIOUSOUTPUT:
295 copiousoutput = TRUE;
297 entry->copiousoutput = TRUE;
300 DO_CASE(MIME_COMPOSETYPED, composecommand, composecommand = TRUE);
301 DO_CASE(MIME_COMPOSE, composecommand, composecommand = TRUE);
302 DO_CASE(MIME_PRINT, printcommand, printcommand = TRUE);
303 DO_CASE(MIME_EDIT, editcommand, editcommand = TRUE);
304 DO_CASE(MIME_NAMETEMPLATE, nametemplate, );
305 DO_CASE(MIME_X_CONVERT, convert, );
309 * This routine executes the given test command to determine
310 * if this is the right entry. a non-zero exit code means
314 ssize_t len = m_strlen(value) + STRING;
315 char *testcmd = p_new(char, len);
317 strcpy(testcmd, value);
318 rfc1524_expand_command(a, a->filename, type, testcmd, len);
319 found = !mutt_system(testcmd);
322 parse_field_error(type, filename, line);
332 if (opt == M_AUTOVIEW) {
336 else if (opt == M_COMPOSE) {
340 else if (opt == M_EDIT) {
344 else if (opt == M_PRINT) {
349 if (!found && entry) {
350 rfc1524_entry_wipe(entry);
351 rfc1524_entry_init(entry);
353 } /* while (!found && (buf = mutt_read_line ())) */
362 /************** READ MARK **********************/
365 * rfc1524_mailcap_lookup attempts to find the given type in the
366 * list of mailcap files. On success, this returns the entry information
367 * in *entry, and returns 1. On failure (not found), returns 0.
368 * If entry == NULL just return 1 if the given type is found.
370 int rfc1524_mailcap_lookup (BODY * a, char *type, rfc1524_entry * entry,
373 char path[_POSIX_PATH_MAX];
376 char *curr = MailcapPath;
378 /* rfc1524 specifies that a path of mailcap files should be searched.
380 * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc
381 * and overriden by the MAILCAPS environment variable, and, just to be nice,
382 * we'll make it specifiable in .muttrc
384 if (!curr || !*curr) {
385 mutt_error _("No mailcap path specified");
390 mutt_check_lookup_list (a, type, SHORT_STRING);
392 while (!found && *curr) {
394 while (*curr && *curr != ':' && x < ssizeof (path) - 1) {
405 mutt_expand_path (path, sizeof (path));
407 found = rfc1524_mailcap_parse (a, path, type, entry, opt);
411 mutt_error (_("mailcap entry for type %s not found"), type);
417 /* This routine will create a _temporary_ filename matching the
418 * name template given if this needs to be done.
420 * Please note that only the last path element of the
421 * template and/or the old file name will be used for the
422 * comparison and the temporary file name.
424 * Returns 0 if oldfile is fine as is.
425 * Returns 1 if newfile specified
428 int rfc1524_expand_filename (char *nametemplate,
429 char *oldfile, char *newfile, ssize_t nflen)
433 short lmatch = 0, rmatch = 0;
434 char left[_POSIX_PATH_MAX];
435 char right[_POSIX_PATH_MAX];
439 /* first, ignore leading path components.
442 if (nametemplate && (s = strrchr (nametemplate, '/')))
443 nametemplate = s + 1;
445 if (oldfile && (s = strrchr (oldfile, '/')))
450 m_strcpy(newfile, nflen, oldfile);
453 mutt_expand_fmt (newfile, nflen, nametemplate, "mutt");
455 else { /* oldfile && nametemplate */
458 /* first, compare everything left from the "%s"
464 for (i = 0; nametemplate[i]; i++) {
465 if (nametemplate[i] == '%' && nametemplate[i + 1] == 's') {
470 /* note that the following will _not_ read beyond oldfile's end. */
472 if (lmatch && nametemplate[i] != oldfile[i])
478 /* If we had a "%s", check the rest. */
480 /* now, for the right part: compare everything right from
481 * the "%s" to the final part of oldfile.
483 * The logic here is as follows:
485 * - We start reading from the end.
486 * - There must be a match _right_ from the "%s",
488 * - If there was a left hand match, this stuff
489 * must not be counted again. That's done by the
490 * condition (j >= (lmatch ? i : 0)).
495 for (r = 0, j = m_strlen(oldfile) - 1, k =
496 m_strlen(nametemplate) - 1;
497 j >= (lmatch ? i : 0) && k >= i + 2; j--, k--) {
498 if (nametemplate[k] != oldfile[j]) {
504 /* Now, check if we had a full match. */
512 m_strncpy(left, sizeof(left), nametemplate, i);
517 m_strcpy(right, sizeof(right), nametemplate + i + 2);
519 snprintf (newfile, nflen, "%s%s%s", left, oldfile, right);
522 /* no "%s" in the name template. */
523 m_strcpy(newfile, nflen, nametemplate);
527 mutt_adv_mktemp (NULL, newfile, nflen);
529 return !(rmatch && lmatch);