* Update to version 2.19.5
[alpine.git] / alpine / dispfilt.c
blobfafdafe98e2730d1b874d2db7ba9d58373d38dc1
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: dispfilt.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-2014 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 /*======================================================================
20 dispfilt.c
21 Display filter technology ;)
23 ====*/
25 #include "headers.h"
27 #include "../pith/state.h"
28 #include "../pith/conf.h"
29 #include "../pith/filter.h"
30 #include "../pith/store.h"
31 #include "../pith/addrstring.h"
32 #include "../pith/mimedesc.h"
33 #include "../pith/list.h"
34 #include "../pith/detach.h"
36 #include "mailview.h"
37 #include "signal.h"
38 #include "busy.h"
39 #include "dispfilt.h"
42 /* internal prototypes */
43 int df_valid_test(BODY *, char *);
48 * dfilter - pipe the data from the given storage object thru the
49 * global display filter and into whatever the putchar's
50 * function points to.
52 * Input is assumed to be UTF-8.
53 * That's converted to user's locale and passed to rawcmd.
54 * That's converted back to UTF-8 and passed through aux_filters.
56 char *
57 dfilter(char *rawcmd, STORE_S *input_so, gf_io_t output_pc, FILTLIST_S *aux_filters)
59 char *status = NULL, *cmd, *resultf = NULL, *tmpfile = NULL;
60 int key = 0, silent = 0;
62 if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,&resultf,NULL,&key,NULL, &silent)) != NULL){
63 suspend_busy_cue();
64 #ifndef _WINDOWS
65 if(!silent){
66 ps_global->mangled_screen = 1;
67 ClearScreen();
69 fflush(stdout);
70 #endif
73 * If it was requested that the interaction take place via
74 * a tmpfile, fill it with text from our input_so, and let
75 * system_pipe handle the rest. Session key and tmp file
76 * mode support additions based loosely on a patch
77 * supplied by Thomas Stroesslin <thomas.stroesslin@epfl.ch>
79 if(tmpfile){
80 PIPE_S *filter_pipe;
81 FILE *fp;
82 gf_io_t gc, pc;
83 STORE_S *tmpf_so;
85 /* write the tmp file */
86 so_seek(input_so, 0L, 0);
87 if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
88 if(key){
89 so_puts(tmpf_so, filter_session_key());
90 so_puts(tmpf_so, NEWLINE);
92 /* copy input to tmp file */
93 gf_set_so_readc(&gc, input_so);
94 gf_set_so_writec(&pc, tmpf_so);
95 gf_filter_init();
96 status = gf_pipe(gc, pc);
97 gf_clear_so_readc(input_so);
98 gf_clear_so_writec(tmpf_so);
99 if(so_give(&tmpf_so) != 0 && status == NULL)
100 status = error_description(errno);
102 /* prepare the terminal in case the filter uses it */
103 if(status == NULL){
104 if((filter_pipe = open_system_pipe(cmd, NULL, NULL,
105 PIPE_USER | (silent ? PIPE_SILENT :
106 (F_ON(F_DISABLE_TERM_RESET_DISP, ps_global) ? 0 : PIPE_RESET)),
107 0, pipe_callback, NULL)) != NULL){
108 if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){
109 /* pull result out of tmp file */
110 if((fp = our_fopen(tmpfile, "rb")) != NULL){
111 gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE);
112 gf_filter_init();
113 if(aux_filters)
114 for( ; aux_filters->filter; aux_filters++)
115 gf_link_filter(aux_filters->filter,
116 aux_filters->data);
118 status = gf_pipe(gc, output_pc);
119 fclose(fp);
121 else
122 status = "Can't read result of display filter";
124 else
125 status = "Filter command returned error.";
127 else
128 status = "Can't open pipe for display filter";
131 our_unlink(tmpfile);
133 else
134 status = "Can't open display filter tmp file";
136 else if((status = gf_filter(cmd, key ? filter_session_key() : NULL,
137 input_so, output_pc, aux_filters, silent,
138 F_ON(F_DISABLE_TERM_RESET_DISP, ps_global),
139 pipe_callback)) != NULL){
140 unsigned long ch;
142 fprintf(stdout,"\r\n%s Hit return to continue.", status);
143 fflush(stdout);
144 while((ch = read_char(300)) != ctrl('M') && ch != NO_OP_IDLE)
145 putchar(BELL);
148 if(resultf){
149 if(name_file_size(resultf) > 0L)
150 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
152 fs_give((void **)&resultf);
155 resume_busy_cue(0);
156 #ifndef _WINDOWS
157 if(!silent)
158 ClearScreen();
159 #endif
160 fs_give((void **)&cmd);
163 return(status);
168 * expand_filter_tokens - return an alloc'd string with any special tokens
169 * in the given filter expanded, NULL otherwise.
171 char *
172 expand_filter_tokens(char *filter, ENVELOPE *env, char **tmpf, char **resultf,
173 char **mtypef, int *key, int *hdrs, int *silent)
175 char **array, **q;
176 char *bp, *cmd = NULL, *p = NULL,
177 *tfn = NULL, *rfn = NULL, *dfn = NULL, *mfn = NULL,
178 *freeme_tfn = NULL, *freeme_rfn = NULL, *freeme_mfn = NULL;
179 int n = 0;
180 size_t len;
183 * break filter into words delimited by whitespace so that we can
184 * look for tokens. First we count how many words.
186 if((bp = cpystr(filter)) != NULL)
187 p = strtok(bp, " \t");
189 if(p){
190 n++;
191 while(strtok(NULL, " \t") != NULL)
192 n++;
195 if(!n){
196 dprint((1, "Unexpected failure creating sending_filter\n"));
197 if(bp)
198 fs_give((void **)&bp);
200 return(cmd);
203 q = array = (char **) fs_get((n+1) * sizeof(*array));
204 memset(array, 0, (n+1) * sizeof(*array));
205 /* restore bp and form the array */
206 strncpy(bp, filter, strlen(filter)+1);
207 if((p = strtok(bp, " \t")) != NULL){
208 if(q-array < n+1)
209 *q++ = cpystr(p);
211 while((p = strtok(NULL, " \t")) != NULL && (q-array < n+1))
212 *q++ = cpystr(p);
215 if(bp)
216 fs_give((void **)&bp);
218 for(q = array; *q != NULL; q++){
219 if(!strcmp(*q, "_RECIPIENTS_")){
220 char *rl = NULL;
222 if(env){
223 size_t l;
225 char *to_l = addr_list_string(env->to,
226 simple_addr_string, 0),
227 *cc_l = addr_list_string(env->cc,
228 simple_addr_string, 0),
229 *bcc_l = addr_list_string(env->bcc,
230 simple_addr_string, 0);
232 l = strlen(to_l) + strlen(cc_l) + strlen(bcc_l) + 2;
233 rl = (char *) fs_get((l+1) * sizeof(char));
234 snprintf(rl, l+1, "%s %s %s", to_l, cc_l, bcc_l);
235 fs_give((void **)&to_l);
236 fs_give((void **)&cc_l);
237 fs_give((void **)&bcc_l);
238 for(to_l = rl; *to_l; to_l++) /* to_l overloaded! */
239 if(*to_l == ',') /* space delim'd list */
240 *to_l = ' ';
243 fs_give((void **)q);
244 *q = rl ? rl : cpystr("");
246 else if(!strcmp(*q, "_TMPFILE_")){
247 if(!tfn){
248 tfn = temp_nam(NULL, "sf"); /* send filter file */
249 if(!tfn)
250 dprint((1, "FAILED creat of _TMPFILE_\n"));
253 if(tmpf)
254 *tmpf = tfn;
255 else
256 freeme_tfn = tfn;
258 fs_give((void **)q);
259 *q = cpystr(tfn ? tfn : "");
261 else if(!strcmp(*q, "_RESULTFILE_")){
262 if(!rfn){
263 rfn = temp_nam(NULL, "rf");
265 * We don't create the result file, the user does.
266 * That means we have to remove it after temp_nam creates it.
268 if(rfn)
269 our_unlink(rfn);
270 else
271 dprint((1, "FAILED creat of _RESULTFILE_\n"));
274 if(resultf)
275 *resultf = rfn;
276 else
277 freeme_rfn = rfn;
279 fs_give((void **)q);
280 *q = cpystr(rfn ? rfn : "");
282 else if(!strcmp(*q, "_MIMETYPE_")){
283 if(!mfn){
284 mfn = temp_nam(NULL, "mt");
286 * We don't create the mimetype file, the user does.
287 * That means we have to remove it after temp_nam creates it.
289 if(mfn)
290 our_unlink(mfn);
291 else
292 dprint((1, "FAILED creat of _MIMETYPE_\n"));
295 if(mtypef)
296 *mtypef = mfn;
297 else
298 freeme_mfn = mfn;
300 fs_give((void **)q);
301 *q = cpystr(mfn ? mfn : "");
303 else if(!strcmp(*q, "_DATAFILE_")){
304 if((dfn = filter_data_file(1)) == NULL) /* filter data file */
305 dprint((1, "FAILED creat of _DATAFILE_\n"));
307 fs_give((void **)q);
308 *q = cpystr(dfn ? dfn : "");
310 else if(!strcmp(*q, "_PREPENDKEY_")){
311 (*q)[0] = '\0';
312 if(key)
313 *key = 1;
315 else if(!strcmp(*q, "_INCLUDEALLHDRS_")){
316 (*q)[0] = '\0';
317 if(hdrs)
318 *hdrs = 1;
320 else if(!strcmp(*q, "_SILENT_")){
321 (*q)[0] = '\0';
322 if(silent)
323 *silent = 1;
327 /* count up required length */
328 for(len = 0, q = array; *q != NULL; q++)
329 len += (strlen(*q)+1);
331 cmd = fs_get((len+1) * sizeof(char));
332 cmd[len] = '\0';
334 /* cat together all the args */
335 p = cmd;
336 for(q = array; *q != NULL; q++){
337 sstrncpy(&p, *q, len+1-(p-cmd));
338 sstrncpy(&p, " ", len+1-(p-cmd));
341 cmd[len] = '\0';
343 if(freeme_rfn)
344 fs_give((void **) &freeme_rfn);
346 if(freeme_tfn){ /* this shouldn't happen */
347 our_unlink(freeme_tfn);
348 fs_give((void **) &freeme_tfn);
351 if(freeme_mfn)
352 fs_give((void **) &freeme_mfn);
354 free_list_array(&array);
356 return(cmd);
361 * filter_session_key - function to return randomly generated number
362 * representing a key for this session. The idea is
363 * the display/sending filter could use it to muddle
364 * up any pass phrase or such stored in the
365 * "_DATAFILE_".
367 char *
368 filter_session_key(void)
370 static char *key = NULL;
372 if(!key){
373 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%ld", random());
374 key = cpystr(tmp_20k_buf);
377 return(key);
384 * filter_data_file - function to return filename of scratch file for
385 * display and sending filters. This file is created
386 * the first time it's needed, and persists until pine
387 * exits.
389 char *
390 filter_data_file(int create_it)
392 static char *fn = NULL;
394 if(!fn && create_it)
395 fn = temp_nam(NULL, "df");
397 return(fn);
402 * df_static_trigger - look thru the display filter list and set the
403 * filter to any triggers that don't require scanning
404 * the message segment.
406 char *
407 dfilter_trigger(struct mail_bodystruct *body, char *cmdbuf, size_t cmdbuflen)
409 int passed = 0;
410 char **l, *test, *cmd;
412 for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l && !passed; l++){
414 get_pair(*l, &test, &cmd, 1, 1);
416 dprint((5, "static trigger: \"%s\" --> \"%s\" and \"%s\"",
417 (l && *l) ? *l : "?",
418 test ? test : "<NULL>", cmd ? cmd : "<NULL>"));
420 if((passed = (df_valid_test(body, test) && valid_filter_command(&cmd))) != 0){
421 strncpy(cmdbuf, cmd, cmdbuflen);
422 cmdbuf[cmdbuflen-1] = '\0';
425 fs_give((void **) &test);
426 fs_give((void **) &cmd);
429 return(passed ? cmdbuf : NULL);
435 df_valid_test(struct mail_bodystruct *body, char *test)
437 int passed = 0;
439 if(!(passed = !test)){ /* NO test always wins */
440 if(!*test){
441 passed++; /* NULL test also wins! */
443 else if(body && !struncmp(test, "_CHARSET(", 9)){
444 char *p = strrindex(test, ')');
446 if(p){
447 *p = '\0'; /* tie off user charset */
448 if((p = parameter_val(body->parameter,"charset")) != NULL){
449 passed = !strucmp(test + 9, p);
450 fs_give((void **) &p);
452 else
453 passed = !strucmp(test + 9, "us-ascii");
455 else
456 dprint((1,
457 "filter trigger: malformed test: %s\n",
458 test ? test : "?"));
462 return(passed);