* Do not define _BSD_SOURCE but define _DEFAULT_SOURCE instead.
[alpine.git] / pith / mailcap.c
blobe8d802a35c2b62b47888da9cec5ded0151c39b06
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailcap.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/mailcap.h"
21 #include "../pith/init.h"
22 #include "../pith/conf.h"
23 #include "../pith/mimetype.h"
24 #include "../pith/mimedesc.h"
25 #include "../pith/status.h"
26 #include "../pith/util.h"
27 #include "../pith/readfile.h"
30 * We've decided not to implement the RFC1524 standard minimum path, because
31 * some of us think it is harder to debug a problem when you may be misled
32 * into looking at the wrong mailcap entry. Likewise for MIME.Types files.
34 #if defined(DOS) || defined(OS2)
35 #define MC_PATH_SEPARATOR ';'
36 #define MC_USER_FILE "MAILCAP"
37 #define MC_STDPATH NULL
38 #else /* !DOS */
39 #define MC_PATH_SEPARATOR ':'
40 #define MC_USER_FILE NULL
41 #define MC_STDPATH \
42 ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"
43 #endif /* !DOS */
45 #ifdef _WINDOWS
46 #define MC_ADD_TMP " %s"
47 #else
48 #define MC_ADD_TMP " < %s"
49 #endif
51 typedef struct mcap_entry {
52 struct mcap_entry *next;
53 int needsterminal;
54 char *contenttype;
55 char *command;
56 char *testcommand;
57 char *label; /* unused */
58 char *printcommand; /* unused */
59 } MailcapEntry;
61 struct mailcap_data {
62 MailcapEntry *head, **tail;
63 STRINGLIST *raw;
64 } MailcapData;
66 #define MC_TOKEN_MAX 64
70 * Internal prototypes
72 void mc_init(void);
73 void mc_process_file(char *);
74 void mc_parse_file(char *);
75 int mc_parse_line(char **, char **);
76 int mc_comment(char **);
77 int mc_token(char **, char **);
78 void mc_build_entry(char **);
79 int mc_sane_command(char *);
80 MailcapEntry *mc_get_command(int, char *, BODY *, int, int *);
81 int mc_ctype_match(int, char *, char *);
82 int mc_passes_test(MailcapEntry *, int, char *, BODY *);
83 char *mc_bld_test_cmd(char *, int, char *, BODY *);
84 char *mc_cmd_bldr(char *, int, char *, BODY *, char *, char **);
85 MailcapEntry *mc_new_entry(void);
86 void mc_free_entry(MailcapEntry **);
89 char *
90 mc_conf_path(char *def_path, char *env_path, char *user_file, int separator, char *stdpath)
92 char *path;
94 /* We specify MIMETYPES as a path override */
95 if(def_path)
96 /* there may need to be an override specific to pine */
97 path = cpystr(def_path);
98 else if(env_path)
99 path = cpystr(env_path);
100 else{
101 #if defined(DOS) || defined(OS2)
102 char *s;
105 * This gets interesting. Since we don't have any standard location
106 * for config/data files, look in the same directory as the PINERC
107 * and the same dir as PINE.EXE. This is similar to the UNIX
108 * situation with personal config info coming before
109 * potentially shared config data...
111 if(s = last_cmpnt(ps_global->pinerc)){
112 strncpy(tmp_20k_buf+1000, ps_global->pinerc, MIN(s - ps_global->pinerc,SIZEOF_20KBUF-1000));
113 tmp_20k_buf[1000+MIN(s - ps_global->pinerc,SIZEOF_20KBUF-1000-1)] = '\0';
115 else
116 strncpy(tmp_20k_buf+1000, ".\\", SIZEOF_20KBUF-1000);
118 /* pinerc directory version of file */
119 build_path(tmp_20k_buf+2000, tmp_20k_buf+1000, user_file, SIZEOF_20KBUF-2000);
120 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
122 /* pine.exe directory version of file */
123 build_path(tmp_20k_buf+3000, ps_global->pine_dir, user_file, SIZEOF_20KBUF-3000);
124 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
126 /* combine them */
127 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%c%s", tmp_20k_buf+2000, separator, tmp_20k_buf+3000);
129 #else /* !DOS */
130 build_path(tmp_20k_buf, ps_global->home_dir, stdpath, SIZEOF_20KBUF);
131 #endif /* !DOS */
132 path = cpystr(tmp_20k_buf);
135 return(path);
140 * mc_init - Run down the path gathering all the mailcap entries.
141 * Returns with the Mailcap list built.
143 void
144 mc_init(void)
146 char *s,
147 *pathcopy,
148 *path,
149 image_viewer[MAILTMPLEN];
151 if(MailcapData.raw) /* already have the file? */
152 return;
153 else
154 MailcapData.tail = &MailcapData.head;
156 dprint((5, "- mc_init -\n"));
158 pathcopy = mc_conf_path(ps_global->VAR_MAILCAP_PATH, getenv("MAILCAPS"),
159 MC_USER_FILE, MC_PATH_SEPARATOR, MC_STDPATH);
161 path = pathcopy; /* overloaded "path" */
164 * Insert an entry for the image-viewer variable from .pinerc, if present.
166 if(ps_global->VAR_IMAGE_VIEWER && *ps_global->VAR_IMAGE_VIEWER){
167 MailcapEntry *mc = mc_new_entry();
169 snprintf(image_viewer, sizeof(image_viewer), "%s %%s", ps_global->VAR_IMAGE_VIEWER);
171 MailcapData.raw = mail_newstringlist();
172 MailcapData.raw->text.data = (unsigned char *) cpystr(image_viewer);
173 mc->command = (char *) MailcapData.raw->text.data;
174 mc->contenttype = "image/*";
175 mc->label = "Alpine Image Viewer";
176 dprint((5, "mailcap: using image-viewer=%s\n",
177 ps_global->VAR_IMAGE_VIEWER
178 ? ps_global->VAR_IMAGE_VIEWER : "?"));
181 dprint((7, "mailcap: path: %s\n", path ? path : "?"));
182 while(path){
183 s = strindex(path, MC_PATH_SEPARATOR);
184 if(s)
185 *s++ = '\0';
186 mc_process_file(path);
187 path = s;
190 if(pathcopy)
191 fs_give((void **)&pathcopy);
193 #ifdef DEBUG
194 if(debug >= 11){
195 MailcapEntry *mc;
196 int i = 0;
198 dprint((11, "Collected mailcap entries\n"));
199 for(mc = MailcapData.head; mc; mc = mc->next){
201 dprint((11, "%d: ", i++));
202 if(mc->label)
203 dprint((11, "%s\n", mc->label ? mc->label : "?"));
204 if(mc->contenttype)
205 dprint((11, " %s",
206 mc->contenttype ? mc->contenttype : "?"));
207 if(mc->command)
208 dprint((11, " command: %s\n",
209 mc->command ? mc->command : "?"));
210 if(mc->testcommand)
211 dprint((11, " testcommand: %s",
212 mc->testcommand ? mc->testcommand : "?"));
213 if(mc->printcommand)
214 dprint((11, " printcommand: %s",
215 mc->printcommand ? mc->printcommand : "?"));
216 dprint((11, " needsterminal %d\n", mc->needsterminal));
219 #endif /* DEBUG */
224 * Add all the entries from this file onto the Mailcap list.
226 void
227 mc_process_file(char *file)
229 char filebuf[MAXPATH+1], *file_data;
231 dprint((5, "mailcap: process_file: %s\n", file ? file : "?"));
233 (void)strncpy(filebuf, file, MAXPATH);
234 filebuf[MAXPATH] = '\0';
235 file = fnexpand(filebuf, sizeof(filebuf));
236 dprint((7, "mailcap: processing file: %s\n", file ? file : "?"));
237 switch(is_writable_dir(file)){
238 case 0: case 1: /* is a directory */
239 dprint((1, "mailcap: %s is a directory, should be a file\n",
240 file ? file : "?"));
241 return;
243 case 2: /* ok */
244 break;
246 case 3: /* doesn't exist */
247 dprint((5, "mailcap: %s doesn't exist\n", file ? file : "?"));
248 return;
250 default:
251 alpine_panic("Programmer botch in mc_process_file");
252 /*NOTREACHED*/
255 if((file_data = read_file(file, READ_FROM_LOCALE)) != NULL){
256 STRINGLIST *newsl, **sl;
258 /* Create a new container */
259 newsl = mail_newstringlist();
260 newsl->text.data = (unsigned char *) file_data;
262 /* figure out where in the list it should go */
263 for(sl = &MailcapData.raw; *sl; sl = &((*sl)->next))
266 *sl = newsl; /* Add it to the list */
268 mc_parse_file(file_data); /* the process mailcap data */
270 else
271 dprint((5, "mailcap: %s can't be read\n", file ? file : "?"));
275 void
276 mc_parse_file(char *file)
278 char *tokens[MC_TOKEN_MAX];
280 while(mc_parse_line(&file, tokens))
281 mc_build_entry(tokens);
286 mc_parse_line(char **line, char **tokens)
288 char **tokenp = tokens;
290 while(mc_comment(line)) /* skip comment lines */
293 while(mc_token(tokenp, line)) /* collect ';' delim'd tokens */
294 if(++tokenp - tokens >= MC_TOKEN_MAX)
295 fatal("Ran out of tokens parsing mailcap file"); /* outch! */
297 *++tokenp = NULL; /* tie off list */
298 return(*tokens != NULL);
303 * Retuns 1 if line is a comment, 0 otherwise
306 mc_comment(char **line)
308 if(**line == '\n'){ /* blank line is a comment, too */
309 (*line)++;
310 return(1);
313 if(**line == '#'){
314 while(**line) /* !EOF */
315 if(*++(*line) == '\n'){ /* EOL? */
316 (*line)++;
317 break;
320 return(1);
323 return(0);
328 * Retuns 0 if EOL, 1 otherwise
331 mc_token(char **token, char **line)
333 int rv = 0;
334 char *start, *wsp = NULL;
336 *token = NULL; /* init the slot for this token */
338 /* skip leading white space */
339 while(**line && isspace((unsigned char) **line))
340 (*line)++;
342 start = *line;
344 /* Then see what's left */
345 while(1)
346 switch(**line){
347 case ';' : /* End-Of-Token */
348 rv = 1; /* let caller know more follows */
349 case '\n' : /* EOL */
350 if(wsp)
351 *wsp = '\0'; /* truncate white space? */
352 else
353 *start = '\0'; /* if we have a token, tie it off */
355 (*line)++; /* and get ready to parse next one */
357 if(rv == 1){ /* ignore trailing semicolon */
358 while(**line){
359 if(**line == '\n')
360 rv = 0;
362 if(isspace((unsigned char) **line))
363 (*line)++;
364 else
365 break;
369 case '\0' : /* EOF */
370 return(rv);
372 case '\\' : /* Quoted char */
373 (*line)++;
374 #if defined(DOS) || defined(OS2)
376 * RFC 1524 says that backslash is used to quote
377 * the next character, but since backslash is part of pathnames
378 * on DOS we're afraid people will not put double backslashes
379 * in their mailcap files. Therefore, we violate the RFC by
380 * looking ahead to the next character. If it looks like it
381 * is just part of a pathname, then we consider a single
382 * backslash to *not* be a quoting character, but a literal
383 * backslash instead.
385 * SO:
386 * If next char is any of these, treat the backslash
387 * that preceded it like a regular character.
389 if(**line && isascii(**line)
390 && (isalnum((unsigned char) **line) || strchr("_+-=~" , **line))){
391 *start++ = '\\';
392 wsp = NULL;
393 break;
395 else
396 #endif /* !DOS */
398 if(**line == '\n'){ /* quoted line break */
399 *start = ' ';
400 (*line)++; /* just move on */
401 while(isspace((unsigned char) **line))
402 (*line)++;
404 break;
406 else if(**line == '%') /* quoted '%' becomes "%%" */
407 *--(*line) = '%'; /* overwrite '\' !! */
409 /* Fall thru and copy/advance pointers*/
411 default :
412 if(!*token)
413 *token = start;
415 *start = *(*line)++;
416 wsp = (isspace((unsigned char) *start) && !wsp) ? start : NULL;
417 start++;
418 break;
423 void
424 mc_build_entry(char **tokens)
426 MailcapEntry *mc;
428 if(!tokens[0]){
429 dprint((5, "mailcap: missing content type!\n"));
430 return;
432 else if(!tokens[1] || !mc_sane_command(tokens[1])){
433 dprint((5, "mailcap: missing/bogus command!\n"));
434 return;
437 mc = mc_new_entry();
438 mc->contenttype = *tokens++;
439 mc->command = *tokens++;
441 dprint((9, "mailcap: content type: %s\n command: %s\n",
442 mc->contenttype ? mc->contenttype : "?",
443 mc->command ? mc->command : "?"));
445 /* grok options */
446 for( ; *tokens; tokens++){
447 char *arg;
449 /* legit value? */
450 if(!isalnum((unsigned char) **tokens)){
451 dprint((5, "Unknown parameter = \"%s\"", *tokens));
452 continue;
455 if((arg = strindex(*tokens, '=')) != NULL){
456 *arg = ' ';
457 while(arg > *tokens && isspace((unsigned char) arg[-1]))
458 arg--;
460 *arg++ = '\0'; /* tie off parm arg */
461 while(*arg && isspace((unsigned char) *arg))
462 arg++;
464 if(!*arg)
465 arg = NULL;
468 if(!strucmp(*tokens, "needsterminal")){
469 mc->needsterminal = 1;
470 dprint((9, "mailcap: set needsterminal\n"));
472 else if(!strucmp(*tokens, "copiousoutput")){
473 mc->needsterminal = 2;
474 dprint((9, "mailcap: set copiousoutput\n"));
476 else if(arg && !strucmp(*tokens, "test")){
477 mc->testcommand = arg;
478 dprint((9, "mailcap: testcommand=%s\n",
479 mc->testcommand ? mc->testcommand : "?"));
481 else if(arg && !strucmp(*tokens, "description")){
482 mc->label = arg;
483 dprint((9, "mailcap: label=%s\n",
484 mc->label ? mc->label : "?"));
486 else if(arg && !strucmp(*tokens, "print")){
487 mc->printcommand = arg;
488 dprint((9, "mailcap: printcommand=%s\n",
489 mc->printcommand ? mc->printcommand : "?"));
491 else if(arg && !strucmp(*tokens, "compose")){
492 /* not used */
493 dprint((9, "mailcap: not using compose=%s\n",
494 arg ? arg : "?"));
496 else if(arg && !strucmp(arg, "composetyped")){
497 /* not used */
498 dprint((9, "mailcap: not using composetyped=%s\n",
499 arg ? arg : "?"));
501 else if(arg && !strucmp(arg, "textualnewlines")){
502 /* not used */
503 dprint((9,
504 "mailcap: not using texttualnewlines=%s\n",
505 arg ? arg : "?"));
507 else if(arg && !strucmp(arg, "edit")){
508 /* not used */
509 dprint((9, "mailcap: not using edit=%s\n",
510 arg ? arg : "?"));
512 else if(arg && !strucmp(arg, "x11-bitmap")){
513 /* not used */
514 dprint((9, "mailcap: not using x11-bitmap=%s\n",
515 arg ? arg : "?"));
517 else
518 dprint((9, "mailcap: ignoring unknown flag: %s\n",
519 arg ? arg : "?"));
525 * Tests for mailcap defined command's sanity
528 mc_sane_command(char *command)
530 /* First, test that a command string actually exists */
531 if(command && *command){
532 #ifdef LATER
534 * NOTE: Maybe we'll do this later. The problem is when the
535 * mailcap's been misconfigured. We then end up supressing
536 * valuable output when the user actually tries to launch the
537 * spec'd viewer.
540 /* Second, Make sure we can get at it */
541 if(can_access_in_path(getenv("PATH"), command, EXECUTE_ACCESS) >= 0)
542 #endif
543 return(1);
546 return(0); /* failed! */
551 * Returns the mailcap entry for type/subtype from the successfull
552 * mailcap entry, or NULL if none. Command string still contains % stuff.
554 MailcapEntry *
555 mc_get_command(int type, char *subtype, BODY *body,
556 int check_extension, int *sp_handlingp)
558 MailcapEntry *mc;
559 char tmp_subtype[256], tmp_ext[16], *ext = NULL;
561 dprint((5, "- mc_get_command(%s/%s) -\n",
562 body_type_names(type),
563 subtype ? subtype : "?"));
565 if(type == TYPETEXT
566 && (!subtype || !strucmp(subtype, "plain"))
567 && F_ON(F_SHOW_TEXTPLAIN_INT, ps_global))
568 return(NULL);
570 mc_init();
572 if(check_extension){
573 char *fname;
574 MT_MAP_T e2b;
577 * Special handling for when we're looking at what's likely
578 * binary application data. Look for a file name extension
579 * that we might use to hook a helper app to.
581 * NOTE: This used to preclude an "app/o-s" mailcap entry
582 * since this took precedence. Now that there are
583 * typically two scans through the check_extension
584 * mechanism, the mailcap entry now takes precedence.
586 if((fname = get_filename_parameter(NULL, 0, body, &e2b.from.ext)) != NULL
587 && e2b.from.ext && e2b.from.ext[0]){
588 if(strlen(e2b.from.ext) < sizeof(tmp_ext) - 2){
589 strncpy(ext = tmp_ext, e2b.from.ext - 1, sizeof(tmp_ext)); /* remember it */
590 tmp_ext[sizeof(tmp_ext)-1] = '\0';
591 if(mt_srch_mime_type(mt_srch_by_ext, &e2b)){
592 type = e2b.to.mime.type; /* mapped type */
593 strncpy(subtype = tmp_subtype, e2b.to.mime.subtype,
594 sizeof(tmp_subtype)-1);
595 tmp_subtype[sizeof(tmp_subtype)-1] = '\0';
596 fs_give((void **) &e2b.to.mime.subtype);
597 body = NULL; /* the params no longer apply */
601 fs_give((void **) &fname);
603 else{
604 if(fname)
605 fs_give((void **) &fname);
607 return(NULL);
611 for(mc = MailcapData.head; mc; mc = mc->next)
612 if(mc_ctype_match(type, subtype, mc->contenttype)
613 && mc_passes_test(mc, type, subtype, body)){
614 dprint((9,
615 "mc_get_command: type=%s/%s, command=%s\n",
616 body_type_names(type),
617 subtype ? subtype : "?",
618 mc->command ? mc->command : "?"));
619 return(mc);
622 if(mime_os_specific_access()){
623 static MailcapEntry fake_mc;
624 static char fake_cmd[1024];
625 char tmp_mime_type[256];
627 memset(&fake_mc, 0, sizeof(MailcapEntry));
628 fake_cmd[0] = '\0';
629 fake_mc.command = fake_cmd;
631 snprintf(tmp_mime_type, sizeof(tmp_mime_type), "%s/%s", body_types[type], subtype);
632 if(mime_get_os_mimetype_command(tmp_mime_type, ext, fake_cmd,
633 sizeof(fake_cmd), check_extension, sp_handlingp))
634 return(&fake_mc);
637 return(NULL);
642 * Check whether the pattern "pat" matches this type/subtype.
643 * Returns 1 if it does, 0 if not.
646 mc_ctype_match(int type, char *subtype, char *pat)
648 char *type_name = body_type_names(type);
649 int len = strlen(type_name);
651 dprint((5, "mc_ctype_match: %s == %s / %s ?\n",
652 pat ? pat : "?",
653 type_name ? type_name : "?",
654 subtype ? subtype : "?"));
656 return(!struncmp(type_name, pat, len)
657 && ((pat[len] == '/'
658 && (!pat[len+1] || pat[len+1] == '*'
659 || !strucmp(subtype, &pat[len+1])))
660 || !pat[len]));
665 * Run the test command for entry mc to see if this entry currently applies to
666 * applies to this type/subtype.
668 * Returns 1 if it does pass test (exits with status 0), 0 otherwise.
671 mc_passes_test(MailcapEntry *mc, int type, char *subtype, BODY *body)
673 char *cmd = NULL;
674 int rv;
676 dprint((5, "- mc_passes_test -\n"));
678 if(mc->testcommand
679 && *mc->testcommand
680 && !(cmd = mc_bld_test_cmd(mc->testcommand, type, subtype, body)))
681 return(FALSE); /* couldn't be built */
683 if(!mc->testcommand || !cmd || !*cmd){
684 if(cmd)
685 fs_give((void **)&cmd);
687 dprint((7, "no test command, so Pass\n"));
688 return 1;
691 rv = exec_mailcap_test_cmd(cmd);
692 dprint((7, "mc_passes_test: \"%s\" %s (rv=%d)\n",
693 cmd ? cmd : "?", rv ? "Failed" : "Passed", rv)) ;
695 fs_give((void **)&cmd);
697 return(!rv);
702 mailcap_can_display(int type, char *subtype, BODY *body, int check_extension)
704 dprint((5, "- mailcap_can_display -\n"));
706 return(mc_get_command(type, subtype, body,
707 check_extension, NULL) != NULL);
711 MCAP_CMD_S *
712 mailcap_build_command(int type, char *subtype, BODY *body,
713 char *tmp_file, int *needsterm, int chk_extension)
715 MailcapEntry *mc;
716 char *command, *err = NULL;
717 MCAP_CMD_S *mc_cmd = NULL;
718 int sp_handling = 0;
720 dprint((5, "- mailcap_build_command -\n"));
722 mc = mc_get_command(type, subtype, body, chk_extension, &sp_handling);
723 if(!mc){
724 q_status_message(SM_ORDER, 3, 4, "Error constructing viewer command");
725 dprint((1,
726 "mailcap_build_command: no command string for %s/%s\n",
727 body_type_names(type), subtype ? subtype : "?"));
728 return((MCAP_CMD_S *)NULL);
731 if(needsterm)
732 *needsterm = mc->needsterminal;
734 if(sp_handling)
735 command = cpystr(mc->command);
736 else if(!(command = mc_cmd_bldr(mc->command, type, subtype, body, tmp_file, &err)) && err && *err)
737 q_status_message(SM_ORDER, 5, 5, err);
739 dprint((5, "built command: %s\n", command ? command : "?"));
741 if(command){
742 mc_cmd = (MCAP_CMD_S *)fs_get(sizeof(MCAP_CMD_S));
743 mc_cmd->command = command;
744 mc_cmd->special_handling = sp_handling;
746 return(mc_cmd);
751 * mc_bld_test_cmd - build the command to test if the given type flies
753 * mc_cmd_bldr's tmp_file argument is NULL as we're not going to
754 * decode and write each and every MIME segment's data to a temp file
755 * when no test's going to use the data anyway.
757 char *
758 mc_bld_test_cmd(char *controlstring, int type, char *subtype, BODY *body)
760 return(mc_cmd_bldr(controlstring, type, subtype, body, NULL, NULL));
765 * mc_cmd_bldr - construct a command string to execute
767 * If tmp_file is null, then the contents of the given MIME segment
768 * is not provided. This is useful for building the "test=" string
769 * as it doesn't operate on the segment's data.
771 * The return value is an alloc'd copy of the command to be executed.
773 char *
774 mc_cmd_bldr(char *controlstring, int type, char *subtype,
775 BODY *body, char *tmp_file, char **err)
777 char *from, *to, *s, *parm;
778 int prefixed = 0, used_tmp_file = 0;
780 dprint((8, "- mc_cmd_bldr -\n"));
782 for(from = controlstring, to = tmp_20k_buf; *from; ++from){
783 if(prefixed){ /* previous char was % */
784 prefixed = 0;
785 switch(*from){
786 case '%': /* turned \% into this earlier */
787 if(to-tmp_20k_buf < SIZEOF_20KBUF)
788 *to++ = '%';
790 break;
792 case 's': /* insert tmp_file name in cmd */
793 if(tmp_file){
794 used_tmp_file = 1;
795 sstrncpy(&to, tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf));
797 else
798 dprint((1,
799 "mc_cmd_bldr: %%s in cmd but not supplied!\n"));
801 break;
803 case 't': /* insert MIME type/subtype */
804 /* quote to prevent funny business */
805 if(to-tmp_20k_buf < SIZEOF_20KBUF)
806 *to++ = '\'';
808 sstrncpy(&to, body_type_names(type), SIZEOF_20KBUF-(to-tmp_20k_buf));
810 if(to-tmp_20k_buf < SIZEOF_20KBUF)
811 *to++ = '/';
813 sstrncpy(&to, subtype, SIZEOF_20KBUF-(to-tmp_20k_buf));
815 if(to-tmp_20k_buf < SIZEOF_20KBUF)
816 *to++ = '\'';
818 break;
820 case '{': /* insert requested MIME param */
821 if(F_OFF(F_DO_MAILCAP_PARAM_SUBST, ps_global)){
822 int save;
824 dprint((2, "mc_cmd_bldr: param subs %s\n",
825 from ? from : "?"));
826 if(err){
827 if((s = strindex(from, '}')) != NULL){
828 save = *++s;
829 *s = '\0';
832 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
833 "Mailcap: see hidden feature %.200s (%%%.200s)",
834 feature_list_name(F_DO_MAILCAP_PARAM_SUBST), from);
835 *err = tmp_20k_buf;
836 if(s)
837 *s = save;
840 return(NULL);
843 s = strindex(from, '}');
844 if(!s){
845 q_status_message1(SM_ORDER, 0, 4,
846 "Ignoring ill-formed parameter reference in mailcap file: %.200s", from);
847 break;
850 *s = '\0';
851 ++from; /* from is the part inside the brackets now */
853 parm = parameter_val(body ? body->parameter : NULL, from);
855 dprint((9,
856 "mc_cmd_bldr: parameter %s = %s\n",
857 from ? from : "?", parm ? parm : "(not found)"));
860 * Quote parameter values for /bin/sh.
861 * Put single quotes around the whole thing but every time
862 * there is an actual single quote put it outside of the
863 * single quotes with a backslash in front of it. So the
864 * parameter value fred's car
865 * turns into 'fred'\''s car'
867 if(to-tmp_20k_buf < SIZEOF_20KBUF)
868 *to++ = '\''; /* opening quote */
870 if(parm){
871 char *p;
874 * Copy value, but quote single quotes for /bin/sh
875 * Backslash quote is ignored inside single quotes so
876 * have to put those outside of the single quotes.
877 * (The parm+1000 nonsense is to protect against
878 * malicious mail trying to overflow our buffer.)
880 * TCH - Change 2/8/1999
881 * Also quote the ` to prevent execution of arbitrary code
883 for(p = parm; *p && p < parm+1000; p++){
884 if((*p == '\'') || (*p == '`')){
885 if(to-tmp_20k_buf+4 < SIZEOF_20KBUF){
886 *to++ = '\''; /* closing quote */
887 *to++ = '\\';
888 *to++ = *p; /* quoted character */
889 *to++ = '\''; /* opening quote */
892 else if(to-tmp_20k_buf < SIZEOF_20KBUF)
893 *to++ = *p;
896 fs_give((void **) &parm);
899 if(to-tmp_20k_buf < SIZEOF_20KBUF)
900 *to++ = '\''; /* closing quote for /bin/sh */
902 *s = '}'; /* restore */
903 from = s;
904 break;
907 * %n and %F are used by metamail to support otherwise
908 * unrecognized multipart Content-Types. Pine does
909 * not use these since we're only dealing with the individual
910 * parts at this point.
912 case 'n':
913 case 'F':
914 default:
915 dprint((9,
916 "Ignoring %s format code in mailcap file: %%%c\n",
917 (*from == 'n' || *from == 'F') ? "unimplemented"
918 : "unrecognized",
919 *from));
920 break;
923 else if(*from == '%') /* next char is special */
924 prefixed = 1;
925 else if(to-tmp_20k_buf < SIZEOF_20KBUF) /* regular character, just copy */
926 *to++ = *from;
929 if(to-tmp_20k_buf < SIZEOF_20KBUF)
930 *to = '\0';
932 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
935 * file not specified, redirect to stdin
937 if(!used_tmp_file && tmp_file)
938 snprintf(to, SIZEOF_20KBUF-(to-tmp_20k_buf), MC_ADD_TMP, tmp_file);
940 return(cpystr(tmp_20k_buf));
947 MailcapEntry *
948 mc_new_entry(void)
950 MailcapEntry *mc = (MailcapEntry *) fs_get(sizeof(MailcapEntry));
951 memset(mc, 0, sizeof(MailcapEntry));
952 *MailcapData.tail = mc;
953 MailcapData.tail = &mc->next;
954 return(mc);
959 * Free a list of mailcap entries
961 void
962 mc_free_entry(MailcapEntry **mc)
964 if(mc && *mc){
965 mc_free_entry(&(*mc)->next);
966 fs_give((void **) mc);
971 void
972 mailcap_free(void)
974 mail_free_stringlist(&MailcapData.raw);
975 mc_free_entry(&MailcapData.head);