static char rcsid[] = "@(#)$Id: mailcap.c,v 1.15 2006/04/09 07:37:30 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.15 $ $State: Exp $ * * Author: Kari Hurtta (was hurtta+elm@ozone.FMI.FI) *****************************************************************************/ #include "def_melib.h" #include "s_me.h" #include #ifndef ANSI_C extern int errno; #endif DEBUG_VAR(Debug,__FILE__,"mime"); #if ANSI_C #define S_(x) static x; #else #define S_(x) #endif /* RFC 1343: A User Agent Configuration Mechanism for Multimedia Mail Format Information ... this exactly does not follow that specially entries are NOT passed to shell (as sh -c "command") this is not very safe ..... RFC 1343: Because of differences in shells and the implementation and behavior of the same shell from one system to another, it is specified that the command line be intended as input to the Bourne shell, i.e. that it is implicitly preceded by "/bin/sh -c " on the command line. Using of that may case that sender of mail may execute command by using shells special characters. Avoiding of that requires carefull quotation of mailcap entries. And correct quotation requires carefull parsing of Bourne shell syntax. And we do not really know if /bin/sh following Bourne shell syntax or have on it some extensions. Only view command and test command is supported. And only if there is not any meta character of shell on there so it can executed directly. */ #define MAILCAP_PARAM_magic 0xF401 struct mailcap_vector { unsigned short magic; /* MAILCAP_PARAM_magic */ struct agv1 { enum arg_type { mv_literal, mv_filename, mv_type, mv_parameter, mv_environ /* Environment variable inside of " */ } type; struct arg { int arg_len; int alloced; char * arg; } arg; } * subvector; int subcount; }; static void arg_zero P_((struct arg *arg)); static void arg_zero(arg) struct arg *arg; { arg->arg_len = 0; arg->alloced = 0; arg->arg = NULL; } static void arg_free P_((struct arg *arg)); static void arg_free(arg) struct arg *arg; { if (arg->arg) free(arg->arg); arg->arg = NULL; arg->arg_len = 0; arg->alloced = 0; } static void arg_add P_((struct arg *arg, int c)); static void arg_add(arg,c) struct arg *arg; int c; { if (arg->arg_len+2 > arg->alloced) { arg->alloced = arg->arg_len + 10; arg->arg = safe_realloc(arg->arg,arg->alloced); } arg->arg[arg->arg_len] = c; arg->arg[arg->arg_len+1] = '\0'; arg->arg_len++; } static void inc_mailcap_vector P_((struct mailcap_vector **P, int *count)); static void inc_mailcap_vector(P,count) struct mailcap_vector **P; int *count; { struct mailcap_vector *vector = *P; int vector_count = *count; vector = safe_realloc(vector, (vector_count+1) * sizeof (vector[0])); vector[vector_count].magic = MAILCAP_PARAM_magic; vector[vector_count].subvector = NULL; vector[vector_count].subcount = 0; vector_count++; *P = vector; *count = vector_count; } static void add_mailcap_vector P_((struct mailcap_vector *vector, int vector_count, enum arg_type type, int c)); static void add_mailcap_vector(vector,vector_count,type,c) struct mailcap_vector *vector; int vector_count; enum arg_type type; int c; { if (!vector || vector_count < 1) panic("MAILCAP PANIC",__FILE__,__LINE__,"add_mailcap_vector", "No vector",0); if (MAILCAP_PARAM_magic != vector[vector_count-1].magic) { panic("MAILCAP PANIC",__FILE__,__LINE__,"add_mailcap_vector", "Bad magic number",0); } if (vector[vector_count-1].subcount < 1 || type != vector[vector_count-1]. subvector[vector[vector_count-1].subcount-1]. type) { vector[vector_count-1].subvector = safe_realloc(vector[vector_count-1].subvector, (vector[vector_count-1].subcount + 1 ) * sizeof(vector[vector_count-1].subvector[0])); vector[vector_count-1]. subvector[vector[vector_count-1].subcount].type = type; arg_zero( & vector[vector_count-1]. subvector[vector[vector_count-1].subcount].arg); vector[vector_count-1].subcount++; } if (c != -1) arg_add(& vector[vector_count-1]. subvector[vector[vector_count-1].subcount-1].arg, c); } static void free_mailcap_vector P_((struct mailcap_vector **P, int *count)); static void free_mailcap_vector(P,count) struct mailcap_vector **P; int *count; { struct mailcap_vector *vector = *P; int vector_count = *count; if (vector) { int i; for (i = 0; i < vector_count; i++) { int j; if (MAILCAP_PARAM_magic != vector[i].magic) { panic("MAILCAP PANIC",__FILE__,__LINE__,"free_mailcap_vector", "Bad magic number",0); } if (vector[i].subvector) { for (j = 0; j < vector[i].subcount; j++) { arg_free( & vector[i].subvector[j].arg); } free(vector[i].subvector); vector[i].subvector = NULL; } vector[i].subcount = 0; } free(vector); vector = NULL; } vector_count = 0; *P = vector; *count = vector_count; } #define MAILCAP_magic 0xF400 struct mailcap_entry { unsigned short magic; /* MAILCAP_magic */ struct mailcap_vector * view_command; int view_command_len; struct mailcap_vector * test_command; int test_command_len; /* TODO commands: compose composetyped edit print */ int needsterminal; int copiousoutput; char * extension; }; static void zero_mailcap_entry P_((struct mailcap_entry *x)); static void zero_mailcap_entry(x) struct mailcap_entry *x; { /* bzero is defined on hdrs/defs.h */ bzero((void *)x,sizeof (*x)); x->magic = MAILCAP_magic; x->view_command = NULL; x->view_command_len = 0; x->test_command = NULL; x->test_command_len = 0; /* TODO commands: compose composetyped edit print */ x->needsterminal = 0; x->copiousoutput = 0; x->extension = NULL; } static void free_mailcap_entry P_((struct mailcap_entry *x)); static void free_mailcap_entry(x) struct mailcap_entry *x; { if (MAILCAP_magic != x->magic) panic("MAILCAP PANIC",__FILE__,__LINE__,"free_mailcap_entry", "Bad magic number",0); free_mailcap_vector(& x->view_command,& x->view_command_len); free_mailcap_vector(& x->test_command,& x->test_command_len); x->needsterminal = 0; x->copiousoutput = 0; if (x->extension) free(x->extension); x->extension = NULL; } enum mc_entry_state { mcES_keyword, mcES_vector_begin, mcES_arg, mcES_error, mcES_string }; enum mc_entry_state select_mode P_((char * filename, int linenum, struct mailcap_entry *x, struct arg *keyword)); enum mc_entry_state select_mode(filename,linenum,x,keyword) char * filename; int linenum; struct mailcap_entry *x; struct arg *keyword; { if (!keyword->arg) { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- empty field\n", filename, linenum)); return mcES_error; } if (0 == istrcmp("nametemplate",keyword->arg)) { return mcES_string; } return mcES_vector_begin; } static void process_field P_((char * filename, int linenum, struct mailcap_entry *x, struct arg *keyword, struct arg *string, struct mailcap_vector ** vector, int * vector_count, int field_count)); static void process_field(filename,linenum,x,keyword,string,vector,vector_count, field_count) char * filename; int linenum; struct mailcap_entry *x; struct arg *keyword; struct arg *string; struct mailcap_vector ** vector; int * vector_count; int field_count; { if (1 == field_count) { if (keyword->arg) panic("MAILCAP PANIC",__FILE__,__LINE__,"process_field", "keyword should be empty",0); x->view_command = *vector; x->view_command_len = *vector_count; *vector = NULL; *vector_count = 0; } else { if (!keyword->arg && *vector_count) { panic("MAILCAP PANIC",__FILE__,__LINE__,"process_field", "keyword should be given",0); } if (!keyword->arg) { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- empty field\n", filename, linenum)); return; } if (0 == istrcmp("test",keyword->arg)) { if (x->test_command_len) { lib_error(CATGETS(elm_msg_cat, MeSet, MeDuplicateKeyword, "Line %d on mailcap %s -- duplicate %s keyword"), linenum,filename, keyword->arg ); } else { x->test_command = *vector; x->test_command_len = *vector_count; *vector = NULL; *vector_count = 0; } } else if (0 == istrcmp("needsterminal",keyword->arg)) { x->needsterminal = 1; } else if (0 == istrcmp("copiousoutput",keyword->arg)) { x->copiousoutput = 1; } else if (0 == istrcmp("nametemplate",keyword->arg)) { if (x->extension) { lib_error(CATGETS(elm_msg_cat, MeSet, MeDuplicateKeyword, "Line %d on mailcap %s -- duplicate %s keyword"), linenum,filename, keyword->arg ); } else { if (!string->arg || string->arg != strstr(string->arg,"%s")) { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- nametemplate do not start with %%s: %s\n", filename, linenum, string->arg ? string->arg : "")); return; } x->extension = safe_strdup(string->arg+2); } } else { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- unsupported keyword %s\n", filename, linenum, keyword->arg)); } } } void read_mailcaps() { /* Pointer to internal list, do NOT free */ char ** mailcaps = give_dt_path_as_elems(&internal_mailcaps,"internal-mailcaps"); int mailcap_idx; static int mailcaps_readed = 0; if (mailcaps_readed || !mailcaps) return; for (mailcap_idx = 0; mailcaps[mailcap_idx]; mailcap_idx++) { FILE * F; enum mc_state { mc_begin_on_line, mc_entry, mc_comment } state = mc_begin_on_line; enum mc_entry_state entry_state = mcES_keyword; int c; int linenum = 1; int field_count = 0; struct arg keyword; struct arg string; struct mailcap_vector * vector = NULL; int vector_count = 0; enum arg_type arg_type = mv_literal; int is_shell_quoted = 0; struct mailcap_entry Entry; /* Temporary storage */ int unsupported = 0; media_type_t T = NULL; enum mime_major_type T_major = MIME_TYPE_UNKNOWN; int err; zero_mailcap_entry(&Entry); arg_zero(&keyword); arg_zero(&string); err = can_open(mailcaps[mailcap_idx],"r"); if (err) { DPRINT(Debug,2,(&Debug, "Failed to open mailcap %s: (errno=%d) %s (can_open)\n", mailcaps[mailcap_idx],err,error_description(err))); continue; } F = fopen(mailcaps[mailcap_idx],"r"); if (!F) { int err = errno; DPRINT(Debug,2,(&Debug, "Failed to open mailcap %s: (errno=%d) %s\n", mailcaps[mailcap_idx],err,error_description(err))); continue; } while (EOF != (c = getc(F))) { switch (state) { case mc_begin_on_line: switch(c) { case '\n': /* still as begin of line */ linenum++; break; case '#': /* comment until newline */ state = mc_comment; break; default: state = mc_entry; /* mailcap entry seen */ if (vector || vector_count) panic("MAILCAP PANIC",__FILE__,__LINE__,"read_mailcaps", "Not beginning of vector",0); field_count = 0; arg_free(&keyword); arg_free(&string); free_mailcap_vector(&vector,&vector_count); entry_state = mcES_keyword; /* for mime type */ /* Start new entry */ free_mailcap_entry(&Entry); goto handle_entry; } break; case mc_comment: switch(c) { case '\n': /* new beginning of line -- comment parsed */ state = mc_begin_on_line; linenum++; break; default: /* still as comment */ break; } break; case mc_entry: handle_entry: switch(c) { case '\n': if (field_count < 1) { lib_error(CATGETS(elm_msg_cat, MeSet, MeNoAction, "No action after type on line %d on mailcap %s"), linenum,mailcaps[mailcap_idx]); unsupported++; } else if (mcES_error == entry_state || !T && T_major == MIME_TYPE_UNKNOWN) { unsupported++; } else process_field(mailcaps[mailcap_idx],linenum, &Entry,&keyword,&string, &vector,&vector_count, field_count); if (!unsupported) { struct mailcap_entry *mailcap; struct media_type_handle *entry; mailcap = safe_malloc(sizeof *mailcap); *mailcap = Entry; zero_mailcap_entry(&Entry); /* Avoid freeing of stored entry */ entry = safe_malloc(sizeof *entry); entry->type = handle_mailcap_entry; entry->p.mailcap = mailcap; if (T) { register_mt_handler(T,entry); DPRINT(Debug,4,(&Debug, "Mailcap entry for %s/%s added (%s:%d)\n", get_major_type_name(T), get_subtype_name(T), mailcaps[mailcap_idx], linenum)); } else { register_mt_defhandler(T_major,entry); DPRINT(Debug,4,(&Debug, "Mailcap entry for default %s/* added (%s:%d)\n", get_major_type_name2(T_major), mailcaps[mailcap_idx], linenum)); } } else { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- unsupported\n", mailcaps[mailcap_idx], linenum)); } unsupported = 0; T = NULL; T_major = MIME_TYPE_UNKNOWN; arg_free(&keyword); arg_free(&string); free_mailcap_vector(&vector,&vector_count); state = mc_begin_on_line; linenum++; break; case '\\': c = getc(F); if ('\n' == c) { linenum++; continue; /* Ignore escaped newline */ } /* FALLTHROUGH */ default: normal_char: if ( (' ' == c || '\t' == c) && mcES_arg == entry_state && !is_shell_quoted) { entry_state = mcES_vector_begin; } if ( (' ' != c && '\t' != c) && mcES_vector_begin == entry_state) { /* Start new argument for command */ inc_mailcap_vector(&vector,&vector_count); entry_state = mcES_arg; arg_type = mv_literal; is_shell_quoted = 0; } switch (entry_state) { case mcES_arg: if (mv_environ == arg_type) { if(isascii(c) && isalpha(c)) { /* OK */ } else if ('/' == c || '"' == c || ' ' == c || '\t' == c) { /* Variable terminated ... let next condition check it ... */ arg_type = mv_literal; add_mailcap_vector(vector,vector_count,arg_type,-1); } else { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- character %c unsupported on $\n", mailcaps[mailcap_idx], linenum,c)); entry_state = mcES_error; break; } } if (mv_literal == arg_type) { /* Check unsupported characters -- all shell special go to that */ switch(c) { case '\'': if ( 0 == is_shell_quoted) is_shell_quoted = '\''; else if ( '\'' == is_shell_quoted) is_shell_quoted = 0; /* Quarantee that there is argument -- even when '' was given */ add_mailcap_vector(vector,vector_count,arg_type,-1); goto out_1; /* DO not add quote to string */ case '"': if ( 0 == is_shell_quoted) is_shell_quoted = '"'; else if ( '"' == is_shell_quoted) is_shell_quoted = 0; /* Quarantee that there is argument -- even when "" was given */ add_mailcap_vector(vector,vector_count,arg_type,-1); goto out_1; /* DO not add quote to string */ case '#': case '&': case '(': case ')': case '[': case ']': case '{': case '}': case '?': case '*': case '^': case '~': case '<': case '>': case '|': case ';': if ('"' == is_shell_quoted) break; /* WAS OK */ case '$': if ('"' == is_shell_quoted) { /* variables inside of " are OK */ c = getc(F); if (isascii(c) && isalpha(c)) { arg_type = mv_environ; add_mailcap_vector(vector,vector_count,arg_type, -1); /* c is added later */ break; /* OK */ } else { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- shell special $%c unsupported\n", mailcaps[mailcap_idx], linenum,c)); entry_state = mcES_error; break; } } case '`': if ('\'' == is_shell_quoted) break; /* WAS OK */ case '\\': DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- shell special %c unsupported\n", mailcaps[mailcap_idx], linenum,c)); entry_state = mcES_error; break; } } if (mv_parameter == arg_type && '}' == c) { arg_type = mv_literal; /* HACK: This causes that lines like %{...}%{...} work because that adds empty literal between two %{...} */ add_mailcap_vector(vector,vector_count,arg_type,-1); } else add_mailcap_vector(vector,vector_count, arg_type,c); out_1: break; case mcES_keyword: if (c != ' ' && c != '\t') arg_add(&keyword,c); break; case mcES_string: arg_add(&string,c); break; } break; case '=': if (entry_state != mcES_keyword) goto normal_char; if (!keyword.arg) { lib_error(CATGETS(elm_msg_cat, MeSet, MeNoFieldnameBefore, "No fieldname before = on line %d on mailcap %s"), linenum,mailcaps[mailcap_idx]); entry_state = mcES_error; continue; } if (vector || vector_count) panic("MAILCAP PANIC",__FILE__,__LINE__,"read_mailcaps", "Not beginning of vector",0); if (string.arg_len) panic("MAILCAP PANIC",__FILE__,__LINE__,"read_mailcaps", "Not beginning of string",0); entry_state = select_mode(mailcaps[mailcap_idx],linenum, &Entry,&keyword); break; case ';': if (0 == field_count) { if (mcES_error == entry_state) { unsupported++; } else if (!keyword.arg) { lib_error(CATGETS(elm_msg_cat, MeSet, MeNoMimeType, "Line %d on mailcap %s -- Mime type is not given"), linenum,mailcaps[mailcap_idx]); unsupported++; } else { char * c = strchr(keyword.arg,'/'); if (c) { *c = '\0'; c++; } if (!c) unsupported++; /* Wild cards are not supported */ else if (0 == strcmp(c,"*")) T_major = give_major_type(keyword.arg,1); else T = give_media_type(keyword.arg,c,1); } entry_state = mcES_vector_begin; /* for view command */ } else { if (mcES_error == entry_state) { unsupported++; } else if (mcES_arg == entry_state && (mv_literal == arg_type || mv_environ == arg_type) && is_shell_quoted) { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- Shell quote %c not terminated before ;", linenum,mailcaps[mailcap_idx], is_shell_quoted)); unsupported++; } else process_field(mailcaps[mailcap_idx],linenum, &Entry,&keyword,&string, &vector,&vector_count, field_count); entry_state = mcES_keyword; } arg_free(&keyword); arg_free(&string); free_mailcap_vector(&vector,&vector_count); field_count++; break; case '%': if (mcES_vector_begin == entry_state) { /* Start new argument for command */ inc_mailcap_vector(&vector,&vector_count); entry_state = mcES_arg; arg_type = mv_literal; is_shell_quoted = 0; } if (mcES_string == entry_state) { arg_add(&string,c); break; } if (mcES_arg != entry_state) { DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- unexpected %%\n", mailcaps[mailcap_idx], linenum)); entry_state = mcES_error; goto normal_char; } c = getc(F); switch (c) { case 's': add_mailcap_vector(vector,vector_count,mv_filename,-1); arg_type = mv_literal; break; case 't': add_mailcap_vector(vector,vector_count,mv_type,-1); arg_type = mv_literal; break; case '{': arg_type = mv_parameter; break; default: DPRINT(Debug,2,(&Debug, "mailcap %s, line %d -- unexpected param %%%c\n", mailcaps[mailcap_idx], linenum,c)); arg_type = mv_literal; entry_state = mcES_error; goto normal_char; } /* HACK: This causes that lines like %s%s work because that adds empty literal between two %s */ add_mailcap_vector(vector,vector_count,arg_type,-1); break; } break; } } if (state != mc_begin_on_line) { lib_error(CATGETS(elm_msg_cat, MeSet, MeIncompleteMailcapLine, "Incomplete line %d on mailcap %s -- no newline"), linenum,mailcaps[mailcap_idx]); } else if (keyword.arg || vector || string.arg) { panic("MAILCAP PANIC",__FILE__,__LINE__,"read_mailcaps", "State not cleared",0); } free_mailcap_entry(&Entry); arg_free(&keyword); arg_free(&string); free_mailcap_vector(&vector,&vector_count); fclose(F); } mailcaps_readed = 1; } static char * make_mailcap_arg P_((struct mailcap_vector *arg, char * filename, mime_t *p, int *fn_seen)); static char * make_mailcap_arg(arg,filename,p, fn_seen) struct mailcap_vector *arg; char * filename; mime_t *p; int *fn_seen; { struct agv1 * subvector; int len,i; char * ret = NULL; int retlen = 0; if (MAILCAP_PARAM_magic != arg->magic) panic("MAILCAP PANIC",__FILE__,__LINE__,"make_mailcap_arg", "Bad magic number",0); subvector = arg->subvector; len = arg->subcount; for (i = 0; i < len; i++) { switch(subvector[i].type) { int tmp,l,l2; CONST char *ma, *mi, *pv; char *e; char buffer[STRING]; case mv_literal: tmp = retlen + subvector[i].arg.arg_len; ret = safe_realloc(ret,tmp+1); memcpy(ret+retlen,subvector[i].arg.arg,subvector[i].arg.arg_len); retlen = tmp; ret[retlen] = '\0'; break; case mv_environ: e = getenv(subvector[i].arg.arg); if (!e) { DPRINT(Debug,12,(&Debug, "make_mailcap_arg: Environ variable $%s not found, treating as \"\"\n", subvector[i].arg.arg)); e = ""; } l = strlen(e); tmp = retlen + l; ret = safe_realloc(ret,tmp+1); memcpy(ret+retlen,e,l); retlen = tmp; ret[retlen] = '\0'; break; case mv_filename: if (fn_seen) *fn_seen = 1; if (!filename) { DPRINT(Debug,12,(&Debug, "make_mailcap_arg: no filename\n")); goto fail; } l = strlen(filename); tmp = retlen + l; ret = safe_realloc(ret,tmp+1); memcpy(ret+retlen,filename,l); retlen = tmp; ret[retlen] = '\0'; break; case mv_type: ma = get_major_type_name(p->TYPE); mi = get_subtype_name(p->TYPE); l = strlen(ma); l2 = strlen(mi); tmp = retlen + l + l2 +1; ret = safe_realloc(ret,tmp+1); memcpy(ret+retlen,ma,l); ret[retlen+l] = '/'; memcpy(ret+retlen+l+1,mi,l2); retlen = tmp; ret[retlen] = '\0'; break; case mv_parameter: { int mp = give_dt_enumerate_as_int(&mime_parameters); CONST struct string *dv; CONST char * dva; /* 0 == plain 1 == encoded 2 == plain-and-encoded */ if (mp) pv = get_mime_param_ascii(p->TYPE_opts,subvector[i].arg.arg); else pv = get_mime_param_compat(p->TYPE_opts,subvector[i].arg.arg); } if (!pv) { DPRINT(Debug,12,(&Debug, "make_mailcap_arg: no param %s\n", subvector[i].arg.arg)); goto fail; } l = strlen(pv); tmp = retlen + l; ret = safe_realloc(ret,tmp+1); memcpy(ret+retlen,pv,l); retlen = tmp; ret[retlen] = '\0'; break; default: panic("MAILCAP PANIC",__FILE__,__LINE__,"make_mailcap_arg", "Bad subvector type",0); } } if (ret) { DPRINT(Debug,12,(&Debug, "make_mailcap_arg: len=%d ret=%s\n", retlen,ret)); } return ret; fail: if (ret) free(ret); return NULL; } static char ** make_mailcap_argv P_((struct mailcap_vector *command, int command_len, char * filename, mime_t *p,int *fn_seen)); static char ** make_mailcap_argv(command,command_len,filename,p,fn_seen) struct mailcap_vector *command; int command_len; char * filename; mime_t *p; int *fn_seen; { int i; char **argv = safe_malloc((command_len+1) * sizeof (*argv)); if (fn_seen) *fn_seen = 0; for (i = 0; i < command_len; i++) { argv[i] = make_mailcap_arg(& (command[i]), filename,p,fn_seen); if (! argv[i] ) { DPRINT(Debug,12,(&Debug, "make_mailcap_argv: failed to make argument %d\n", i)); goto fail; } } argv[i] = NULL; return argv; fail: for (; i >= 0; i--) { if (argv[i]) { free(argv[i]); argv[i] = NULL; } } free(argv); return NULL; } /* Return 0 if failure and 1 if succeed */ static int run_mailcap P_((const char **argv, FILE *file_handle, out_state_t *state_out, struct mailcap_entry *x, int *exit_stat, int fn_seen)); static int run_mailcap(argv,file_handle,state_out,x,exit_stat, fn_seen) CONST char **argv; FILE *file_handle; out_state_t *state_out; struct mailcap_entry *x; int *exit_stat; int fn_seen; { int flags = SY_ENAB_SIGINT; int r; int raw; struct run_state RS; FILE * XX = NULL; if (! x->needsterminal) flags |= SY_NOTTY; if (x->copiousoutput && state_out) flags |= SY_RUN_STATE_OPIPE; /* RS.pfd will include other end of pipe */ /* IF state_out is set, it implies that this is not test -command ... */ raw = sr_call_RawState (); if (x->needsterminal) { sr_call_Raw(OFF); sr_call_ClearScreen(); if (state_out) sr_call_Write_to_screen(CATGETS(elm_msg_cat, MeSet, MeMailcapRunning, "Running mailcap program %s...\n"), argv[0]); } else { if (state_out) lib_transient(CATGETS(elm_msg_cat, MeSet, MeMailcapRunning, "Running mailcap program %s...\n"), argv[0]); } /* r == 0 : Not run or child lost or not ended yet r < 0 : died on signal r == 1 : OK */ if (file_handle) { /* This block is probably no required... */ rewind(file_handle); #ifdef _POSIX_VERSION /* Synzronize underlying file descriptor */ fflush(file_handle); #else seek(fileno(file_handle),0,0); #endif r = start_run(&RS,flags,argv, fn_seen && x->needsterminal ? -1 : fileno(file_handle), -1); } else r = start_run(&RS,flags,argv,-1,-1); if (!r) { if (raw) sr_call_Raw (ON); /* TODO: Process failure */ return 0; /* FAILED */ } XX = RS.pfd; /* Avoid closing it by wait_end() or run_already_done() */ RS.pfd = NULL; r= run_already_done(&RS,exit_stat); if (x->copiousoutput && state_out) { int l; char buf[STRING]; int count = 0; if (r != 0) state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeMailcapStart1, "-- Start of mailcap results%s\n"), r < 0 || *exit_stat ? catgets(elm_msg_cat, MeSet, MeMailcapFailProg, ", mailcap program failed!") : "."); else state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeMailcapStart, "-- Start of mailcap results.\n")); retry: while ( (l = mail_gets (buf, sizeof (buf), XX)) > 0) { state_put(buf,l,state_out); count += l; } if (ferror(XX) && EINTR == errno) { clearerr(XX); DPRINT(Debug,5,(&Debug, "Reading of result interrupted (EINTR) -- retrying\n")); if (0 == r) { r = run_already_done(&RS,exit_stat); if (0 != r) { DPRINT(Debug,5,(&Debug, "now mailcap program is completing\n")); } } goto retry; } DPRINT(Debug,11,(&Debug,"%d bytes of mailcap program output copied\n",count)); } if (0 == r) r = wait_end(&RS,exit_stat); if (x->needsterminal && !x->copiousoutput && state_out) call_print_status_cooked(&RS,r < 0 ? -r : 0,*exit_stat); if (raw) sr_call_Raw (ON); if (x->copiousoutput && state_out) { state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeMailcapEnd, "-- End of mailcap results%s\n"), r < 0 || *exit_stat ? catgets(elm_msg_cat, MeSet, MeMailcapFailProg, ", mailcap program failed!") : "."); } if (!x->needsterminal && state_out) { if (r < 0) lib_transient(CATGETS(elm_msg_cat, MeSet, MeMailcapRunningSignal, "Running mailcap program %s... Terminated with signal %d"), argv[0],-r); else if (*exit_stat) lib_transient(CATGETS(elm_msg_cat, MeSet, MeMailcapRunningStatus, "Running mailcap program %s... Terminated with status %d"), argv[0],*exit_stat); else lib_transient(CATGETS(elm_msg_cat, MeSet, MeMailcapRunningOK, "Running mailcap program %s... OK"), argv[0]); } if (XX) fclose(XX); if (r < 0 || *exit_stat) return 0; /* FAILED */ return 1; } /* Return 1 if have view command and test succees ... */ int mailcap_is_valid_view(f,p) struct mailcap_entry *f; mime_t *p; { char *cmd; if (MAILCAP_magic != f->magic) panic("MAILCAP PANIC",__FILE__,__LINE__,"mailcap_is_valid_view", "Bad magic number (mailcap)",0); if (p->magic != MIME_magic) panic("MIME PANIC",__FILE__,__LINE__,"mailcap_is_valid_view", "Bad magic number (mime)",0); if (f->view_command_len < 1) return 0; /* Do not have view command */ cmd = make_mailcap_arg ( & ( f->view_command[0] ), NULL, p,NULL); if (!cmd) return 0; /* Do not have view command */ if ('/' == cmd[0] && -1 == access(cmd,EXECUTE_ACCESS)) { int err = errno; DPRINT(Debug,4,(&Debug, "mailcap_is_valid_view: command %s not executable: %s\n", cmd,error_description(err))); free(cmd); return 0; } free(cmd); cmd = NULL; /* Assume that command is OK */ if (f->test_command_len > 0) { int i,r; int ret; int fn_seen = 0; char **argv = make_mailcap_argv(f->test_command, f->test_command_len, NULL,p, &fn_seen); if (!argv) return 0; /* Test command not executable */ r = run_mailcap((CONST char **)argv,NULL,NULL,f,&ret, fn_seen); for (i = 0; argv[i]; i++) { free(argv[i]); argv[i] = NULL; } free(argv); argv = NULL; if (!r || ret != 0) return 0; /* Test failed */ } return 1; } /* Return value must be free()ed */ char * mailcap_view_command(f,p) struct mailcap_entry *f; mime_t *p; { if (f->view_command_len < 1) return NULL; /* Do not have view command */ return make_mailcap_arg ( & ( f->view_command[0] ), NULL, p, NULL); } static char * make_alternate_filename P_((char *file_name,struct mailcap_entry *x)); static char * make_alternate_filename(file_name,x) char *file_name; struct mailcap_entry *x; { char * new_name; char *y; if (! x->extension) return file_name; /* Need not add extension */ y = strchr(file_name,'.'); if (y && 0 == strcmp(y,x->extension)) return file_name; /* Same extension is already on filename */ new_name = safe_strdup(file_name); new_name = strmcat(new_name,x->extension); if (0 != link(file_name,new_name)) { lib_error(CATGETS(elm_msg_cat, MeSet, MeFailLink, "Failed to link %s to %s"), file_name,new_name); free(new_name); return NULL; } DPRINT(Debug,4,(&Debug,"Using name %s for %s because of extension %s\n", new_name,file_name,x->extension)); return new_name; } int run_mailcap_view(file_name,file_handle,state_out,x,p) char *file_name; FILE * file_handle; out_state_t *state_out; struct mailcap_entry *x; mime_t *p; { char **argv; int i,r; int ret; int fn_seen = 0; char *fn1; if (MAILCAP_magic != x->magic) panic("MAILCAP PANIC",__FILE__,__LINE__,"run_mailcap_view", "Bad magic number (mailcap)",0); if (p->magic != MIME_magic) panic("MIME PANIC",__FILE__,__LINE__,"run_mailcap_view", "Bad magic number (mime)",0); fn1 = make_alternate_filename(file_name,x); if (!fn1) return 0; /* can't generate filename */ argv = make_mailcap_argv(x->view_command, x->view_command_len, fn1,p,&fn_seen); if (!argv) return 0; /* view command not executable */ r = run_mailcap((CONST char **)argv,file_handle, state_out,x,&ret,fn_seen); for (i = 0; argv[i]; i++) { free(argv[i]); argv[i] = NULL; } free(argv); argv = NULL; if (file_name != fn1) { if (0 != unlink(fn1)) { DPRINT(Debug,4,(&Debug,"Failed to remove %s\n", fn1)); } free(fn1); } if (!r || ret != 0) return 0; /* run failed */ return 1; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */