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
38 #include <lib-lib/lib-lib.h>
40 #include <lib-sys/unix.h>
46 void rfc1524_entry_wipe(rfc1524_entry *p)
48 p_delete(&p->command);
49 p_delete(&p->testcommand);
50 p_delete(&p->composecommand);
51 p_delete(&p->composetypecommand);
52 p_delete(&p->editcommand);
53 p_delete(&p->printcommand);
54 p_delete(&p->nametemplate);
55 p_delete(&p->convert);
58 /* returns 1 if Mutt can't display this type of data, 0 otherwise */
59 int rfc1524_mailcap_isneeded(BODY * m)
67 return !(mutt_is_application_pgp(m) || mutt_is_application_smime(m));
70 switch (mime_which_token(m->subtype, -1)) {
72 case MIME_RFC822_HEADERS:
83 /* The command semantics include the following:
84 * %s is the filename that contains the mail body data
85 * %t is the content type, like text/plain
86 * %{parameter} is replaced by the parameter value from the content-type field
88 * Unsupported rfc1524 parameters: these would probably require some doing
89 * by mutt, and can probably just be done by piping the message to metamail
90 * %n is the integer number of sub-parts in the multipart
91 * %F is "content-type filename" repeated for each sub-part
93 * In addition, this function returns a 0 if the command works on a file,
94 * and 1 if the command works on a pipe.
96 int rfc1524_expand_command(BODY *a, const char *filename, const char *mtype,
97 char *command, int clen)
100 int needspipe = TRUE;
101 char buf[LONG_STRING];
102 char type[LONG_STRING];
104 m_strcpy(type, sizeof(type), mtype);
106 if (Mime.mailcap_sanitize)
107 mutt_sanitize_filename(type, 0);
109 while (command[x] && x < clen && y < ssizeof(buf)) {
110 switch (command[x]) {
113 buf[y++] = command[x++];
118 if (command[x] == '{') {
124 while (command[x] && command[x] != '}' && z < ssizeof (param))
125 param[z++] = command[x++];
128 m_strcpy(pval, sizeof(pval),
129 parameter_getval(a->parameter, param));
131 if (Mime.mailcap_sanitize)
132 mutt_sanitize_filename(pval, 0);
134 y += mutt_quote_filename(buf + y, sizeof(buf) - y, pval);
137 if (command[x] == 's' && filename) {
138 y += mutt_quote_filename(buf + y, sizeof(buf) - y, filename);
142 if (command[x] == 't') {
143 y += mutt_quote_filename(buf + y, sizeof(buf) - y, type);
149 buf[y++] = command[x++];
155 m_strcpy(command, clen, buf);
160 static char *parse_field(char *p, char **field, char **value)
176 p += 1 + (p[1] != 0);
182 p = *value = vskipspaces(p);
195 parse_field_error(const char *type, const char *filename, int line)
197 mutt_error(_("Improperly formated entry for type %s in \"%s\" line %d"),
198 type, filename, line);
201 /* rfc1524 mailcap file is of the format:
202 * base/type; command; extradefs
203 * type can be * for matching all
204 * base with no /type is an implicit wild
205 * command contains a %s for the filename to pass, default to pipe on stdin
206 * extradefs are of the form:
207 * def1="definition"; def2="define \;";
208 * line wraps with a \ at the end of the line
212 rfc1524_mailcap_parse(BODY *a, const char *filename, const char *type,
213 rfc1524_entry *entry, int opt)
228 /* find length of basetype */
229 if ((ch = strchr (type, '/')) == NULL)
233 fp = fopen(filename, "r");
237 while (!found && (buf = mutt_read_line(buf, &buflen, fp, &line)) != NULL) {
238 /* ignore comments */
243 ch = parse_field(buf, &field, &value);
244 if (ascii_strcasecmp(field, type)
245 && (ascii_strncasecmp(field, type, btlen)
246 || (buf[btlen] != 0 && m_strcmp(buf + btlen, "/*"))))
249 /* next field is the viewcommand */
250 ch = parse_field(ch, &field, &value);
252 entry->command = m_strdup(field);
254 /* parse the optional fields */
256 copiousoutput = FALSE;
257 composecommand = FALSE;
259 printcommand = FALSE;
262 ch = parse_field(ch, &field, &value);
264 switch (mime_which_token(field, -1)) {
265 #define DO_CASE(token, field, expr) \
269 m_strreplace(&entry->field, value); \
272 parse_field_error(type, filename, line); \
276 case MIME_NEEDSTERMINAL:
278 entry->needsterminal = TRUE;
281 case MIME_COPIOUSOUTPUT:
282 copiousoutput = TRUE;
284 entry->copiousoutput = TRUE;
287 DO_CASE(MIME_COMPOSETYPED, composecommand, composecommand = TRUE);
288 DO_CASE(MIME_COMPOSE, composecommand, composecommand = TRUE);
289 DO_CASE(MIME_PRINT, printcommand, printcommand = TRUE);
290 DO_CASE(MIME_EDIT, editcommand, editcommand = TRUE);
291 DO_CASE(MIME_NAMETEMPLATE, nametemplate, );
292 DO_CASE(MIME_X_CONVERT, convert, );
296 * This routine executes the given test command to determine
297 * if this is the right entry. a non-zero exit code means
301 ssize_t len = m_strlen(value) + STRING;
302 char *testcmd = p_new(char, len);
304 m_strcpy(testcmd, len, value);
305 rfc1524_expand_command(a, a->filename, type, testcmd, len);
306 found = !mutt_system(testcmd);
309 parse_field_error(type, filename, line);
319 if (opt == M_AUTOVIEW) {
323 else if (opt == M_COMPOSE) {
327 else if (opt == M_EDIT) {
331 else if (opt == M_PRINT) {
336 if (!found && entry) {
337 rfc1524_entry_wipe(entry);
338 rfc1524_entry_init(entry);
340 } /* while (!found && (buf = mutt_read_line ())) */
350 * rfc1524_mailcap_lookup attempts to find the given type in the
351 * list of mailcap files. On success, this returns the entry information
352 * in *entry, and returns 1. On failure (not found), returns 0.
353 * If entry == NULL just return 1 if the given type is found.
355 int rfc1524_mailcap_lookup(BODY *a, char *type, rfc1524_entry *entry, int opt)
357 const char *p = Mime.mailcap_path;
359 mutt_check_lookup_list(a, type, STRING);
363 char path[_POSIX_PATH_MAX];
371 q = strchrnul(p, ':');
372 m_strncpy(path, sizeof(path), p, q - p);
373 mutt_expand_path(path, sizeof(path));
375 if (rfc1524_mailcap_parse(a, path, type, entry, opt))
382 mutt_error(_("mailcap entry for type %s not found"), type);
387 /************** READ MARK **********************/
390 /* This routine will create a _temporary_ filename matching the
391 * name template given if this needs to be done.
393 * Please note that only the last path element of the
394 * template and/or the old file name will be used for the
395 * comparison and the temporary file name.
397 * Returns 0 if oldfile is fine as is.
398 * Returns 1 if newfile specified
401 int rfc1524_expand_filename (char *nametemplate,
402 char *oldfile, char *newfile, ssize_t nflen)
406 short lmatch = 0, rmatch = 0;
407 char left[_POSIX_PATH_MAX];
408 char right[_POSIX_PATH_MAX];
412 /* first, ignore leading path components.
415 if (nametemplate && (s = strrchr (nametemplate, '/')))
416 nametemplate = s + 1;
418 if (oldfile && (s = strrchr (oldfile, '/')))
423 m_strcpy(newfile, nflen, oldfile);
426 m_file_fmt(newfile, nflen, nametemplate, "mutt");
427 } else { /* oldfile && nametemplate */
430 /* first, compare everything left from the "%s"
436 for (i = 0; nametemplate[i]; i++) {
437 if (nametemplate[i] == '%' && nametemplate[i + 1] == 's') {
442 /* note that the following will _not_ read beyond oldfile's end. */
444 if (lmatch && nametemplate[i] != oldfile[i])
450 /* If we had a "%s", check the rest. */
452 /* now, for the right part: compare everything right from
453 * the "%s" to the final part of oldfile.
455 * The logic here is as follows:
457 * - We start reading from the end.
458 * - There must be a match _right_ from the "%s",
460 * - If there was a left hand match, this stuff
461 * must not be counted again. That's done by the
462 * condition (j >= (lmatch ? i : 0)).
467 for (r = 0, j = m_strlen(oldfile) - 1, k =
468 m_strlen(nametemplate) - 1;
469 j >= (lmatch ? i : 0) && k >= i + 2; j--, k--) {
470 if (nametemplate[k] != oldfile[j]) {
476 /* Now, check if we had a full match. */
484 m_strncpy(left, sizeof(left), nametemplate, i);
489 m_strcpy(right, sizeof(right), nametemplate + i + 2);
491 snprintf (newfile, nflen, "%s%s%s", left, oldfile, right);
494 /* no "%s" in the name template. */
495 m_strcpy(newfile, nflen, nametemplate);
499 mutt_adv_mktemp (NULL, newfile, nflen);
501 return !(rmatch && lmatch);