1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: dispfilt.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2015 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 /*======================================================================
21 Display filter technology ;)
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"
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
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.
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
){
66 ps_global
->mangled_screen
= 1;
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>
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
){
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
);
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 */
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
);
114 for( ; aux_filters
->filter
; aux_filters
++)
115 gf_link_filter(aux_filters
->filter
,
118 status
= gf_pipe(gc
, output_pc
);
122 status
= "Can't read result of display filter";
125 status
= "Filter command returned error.";
128 status
= "Can't open pipe for display filter";
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
){
142 fprintf(stdout
,"\r\n%s Hit return to continue.", status
);
144 while((ch
= read_char(300)) != ctrl('M') && ch
!= NO_OP_IDLE
)
149 if(name_file_size(resultf
) > 0L)
150 display_output_file(resultf
, "Filter", NULL
, DOF_BRIEF
);
152 fs_give((void **)&resultf
);
160 fs_give((void **)&cmd
);
168 * expand_filter_tokens - return an alloc'd string with any special tokens
169 * in the given filter expanded, NULL otherwise.
172 expand_filter_tokens(char *filter
, ENVELOPE
*env
, char **tmpf
, char **resultf
,
173 char **mtypef
, int *key
, int *hdrs
, int *silent
)
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
;
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");
191 while(strtok(NULL
, " \t") != NULL
)
196 dprint((1, "Unexpected failure creating sending_filter\n"));
198 fs_give((void **)&bp
);
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
){
211 while((p
= strtok(NULL
, " \t")) != NULL
&& (q
-array
< n
+1))
216 fs_give((void **)&bp
);
218 for(q
= array
; *q
!= NULL
; q
++){
219 if(!strcmp(*q
, "_RECIPIENTS_")){
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 */
244 *q
= rl
? rl
: cpystr("");
246 else if(!strcmp(*q
, "_TMPFILE_")){
248 tfn
= temp_nam(NULL
, "sf"); /* send filter file */
250 dprint((1, "FAILED creat of _TMPFILE_\n"));
259 *q
= cpystr(tfn
? tfn
: "");
261 else if(!strcmp(*q
, "_RESULTFILE_")){
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.
271 dprint((1, "FAILED creat of _RESULTFILE_\n"));
280 *q
= cpystr(rfn
? rfn
: "");
282 else if(!strcmp(*q
, "_MIMETYPE_")){
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.
292 dprint((1, "FAILED creat of _MIMETYPE_\n"));
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"));
308 *q
= cpystr(dfn
? dfn
: "");
310 else if(!strcmp(*q
, "_PREPENDKEY_")){
315 else if(!strcmp(*q
, "_INCLUDEALLHDRS_")){
320 else if(!strcmp(*q
, "_SILENT_")){
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));
334 /* cat together all the args */
336 for(q
= array
; *q
!= NULL
; q
++){
337 sstrncpy(&p
, *q
, len
+1-(p
-cmd
));
338 sstrncpy(&p
, " ", len
+1-(p
-cmd
));
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
);
352 fs_give((void **) &freeme_mfn
);
354 free_list_array(&array
);
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
368 filter_session_key(void)
370 static char *key
= NULL
;
373 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%ld", random());
374 key
= cpystr(tmp_20k_buf
);
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
390 filter_data_file(int create_it
)
392 static char *fn
= NULL
;
395 fn
= temp_nam(NULL
, "df");
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.
407 dfilter_trigger(struct mail_bodystruct
*body
, char *cmdbuf
, size_t cmdbuflen
)
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
)
439 if(!(passed
= !test
)){ /* NO test always wins */
441 passed
++; /* NULL test also wins! */
443 else if(body
&& !struncmp(test
, "_CHARSET(", 9)){
444 char *p
= strrindex(test
, ')');
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
);
453 passed
= !strucmp(test
+ 9, "us-ascii");
457 "filter trigger: malformed test: %s\n",