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
4 
5 /*
6  * ========================================================================
7  * Copyright 2006-2008 University of Washington
8  * Copyright 2013-2021 Eduardo Chappa
9  *
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
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
19 /*======================================================================
20      dispfilt.c
21      Display filter technology ;)
22 
23   ====*/
24 
25 #include "headers.h"
26 
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"
35 
36 #include "mailview.h"
37 #include "signal.h"
38 #include "busy.h"
39 #include "dispfilt.h"
40 
41 
42 /* internal prototypes */
43 int	df_valid_test(BODY *, char *);
44 
45 
46 
47 /*
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.
51  *
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.
55  */
56 char *
dfilter(char * rawcmd,STORE_S * input_so,gf_io_t output_pc,FILTLIST_S * aux_filters)57 dfilter(char *rawcmd, STORE_S *input_so, gf_io_t output_pc, FILTLIST_S *aux_filters)
58 {
59     char *status = NULL, *cmd, *resultf = NULL, *tmpfile = NULL;
60     int   key = 0, silent = 0;
61 
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();
68 	}
69 	fflush(stdout);
70 #endif
71 
72 	/*
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>
78 	 */
79 	if(tmpfile){
80 	    PIPE_S	  *filter_pipe;
81 	    FILE          *fp;
82 	    gf_io_t	   gc, pc;
83 	    STORE_S       *tmpf_so;
84 
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);
91 		}
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);
101 
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);
117 
118 				status = gf_pipe(gc, output_pc);
119 				fclose(fp);
120 			    }
121 			    else
122 			      status = "Can't read result of display filter";
123 			}
124 			else
125 			  status = "Filter command returned error.";
126 		    }
127 		    else
128 		      status = "Can't open pipe for display filter";
129 		}
130 
131 		our_unlink(tmpfile);
132 	    }
133 	    else
134 	      status = "Can't open display filter tmp file";
135 	}
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;
141 
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);
146 	}
147 
148 	if(resultf){
149 	    if(name_file_size(resultf) > 0L)
150 	      display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
151 	    our_unlink(resultf);
152 	    fs_give((void **)&resultf);
153 	}
154 
155 	resume_busy_cue(0);
156 #ifndef	_WINDOWS
157 	if(!silent)
158 	  ClearScreen();
159 #endif
160 	fs_give((void **)&cmd);
161     }
162 
163     return(status);
164 }
165 
166 
167 /*
168  * expand_filter_tokens - return an alloc'd string with any special tokens
169  *			  in the given filter expanded, NULL otherwise.
170  */
171 char *
expand_filter_tokens(char * filter,ENVELOPE * env,char ** tmpf,char ** resultf,char ** mtypef,int * key,int * hdrs,int * silent)172 expand_filter_tokens(char *filter, ENVELOPE *env, char **tmpf, char **resultf,
173 		     char **mtypef, int *key, int *hdrs, int *silent)
174 {
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;
181 
182     /*
183      * break filter into words delimited by whitespace so that we can
184      * look for tokens. First we count how many words.
185      */
186     if((bp = cpystr(filter)) != NULL)
187       p = strtok(bp, " \t");
188 
189     if(p){
190 	n++;
191 	while(strtok(NULL, " \t") != NULL)
192 	  n++;
193     }
194 
195     if(!n){
196 	dprint((1, "Unexpected failure creating sending_filter\n"));
197 	if(bp)
198 	  fs_give((void **)&bp);
199 
200 	return(cmd);
201     }
202 
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);
210 
211 	while((p = strtok(NULL, " \t")) != NULL && (q-array < n+1))
212 	  *q++ = cpystr(p);
213     }
214 
215     if(bp)
216       fs_give((void **)&bp);
217 
218     for(q = array; *q != NULL; q++){
219 	if(!strcmp(*q, "_RECIPIENTS_")){
220 	    char *rl = NULL;
221 
222 	    if(env){
223 		size_t l;
224 
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);
231 
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 = ' ';
241 	    }
242 
243 	    fs_give((void **)q);
244 	    *q = rl ? rl : cpystr("");
245 	}
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"));
251 	    }
252 
253 	    if(tmpf)
254 	      *tmpf = tfn;
255 	    else
256 	      freeme_tfn = tfn;
257 
258 	    fs_give((void **)q);
259 	    *q = cpystr(tfn ? tfn : "");
260 	}
261 	else if(!strcmp(*q, "_RESULTFILE_")){
262 	    if(!rfn){
263 		rfn = temp_nam(NULL, "rf");
264 		/*
265 		 * We don't create the result file, the user does.
266 		 * That means we have to remove it after temp_nam creates it.
267 		 */
268 		if(rfn)
269 		  our_unlink(rfn);
270 		else
271 		  dprint((1, "FAILED creat of _RESULTFILE_\n"));
272 	    }
273 
274 	    if(resultf)
275 	      *resultf = rfn;
276 	    else
277 	      freeme_rfn = rfn;
278 
279 	    fs_give((void **)q);
280 	    *q = cpystr(rfn ? rfn : "");
281 	}
282 	else if(!strcmp(*q, "_MIMETYPE_")){
283 	    if(!mfn){
284 		mfn = temp_nam(NULL, "mt");
285 		/*
286 		 * We don't create the mimetype file, the user does.
287 		 * That means we have to remove it after temp_nam creates it.
288 		 */
289 		if(mfn)
290 		  our_unlink(mfn);
291 		else
292 		  dprint((1, "FAILED creat of _MIMETYPE_\n"));
293 	    }
294 
295 	    if(mtypef)
296 	      *mtypef = mfn;
297 	    else
298 	      freeme_mfn = mfn;
299 
300 	    fs_give((void **)q);
301 	    *q = cpystr(mfn ? mfn : "");
302 	}
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"));
306 
307 	    fs_give((void **)q);
308 	    *q = cpystr(dfn ? dfn : "");
309 	}
310 	else if(!strcmp(*q, "_PREPENDKEY_")){
311 	    (*q)[0] = '\0';
312 	    if(key)
313 	      *key = 1;
314 	}
315 	else if(!strcmp(*q, "_INCLUDEALLHDRS_")){
316 	    (*q)[0] = '\0';
317 	    if(hdrs)
318 	      *hdrs = 1;
319 	}
320 	else if(!strcmp(*q, "_SILENT_")){
321 	    (*q)[0] = '\0';
322 	    if(silent)
323 	      *silent = 1;
324 	}
325     }
326 
327     /* count up required length */
328     for(len = 0, q = array; *q != NULL; q++)
329       len += (strlen(*q)+1);
330 
331     cmd = fs_get((len+1) * sizeof(char));
332     cmd[len] = '\0';
333 
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));
339     }
340 
341     cmd[len] = '\0';
342 
343     if(freeme_rfn)
344       fs_give((void **) &freeme_rfn);
345 
346     if(freeme_tfn){			/* this shouldn't happen */
347 	our_unlink(freeme_tfn);
348 	fs_give((void **) &freeme_tfn);
349     }
350 
351     if(freeme_mfn)
352       fs_give((void **) &freeme_mfn);
353 
354     free_list_array(&array);
355 
356     return(cmd);
357 }
358 
359 
360 /*
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_".
366  */
367 char *
filter_session_key(void)368 filter_session_key(void)
369 {
370     static char *key = NULL;
371 
372     if(!key){
373 	snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%ld", random());
374 	key = cpystr(tmp_20k_buf);
375     }
376 
377     return(key);
378 }
379 
380 
381 
382 
383 /*
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.
388  */
389 char *
filter_data_file(int create_it)390 filter_data_file(int create_it)
391 {
392     static char *fn = NULL;
393 
394     if(!fn && create_it)
395       fn = temp_nam(NULL, "df");
396 
397     return(fn);
398 }
399 
400 
401 /*
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.
405  */
406 char *
dfilter_trigger(struct mail_bodystruct * body,char * cmdbuf,size_t cmdbuflen)407 dfilter_trigger(struct mail_bodystruct *body, char *cmdbuf, size_t cmdbuflen)
408 {
409     int    passed = 0;
410     char **l, *test, *cmd;
411 
412     for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l && !passed; l++){
413 
414 	get_pair(*l, &test, &cmd, 1, 1);
415 
416 	dprint((5, "static trigger: \"%s\" --> \"%s\" and \"%s\"",
417 		   (l && *l) ? *l : "?",
418 		   test ? test : "<NULL>", cmd ? cmd : "<NULL>"));
419 
420 	if((passed = (df_valid_test(body, test) && valid_filter_command(&cmd))) != 0){
421 	  strncpy(cmdbuf, cmd, cmdbuflen);
422 	  cmdbuf[cmdbuflen-1] = '\0';
423 	}
424 
425 	fs_give((void **) &test);
426 	fs_give((void **) &cmd);
427     }
428 
429     return(passed ? cmdbuf : NULL);
430 }
431 
432 
433 
434 int
df_valid_test(struct mail_bodystruct * body,char * test)435 df_valid_test(struct mail_bodystruct *body, char *test)
436 {
437     int passed = 0;
438 
439     if(!(passed = !test)){			/* NO test always wins */
440 	if(!*test){
441 	    passed++;				/* NULL test also wins! */
442 	}
443 	else if(body && !struncmp(test, "_CHARSET(", 9)){
444 	    char *p = strrindex(test, ')');
445 
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);
451 		}
452 		else
453 		  passed = !strucmp(test + 9, "us-ascii");
454 	    }
455 	    else
456 	      dprint((1,
457 			 "filter trigger: malformed test: %s\n",
458 			 test ? test : "?"));
459 	}
460     }
461 
462     return(passed);
463 }
464