Line data Source code
1 : /*********************************************************************
2 : *
3 : * File : $Source: /cvsroot/ijbswa/current/actions.c,v $
4 : *
5 : * Purpose : Declares functions to work with actions files
6 : *
7 : * Copyright : Written by and Copyright (C) 2001-2016 the
8 : * Privoxy team. https://www.privoxy.org/
9 : *
10 : * Based on the Internet Junkbuster originally written
11 : * by and Copyright (C) 1997 Anonymous Coders and
12 : * Junkbusters Corporation. http://www.junkbusters.com
13 : *
14 : * This program is free software; you can redistribute it
15 : * and/or modify it under the terms of the GNU General
16 : * Public License as published by the Free Software
17 : * Foundation; either version 2 of the License, or (at
18 : * your option) any later version.
19 : *
20 : * This program is distributed in the hope that it will
21 : * be useful, but WITHOUT ANY WARRANTY; without even the
22 : * implied warranty of MERCHANTABILITY or FITNESS FOR A
23 : * PARTICULAR PURPOSE. See the GNU General Public
24 : * License for more details.
25 : *
26 : * The GNU General Public License should be included with
27 : * this file. If not, you can view it at
28 : * http://www.gnu.org/copyleft/gpl.html
29 : * or write to the Free Software Foundation, Inc., 59
30 : * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 : *
32 : *********************************************************************/
33 :
34 :
35 : #include "config.h"
36 :
37 : #include <stdio.h>
38 : #include <string.h>
39 : #include <assert.h>
40 : #include <stdlib.h>
41 :
42 : #ifdef FEATURE_PTHREAD
43 : #include <pthread.h>
44 : #endif
45 :
46 : #include "project.h"
47 : #include "jcc.h"
48 : #include "list.h"
49 : #include "actions.h"
50 : #include "miscutil.h"
51 : #include "errlog.h"
52 : #include "loaders.h"
53 71410 : #include "encode.h"
54 71410 : #include "urlmatch.h"
55 : #include "cgi.h"
56 71410 : #include "ssplit.h"
57 : #include "filters.h"
58 :
59 108868 : /*
60 221223 : * We need the main list of options.
61 108869 : *
62 71410 : * First, we need a way to tell between boolean, string, and multi-string
63 : * options. For string and multistring options, we also need to be
64 71410 : * able to tell the difference between a "+" and a "-". (For bools,
65 : * the "+"/"-" information is encoded in "add" and "mask"). So we use
66 71410 : * an enumerated type (well, the preprocessor equivalent). Here are
67 71410 : * the values:
68 71410 : */
69 71410 : enum action_value_type {
70 : AV_NONE = 0, /* +opt -opt */
71 71410 : AV_ADD_STRING = 1, /* +stropt{string} */
72 : AV_REM_STRING = 2, /* -stropt */
73 71410 : AV_ADD_MULTI = 3, /* +multiopt{string} +multiopt{string2} */
74 : AV_REM_MULTI = 4 /* -multiopt{string} -multiopt */
75 : };
76 71410 :
77 : /*
78 71410 : * We need a structure to hold the name, flag changes,
79 : * type, and string index.
80 : */
81 71410 : struct action_name
82 : {
83 : const char * name;
84 : unsigned long mask; /* a bit set to "0" = remove action */
85 141034 : unsigned long add; /* a bit set to "1" = add action */
86 71410 : enum action_value_type value_type; /* an AV_... constant */
87 71410 : int index; /* index into strings[] or multi[] */
88 : };
89 71410 :
90 71410 : /*
91 71410 : * And with those building blocks in place, here's the array.
92 : */
93 : static const struct action_name action_names[] =
94 71410 : {
95 : /*
96 : * Well actually there's no data here - it's in actionlist.h
97 71410 : * This keeps it together to make it easy to change.
98 : *
99 : * Here's the macros used to format it:
100 71410 : */
101 : #define DEFINE_ACTION_MULTI(name,index) \
102 : { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
103 71410 : { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
104 : #define DEFINE_ACTION_STRING(name,flag,index) \
105 : { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
106 : { "-" name, ~flag, 0, AV_REM_STRING, index },
107 : #define DEFINE_ACTION_BOOL(name,flag) \
108 : { "+" name, ACTION_MASK_ALL, flag }, \
109 71410 : { "-" name, ~flag, 0 },
110 : #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
111 :
112 71410 : #include "actionlist.h"
113 71410 :
114 : #undef DEFINE_ACTION_MULTI
115 71410 : #undef DEFINE_ACTION_STRING
116 : #undef DEFINE_ACTION_BOOL
117 71410 : #undef DEFINE_ACTION_ALIAS
118 :
119 71410 : { NULL, 0, 0 } /* End marker */
120 : };
121 :
122 :
123 71410 : #ifndef FUZZ
124 71410 : static
125 : #endif
126 108874 : int load_one_actions_file(struct client_state *csp, int fileid);
127 108867 :
128 71410 :
129 71410 : /*********************************************************************
130 : *
131 : * Function : merge_actions
132 : *
133 71410 : * Description : Merge two actions together.
134 : * Similar to "dest += src".
135 : *
136 : * Parameters :
137 : * 1 : dest = Actions to modify.
138 : * 2 : src = Action to add.
139 : *
140 : * Returns : JB_ERR_OK or JB_ERR_MEMORY
141 : *
142 : *********************************************************************/
143 9632 : jb_err merge_actions (struct action_spec *dest,
144 : const struct action_spec *src)
145 : {
146 : int i;
147 : jb_err err;
148 :
149 9632 : dest->mask &= src->mask;
150 9632 : dest->add &= src->mask;
151 9632 : dest->add |= src->add;
152 :
153 202272 : for (i = 0; i < ACTION_STRING_COUNT; i++)
154 : {
155 192640 : char * str = src->string[i];
156 192640 : if (str)
157 : {
158 4816 : freez(dest->string[i]);
159 4816 : dest->string[i] = strdup_or_die(str);
160 : }
161 : }
162 :
163 96320 : for (i = 0; i < ACTION_MULTI_COUNT; i++)
164 : {
165 86688 : if (src->multi_remove_all[i])
166 : {
167 : /* Remove everything from dest */
168 0 : list_remove_all(dest->multi_remove[i]);
169 0 : dest->multi_remove_all[i] = 1;
170 :
171 0 : err = list_duplicate(dest->multi_add[i], src->multi_add[i]);
172 : }
173 86688 : else if (dest->multi_remove_all[i])
174 : {
175 : /*
176 : * dest already removes everything, so we only need to worry
177 : * about what we add.
178 : */
179 0 : list_remove_list(dest->multi_add[i], src->multi_remove[i]);
180 0 : err = list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
181 : }
182 : else
183 : {
184 : /* No "remove all"s to worry about. */
185 86688 : list_remove_list(dest->multi_add[i], src->multi_remove[i]);
186 86688 : err = list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
187 86688 : if (!err) err = list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
188 : }
189 :
190 86688 : if (err)
191 : {
192 0 : return err;
193 : }
194 : }
195 :
196 9632 : return JB_ERR_OK;
197 : }
198 :
199 :
200 : /*********************************************************************
201 : *
202 : * Function : copy_action
203 : *
204 : * Description : Copy an action_specs.
205 : * Similar to "dest = src".
206 : *
207 : * Parameters :
208 : * 1 : dest = Destination of copy.
209 : * 2 : src = Source for copy.
210 : *
211 : * Returns : JB_ERR_OK or JB_ERR_MEMORY
212 : *
213 : *********************************************************************/
214 0 : jb_err copy_action (struct action_spec *dest,
215 : const struct action_spec *src)
216 : {
217 : int i;
218 0 : jb_err err = JB_ERR_OK;
219 :
220 0 : free_action(dest);
221 0 : memset(dest, '\0', sizeof(*dest));
222 :
223 0 : dest->mask = src->mask;
224 0 : dest->add = src->add;
225 :
226 0 : for (i = 0; i < ACTION_STRING_COUNT; i++)
227 : {
228 0 : char * str = src->string[i];
229 0 : if (str)
230 : {
231 0 : str = strdup_or_die(str);
232 0 : dest->string[i] = str;
233 : }
234 : }
235 :
236 0 : for (i = 0; i < ACTION_MULTI_COUNT; i++)
237 : {
238 0 : dest->multi_remove_all[i] = src->multi_remove_all[i];
239 0 : err = list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
240 0 : if (err)
241 : {
242 0 : return err;
243 : }
244 0 : err = list_duplicate(dest->multi_add[i], src->multi_add[i]);
245 0 : if (err)
246 : {
247 0 : return err;
248 : }
249 : }
250 0 : return err;
251 : }
252 :
253 : /*********************************************************************
254 : *
255 : * Function : free_action_spec
256 : *
257 : * Description : Frees an action_spec and the memory used by it.
258 : *
259 : * Parameters :
260 : * 1 : src = Source to free.
261 : *
262 : * Returns : N/A
263 : *
264 : *********************************************************************/
265 0 : void free_action_spec(struct action_spec *src)
266 : {
267 0 : free_action(src);
268 0 : freez(src);
269 0 : }
270 :
271 :
272 : /*********************************************************************
273 : *
274 : * Function : free_action
275 : *
276 : * Description : Destroy an action_spec. Frees memory used by it,
277 : * except for the memory used by the struct action_spec
278 : * itself.
279 : *
280 : * Parameters :
281 : * 1 : src = Source to free.
282 : *
283 : * Returns : N/A
284 : *
285 : *********************************************************************/
286 69882 : void free_action (struct action_spec *src)
287 : {
288 : int i;
289 :
290 69882 : if (src == NULL)
291 : {
292 0 : return;
293 : }
294 :
295 1467522 : for (i = 0; i < ACTION_STRING_COUNT; i++)
296 : {
297 1397640 : freez(src->string[i]);
298 : }
299 :
300 698820 : for (i = 0; i < ACTION_MULTI_COUNT; i++)
301 : {
302 628938 : destroy_list(src->multi_remove[i]);
303 628938 : destroy_list(src->multi_add[i]);
304 : }
305 :
306 69882 : memset(src, '\0', sizeof(*src));
307 : }
308 :
309 :
310 : /*********************************************************************
311 : *
312 : * Function : get_action_token
313 : *
314 : * Description : Parses a line for the first action.
315 : * Modifies its input array, doesn't allocate memory.
316 : * e.g. given:
317 : * *line=" +abc{def} -ghi "
318 : * Returns:
319 : * *line=" -ghi "
320 : * *name="+abc"
321 : * *value="def"
322 : *
323 : * Parameters :
324 : * 1 : line = [in] The line containing the action.
325 : * [out] Start of next action on line, or
326 : * NULL if we reached the end of line before
327 : * we found an action.
328 : * 2 : name = [out] Start of action name, null
329 : * terminated. NULL on EOL
330 : * 3 : value = [out] Start of action value, null
331 : * terminated. NULL if none or EOL.
332 : *
333 : * Returns : JB_ERR_OK => Ok
334 : * JB_ERR_PARSE => Mismatched {} (line was trashed anyway)
335 : *
336 : *********************************************************************/
337 492534 : jb_err get_action_token(char **line, char **name, char **value)
338 : {
339 492534 : char * str = *line;
340 : char ch;
341 :
342 : /* set default returns */
343 492534 : *line = NULL;
344 492534 : *name = NULL;
345 492534 : *value = NULL;
346 :
347 : /* Eat any leading whitespace */
348 724420 : while ((*str == ' ') || (*str == '\t'))
349 : {
350 231886 : str++;
351 : }
352 :
353 492534 : if (*str == '\0')
354 : {
355 131832 : return 0;
356 : }
357 :
358 360702 : if (*str == '{')
359 : {
360 : /* null name, just value is prohibited */
361 0 : return JB_ERR_PARSE;
362 : }
363 :
364 360702 : *name = str;
365 :
366 : /* parse option */
367 5972412 : while (((ch = *str) != '\0') &&
368 5825682 : (ch != ' ') && (ch != '\t') && (ch != '{'))
369 : {
370 5611710 : if (ch == '}')
371 : {
372 : /* error, '}' without '{' */
373 0 : return JB_ERR_PARSE;
374 : }
375 5611710 : str++;
376 : }
377 360702 : *str = '\0';
378 :
379 360702 : if (ch != '{')
380 : {
381 : /* no value */
382 146730 : if (ch == '\0')
383 : {
384 : /* EOL - be careful not to run off buffer */
385 87138 : *line = str;
386 : }
387 : else
388 : {
389 : /* More to parse next time. */
390 59592 : *line = str + 1;
391 : }
392 146730 : return JB_ERR_OK;
393 : }
394 :
395 213972 : str++;
396 213972 : *value = str;
397 :
398 : /* The value ends with the first non-escaped closing curly brace */
399 213972 : while ((str = strchr(str, '}')) != NULL)
400 : {
401 213972 : if (str[-1] == '\\')
402 : {
403 : /* Overwrite the '\' so the action doesn't see it. */
404 0 : string_move(str-1, str);
405 0 : continue;
406 : }
407 213972 : break;
408 : }
409 213972 : if (str == NULL)
410 : {
411 : /* error */
412 0 : *value = NULL;
413 0 : return JB_ERR_PARSE;
414 : }
415 :
416 : /* got value */
417 213972 : *str = '\0';
418 213972 : *line = str + 1;
419 :
420 213972 : chomp(*value);
421 :
422 213972 : return JB_ERR_OK;
423 : }
424 :
425 : /*********************************************************************
426 : *
427 : * Function : action_used_to_be_valid
428 : *
429 : * Description : Checks if unrecognized actions were valid in earlier
430 : * releases.
431 : *
432 : * Parameters :
433 : * 1 : action = The string containing the action to check.
434 : *
435 : * Returns : True if yes, otherwise false.
436 : *
437 : *********************************************************************/
438 0 : static int action_used_to_be_valid(const char *action)
439 : {
440 : static const char * const formerly_valid_actions[] = {
441 : "inspect-jpegs",
442 : "kill-popups",
443 : "send-vanilla-wafer",
444 : "send-wafer",
445 : "treat-forbidden-connects-like-blocks",
446 : "vanilla-wafer",
447 : "wafer"
448 : };
449 : unsigned int i;
450 :
451 0 : for (i = 0; i < SZ(formerly_valid_actions); i++)
452 : {
453 0 : if (0 == strcmpic(action, formerly_valid_actions[i]))
454 : {
455 0 : return TRUE;
456 : }
457 : }
458 :
459 0 : return FALSE;
460 : }
461 :
462 : /*********************************************************************
463 : *
464 : * Function : get_actions
465 : *
466 : * Description : Parses a list of actions.
467 : *
468 : * Parameters :
469 : * 1 : line = The string containing the actions.
470 : * Will be written to by this function.
471 : * 2 : alias_list = Custom alias list, or NULL for none.
472 : * 3 : cur_action = Where to store the action. Caller
473 : * allocates memory.
474 : *
475 : * Returns : JB_ERR_OK => Ok
476 : * JB_ERR_PARSE => Parse error (line was trashed anyway)
477 : * nonzero => Out of memory (line was trashed anyway)
478 : *
479 : *********************************************************************/
480 131832 : jb_err get_actions(char *line,
481 : struct action_alias * alias_list,
482 : struct action_spec *cur_action)
483 : {
484 : jb_err err;
485 131832 : init_action(cur_action);
486 131832 : cur_action->mask = ACTION_MASK_ALL;
487 :
488 624366 : while (line)
489 : {
490 492534 : char * option = NULL;
491 492534 : char * value = NULL;
492 :
493 492534 : err = get_action_token(&line, &option, &value);
494 492534 : if (err)
495 : {
496 0 : return err;
497 : }
498 :
499 492534 : if (option)
500 : {
501 : /* handle option in 'option' */
502 :
503 : /* Check for standard action name */
504 360702 : const struct action_name * action = action_names;
505 :
506 12745994 : while ((action->name != NULL) && (0 != strcmpic(action->name, option)))
507 : {
508 12385292 : action++;
509 : }
510 360702 : if (action->name != NULL)
511 : {
512 : /* Found it */
513 351070 : cur_action->mask &= action->mask;
514 351070 : cur_action->add &= action->mask;
515 351070 : cur_action->add |= action->add;
516 :
517 351070 : switch (action->value_type)
518 : {
519 84122 : case AV_NONE:
520 84122 : if (value != NULL)
521 : {
522 0 : log_error(LOG_LEVEL_ERROR,
523 : "Action %s does not take parameters but %s was given.",
524 : action->name, value);
525 0 : return JB_ERR_PARSE;
526 : }
527 84122 : break;
528 117068 : case AV_ADD_STRING:
529 : {
530 : /* add single string. */
531 :
532 117068 : if ((value == NULL) || (*value == '\0'))
533 : {
534 0 : if (0 == strcmpic(action->name, "+block"))
535 : {
536 : /*
537 : * XXX: Temporary backwards compatibility hack.
538 : * XXX: should include line number.
539 : */
540 0 : value = "No reason specified.";
541 0 : log_error(LOG_LEVEL_ERROR,
542 : "block action without reason found. This may "
543 : "become a fatal error in future versions.");
544 : }
545 : else
546 : {
547 0 : return JB_ERR_PARSE;
548 : }
549 : }
550 : #ifdef FEATURE_EXTENDED_STATISTICS
551 117068 : if (0 == strcmpic(action->name, "+block"))
552 : {
553 14448 : register_block_reason_for_statistics(value);
554 : }
555 : #endif
556 : /* FIXME: should validate option string here */
557 117068 : freez (cur_action->string[action->index]);
558 117068 : cur_action->string[action->index] = strdup(value);
559 117068 : if (NULL == cur_action->string[action->index])
560 : {
561 0 : return JB_ERR_MEMORY;
562 : }
563 117068 : break;
564 : }
565 58692 : case AV_REM_STRING:
566 : {
567 : /* remove single string. */
568 :
569 58692 : freez (cur_action->string[action->index]);
570 58692 : break;
571 : }
572 76740 : case AV_ADD_MULTI:
573 : {
574 : /* append multi string. */
575 :
576 76740 : struct list * remove_p = cur_action->multi_remove[action->index];
577 76740 : struct list * add_p = cur_action->multi_add[action->index];
578 :
579 76740 : if ((value == NULL) || (*value == '\0'))
580 : {
581 0 : return JB_ERR_PARSE;
582 : }
583 :
584 76740 : list_remove_item(remove_p, value);
585 76740 : err = enlist_unique(add_p, value, 0);
586 76740 : if (err)
587 : {
588 0 : return err;
589 : }
590 76740 : break;
591 : }
592 14448 : case AV_REM_MULTI:
593 : {
594 : /* remove multi string. */
595 :
596 14448 : struct list * remove_p = cur_action->multi_remove[action->index];
597 14448 : struct list * add_p = cur_action->multi_add[action->index];
598 :
599 14448 : if ((value == NULL) || (*value == '\0')
600 9632 : || ((*value == '*') && (value[1] == '\0')))
601 : {
602 : /*
603 : * no option, or option == "*".
604 : *
605 : * Remove *ALL*.
606 : */
607 4816 : list_remove_all(remove_p);
608 4816 : list_remove_all(add_p);
609 4816 : cur_action->multi_remove_all[action->index] = 1;
610 : }
611 : else
612 : {
613 : /* Valid option - remove only 1 option */
614 :
615 9632 : if (!cur_action->multi_remove_all[action->index])
616 : {
617 : /* there isn't a catch-all in the remove list already */
618 9632 : err = enlist_unique(remove_p, value, 0);
619 9632 : if (err)
620 : {
621 0 : return err;
622 : }
623 : }
624 9632 : list_remove_item(add_p, value);
625 : }
626 14448 : break;
627 : }
628 0 : default:
629 : /* Shouldn't get here unless there's memory corruption. */
630 0 : assert(0);
631 : return JB_ERR_PARSE;
632 : }
633 : }
634 : else
635 : {
636 : /* try user aliases. */
637 9632 : const struct action_alias * alias = alias_list;
638 :
639 19264 : while ((alias != NULL) && (0 != strcmpic(alias->name, option)))
640 : {
641 9632 : alias = alias->next;
642 : }
643 9632 : if (alias != NULL)
644 : {
645 : /* Found it */
646 9632 : merge_actions(cur_action, alias->action);
647 : }
648 0 : else if (((size_t)2 < strlen(option)) && action_used_to_be_valid(option+1))
649 : {
650 0 : log_error(LOG_LEVEL_ERROR, "Action '%s' is no longer valid "
651 : "in this Privoxy release. Ignored.", option+1);
652 : }
653 0 : else if (((size_t)2 < strlen(option)) && 0 == strcmpic(option+1, "hide-forwarded-for-headers"))
654 : {
655 0 : log_error(LOG_LEVEL_FATAL, "The action 'hide-forwarded-for-headers' "
656 : "is no longer valid in this Privoxy release. "
657 : "Use 'change-x-forwarded-for' instead.");
658 : }
659 : else
660 : {
661 : /* Bad action name */
662 : /*
663 : * XXX: This is a fatal error and Privoxy will later on exit
664 : * in load_one_actions_file() because of an "invalid line".
665 : *
666 : * It would be preferable to name the offending option in that
667 : * error message, but currently there is no way to do that and
668 : * we have to live with two error messages for basically the
669 : * same reason.
670 : */
671 0 : log_error(LOG_LEVEL_ERROR, "Unknown action or alias: %s", option);
672 0 : return JB_ERR_PARSE;
673 : }
674 : }
675 : }
676 : }
677 :
678 131832 : return JB_ERR_OK;
679 : }
680 :
681 :
682 : /*********************************************************************
683 : *
684 : * Function : init_current_action
685 : *
686 : * Description : Zero out an action.
687 : *
688 : * Parameters :
689 : * 1 : dest = An uninitialized current_action_spec.
690 : *
691 : * Returns : N/A
692 : *
693 : *********************************************************************/
694 35897 : void init_current_action (struct current_action_spec *dest)
695 : {
696 35897 : memset(dest, '\0', sizeof(*dest));
697 :
698 35897 : dest->flags = ACTION_MOST_COMPATIBLE;
699 35897 : }
700 :
701 :
702 : /*********************************************************************
703 : *
704 : * Function : init_action
705 : *
706 : * Description : Zero out an action.
707 : *
708 : * Parameters :
709 : * 1 : dest = An uninitialized action_spec.
710 : *
711 : * Returns : N/A
712 : *
713 : *********************************************************************/
714 193792 : void init_action (struct action_spec *dest)
715 : {
716 193792 : memset(dest, '\0', sizeof(*dest));
717 193792 : }
718 :
719 :
720 : /*********************************************************************
721 : *
722 : * Function : merge_current_action
723 : *
724 : * Description : Merge two actions together.
725 : * Similar to "dest += src".
726 : * Differences between this and merge_actions()
727 : * is that this one doesn't allocate memory for
728 : * strings (so "src" better be in memory for at least
729 : * as long as "dest" is, and you'd better free
730 : * "dest" using "free_current_action").
731 : * Also, there is no mask or remove lists in dest.
732 : * (If we're applying it to a URL, we don't need them)
733 : *
734 : * Parameters :
735 : * 1 : dest = Current actions, to modify.
736 : * 2 : src = Action to add.
737 : *
738 : * Returns 0 : no error
739 : * !=0 : error, probably JB_ERR_MEMORY.
740 : *
741 : *********************************************************************/
742 79400 : jb_err merge_current_action (struct current_action_spec *dest,
743 : const struct action_spec *src)
744 : {
745 : int i;
746 79400 : jb_err err = JB_ERR_OK;
747 :
748 79400 : dest->flags &= src->mask;
749 79400 : dest->flags |= src->add;
750 :
751 1667400 : for (i = 0; i < ACTION_STRING_COUNT; i++)
752 : {
753 1588000 : char * str = src->string[i];
754 1588000 : if (str)
755 : {
756 418929 : str = strdup_or_die(str);
757 418929 : freez(dest->string[i]);
758 418929 : dest->string[i] = str;
759 : }
760 : }
761 :
762 794000 : for (i = 0; i < ACTION_MULTI_COUNT; i++)
763 : {
764 714600 : if (src->multi_remove_all[i])
765 : {
766 : /* Remove everything from dest, then add src->multi_add */
767 2489 : err = list_duplicate(dest->multi[i], src->multi_add[i]);
768 2489 : if (err)
769 : {
770 0 : return err;
771 : }
772 : }
773 : else
774 : {
775 712111 : list_remove_list(dest->multi[i], src->multi_remove[i]);
776 712111 : err = list_append_list_unique(dest->multi[i], src->multi_add[i]);
777 712111 : if (err)
778 : {
779 0 : return err;
780 : }
781 : }
782 : }
783 79400 : return err;
784 : }
785 :
786 :
787 : /*********************************************************************
788 : *
789 : * Function : update_action_bits_for_tag
790 : *
791 : * Description : Updates the action bits based on the action sections
792 : * whose tag patterns match a provided tag.
793 : *
794 : * Parameters :
795 : * 1 : csp = Current client state (buffers, headers, etc...)
796 : * 2 : tag = The tag on which the update should be based on
797 : *
798 : * Returns : 0 if no tag matched, or
799 : * 1 otherwise
800 : *
801 : *********************************************************************/
802 10729 : int update_action_bits_for_tag(struct client_state *csp, const char *tag)
803 : {
804 : struct file_list *fl;
805 : struct url_actions *b;
806 :
807 10729 : int updated = 0;
808 : int i;
809 :
810 10729 : assert(tag);
811 10729 : assert(list_contains_item(csp->tags, tag));
812 :
813 : /* Run through all action files, */
814 1083629 : for (i = 0; i < MAX_AF_FILES; i++)
815 : {
816 1072900 : if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
817 : {
818 : /* Skip empty files */
819 1051442 : continue;
820 : }
821 :
822 : /* and through all the action patterns, */
823 246767 : for (b = b->next; NULL != b; b = b->next)
824 : {
825 : /* skip everything but TAG patterns, */
826 225309 : if (!(b->url->flags & PATTERN_SPEC_TAG_PATTERN))
827 : {
828 225309 : continue;
829 : }
830 :
831 : /* and check if one of the tag patterns matches the tag, */
832 0 : if (0 == regexec(b->url->pattern.tag_regex, tag, 0, NULL, 0))
833 : {
834 : /* if it does, update the action bit map, */
835 0 : if (merge_current_action(csp->action, b->action))
836 : {
837 0 : log_error(LOG_LEVEL_ERROR,
838 : "Out of memory while changing action bits");
839 : }
840 : /* and signal the change. */
841 0 : updated = 1;
842 : }
843 : }
844 : }
845 :
846 10729 : return updated;
847 : }
848 :
849 :
850 : /*********************************************************************
851 : *
852 : * Function : check_negative_tag_patterns
853 : *
854 : * Description : Updates the action bits based on NO-*-TAG patterns.
855 : *
856 : * Parameters :
857 : * 1 : csp = Current client state (buffers, headers, etc...)
858 : * 2 : flag = The tag pattern type
859 : *
860 : * Returns : JB_ERR_OK in case off success, or
861 : * JB_ERR_MEMORY on out-of-memory error.
862 : *
863 : *********************************************************************/
864 39281 : jb_err check_negative_tag_patterns(struct client_state *csp, unsigned int flag)
865 : {
866 : struct list_entry *tag;
867 : struct file_list *fl;
868 39281 : struct url_actions *b = NULL;
869 : int i;
870 :
871 3967381 : for (i = 0; i < MAX_AF_FILES; i++)
872 : {
873 3928100 : fl = csp->actions_list[i];
874 3928100 : if ((fl == NULL) || ((b = fl->f) == NULL))
875 : {
876 3849538 : continue;
877 : }
878 903463 : for (b = b->next; NULL != b; b = b->next)
879 : {
880 824901 : int tag_found = 0;
881 824901 : if (0 == (b->url->flags & flag))
882 : {
883 824901 : continue;
884 : }
885 0 : for (tag = csp->tags->first; NULL != tag; tag = tag->next)
886 : {
887 0 : if (0 == regexec(b->url->pattern.tag_regex, tag->str, 0, NULL, 0))
888 : {
889 : /*
890 : * The pattern matches at least one tag, thus the action
891 : * section doesn't apply and we don't need to look at the
892 : * other tags.
893 : */
894 0 : tag_found = 1;
895 0 : break;
896 : }
897 : }
898 0 : if (!tag_found)
899 : {
900 : /*
901 : * The pattern doesn't match any tags,
902 : * thus the action section applies.
903 : */
904 0 : if (merge_current_action(csp->action, b->action))
905 : {
906 0 : log_error(LOG_LEVEL_ERROR,
907 : "Out of memory while changing action bits");
908 0 : return JB_ERR_MEMORY;
909 : }
910 0 : log_error(LOG_LEVEL_HEADER, "Updated action bits based on: %s",
911 : b->url->spec);
912 : }
913 : }
914 : }
915 :
916 39281 : return JB_ERR_OK;
917 : }
918 :
919 :
920 : /*********************************************************************
921 : *
922 : * Function : free_current_action
923 : *
924 : * Description : Free memory used by a current_action_spec.
925 : * Does not free the current_action_spec itself.
926 : *
927 : * Parameters :
928 : * 1 : src = Source to free.
929 : *
930 : * Returns : N/A
931 : *
932 : *********************************************************************/
933 35760 : void free_current_action(struct current_action_spec *src)
934 : {
935 : int i;
936 :
937 750960 : for (i = 0; i < ACTION_STRING_COUNT; i++)
938 : {
939 715200 : freez(src->string[i]);
940 : }
941 :
942 357600 : for (i = 0; i < ACTION_MULTI_COUNT; i++)
943 : {
944 321840 : destroy_list(src->multi[i]);
945 : }
946 :
947 35760 : memset(src, '\0', sizeof(*src));
948 35760 : }
949 :
950 :
951 : static struct file_list *current_actions_file[MAX_AF_FILES] = {
952 : NULL, NULL, NULL, NULL, NULL,
953 : NULL, NULL, NULL, NULL, NULL
954 : };
955 :
956 :
957 : #ifdef FEATURE_GRACEFUL_TERMINATION
958 : /*********************************************************************
959 : *
960 : * Function : unload_current_actions_file
961 : *
962 : * Description : Unloads current actions file - reset to state at
963 : * beginning of program.
964 : *
965 : * Parameters : None
966 : *
967 : * Returns : N/A
968 : *
969 : *********************************************************************/
970 : void unload_current_actions_file(void)
971 : {
972 : int i;
973 :
974 : for (i = 0; i < MAX_AF_FILES; i++)
975 : {
976 : if (current_actions_file[i])
977 : {
978 : current_actions_file[i]->unloader = unload_actions_file;
979 : current_actions_file[i] = NULL;
980 : }
981 : }
982 : }
983 : #endif /* FEATURE_GRACEFUL_TERMINATION */
984 :
985 :
986 : /*********************************************************************
987 : *
988 : * Function : unload_actions_file
989 : *
990 : * Description : Unloads an actions module.
991 : *
992 : * Parameters :
993 : * 1 : file_data = the data structure associated with the
994 : * actions file.
995 : *
996 : * Returns : N/A
997 : *
998 : *********************************************************************/
999 0 : void unload_actions_file(void *file_data)
1000 : {
1001 : struct url_actions * next;
1002 0 : struct url_actions * cur = (struct url_actions *)file_data;
1003 0 : while (cur != NULL)
1004 : {
1005 0 : next = cur->next;
1006 0 : free_pattern_spec(cur->url);
1007 0 : if ((next == NULL) || (next->action != cur->action))
1008 : {
1009 : /*
1010 : * As the action settings might be shared,
1011 : * we can only free them if the current
1012 : * url pattern is the last one, or if the
1013 : * next one is using different settings.
1014 : */
1015 0 : free_action_spec(cur->action);
1016 : }
1017 0 : freez(cur);
1018 0 : cur = next;
1019 : }
1020 0 : }
1021 :
1022 :
1023 : /*********************************************************************
1024 : *
1025 : * Function : free_alias_list
1026 : *
1027 : * Description : Free memory used by a list of aliases.
1028 : *
1029 : * Parameters :
1030 : * 1 : alias_list = Linked list to free.
1031 : *
1032 : * Returns : N/A
1033 : *
1034 : *********************************************************************/
1035 10082 : void free_alias_list(struct action_alias *alias_list)
1036 : {
1037 43794 : while (alias_list != NULL)
1038 : {
1039 33712 : struct action_alias * next = alias_list->next;
1040 33712 : alias_list->next = NULL;
1041 33712 : freez(alias_list->name);
1042 33712 : free_action(alias_list->action);
1043 33712 : free(alias_list);
1044 33712 : alias_list = next;
1045 : }
1046 10082 : }
1047 :
1048 :
1049 : /*********************************************************************
1050 : *
1051 : * Function : load_action_files
1052 : *
1053 : * Description : Read and parse all the action files and add to files
1054 : * list.
1055 : *
1056 : * Parameters :
1057 : * 1 : csp = Current client state (buffers, headers, etc...)
1058 : *
1059 : * Returns : 0 => Ok, everything else is an error.
1060 : *
1061 : *********************************************************************/
1062 6196 : int load_action_files(struct client_state *csp)
1063 : {
1064 : int i;
1065 : int result;
1066 :
1067 625796 : for (i = 0; i < MAX_AF_FILES; i++)
1068 : {
1069 619600 : if (csp->config->actions_file[i])
1070 : {
1071 12392 : result = load_one_actions_file(csp, i);
1072 12392 : if (result)
1073 : {
1074 0 : return result;
1075 : }
1076 : }
1077 607208 : else if (current_actions_file[i])
1078 : {
1079 0 : current_actions_file[i]->unloader = unload_actions_file;
1080 0 : current_actions_file[i] = NULL;
1081 : }
1082 : }
1083 :
1084 6196 : return 0;
1085 : }
1086 :
1087 :
1088 : /*********************************************************************
1089 : *
1090 : * Function : filter_type_to_string
1091 : *
1092 : * Description : Converts a filter type enum into a string.
1093 : *
1094 : * Parameters :
1095 : * 1 : filter_type = filter_type as enum
1096 : *
1097 : * Returns : Pointer to static string.
1098 : *
1099 : *********************************************************************/
1100 0 : static const char *filter_type_to_string(enum filter_type filter_type)
1101 : {
1102 0 : switch (filter_type)
1103 : {
1104 0 : case FT_CONTENT_FILTER:
1105 0 : return "content filter";
1106 0 : case FT_CLIENT_HEADER_FILTER:
1107 0 : return "client-header filter";
1108 0 : case FT_SERVER_HEADER_FILTER:
1109 0 : return "server-header filter";
1110 0 : case FT_CLIENT_HEADER_TAGGER:
1111 0 : return "client-header tagger";
1112 0 : case FT_SERVER_HEADER_TAGGER:
1113 0 : return "server-header tagger";
1114 : #ifdef FEATURE_EXTERNAL_FILTERS
1115 0 : case FT_EXTERNAL_CONTENT_FILTER:
1116 0 : return "external content filter";
1117 : #endif
1118 0 : case FT_SUPPRESS_TAG:
1119 0 : return "suppress tag filter";
1120 0 : case FT_CLIENT_BODY_FILTER:
1121 0 : return "client body filter";
1122 0 : case FT_INVALID_FILTER:
1123 0 : return "invalid filter type";
1124 : }
1125 :
1126 0 : return "unknown filter type";
1127 :
1128 : }
1129 :
1130 : /*********************************************************************
1131 : *
1132 : * Function : referenced_filters_are_missing
1133 : *
1134 : * Description : Checks if any filters of a certain type referenced
1135 : * in an action spec are missing.
1136 : *
1137 : * Parameters :
1138 : * 1 : csp = Current client state (buffers, headers, etc...)
1139 : * 2 : cur_action = The action spec to check.
1140 : * 3 : multi_index = The index where to look for the filter.
1141 : * 4 : filter_type = The filter type the caller is interested in.
1142 : *
1143 : * Returns : 0 => All referenced filters exist, everything else is an error.
1144 : *
1145 : *********************************************************************/
1146 371760 : static int referenced_filters_are_missing(const struct client_state *csp,
1147 : const struct action_spec *cur_action, int multi_index, enum filter_type filter_type)
1148 : {
1149 : struct list_entry *filtername;
1150 :
1151 402740 : for (filtername = cur_action->multi_add[multi_index]->first;
1152 30980 : filtername; filtername = filtername->next)
1153 : {
1154 30980 : if (NULL == get_filter(csp, filtername->str, filter_type))
1155 : {
1156 0 : log_error(LOG_LEVEL_ERROR, "Missing %s '%s'",
1157 : filter_type_to_string(filter_type), filtername->str);
1158 0 : return 1;
1159 : }
1160 : }
1161 :
1162 371760 : return 0;
1163 :
1164 : }
1165 :
1166 :
1167 : /*********************************************************************
1168 : *
1169 : * Function : action_spec_is_valid
1170 : *
1171 : * Description : Should eventually figure out if an action spec
1172 : * is valid, but currently only checks that the
1173 : * referenced filters are accounted for.
1174 : *
1175 : * Parameters :
1176 : * 1 : csp = Current client state (buffers, headers, etc...)
1177 : * 2 : cur_action = The action spec to check.
1178 : *
1179 : * Returns : 0 => No problems detected, everything else is an error.
1180 : *
1181 : *********************************************************************/
1182 61960 : static int action_spec_is_valid(struct client_state *csp, const struct action_spec *cur_action)
1183 : {
1184 : struct {
1185 : int multi_index;
1186 : enum filter_type filter_type;
1187 61960 : } filter_map[] = {
1188 : {ACTION_MULTI_FILTER, FT_CONTENT_FILTER},
1189 : {ACTION_MULTI_CLIENT_HEADER_FILTER, FT_CLIENT_HEADER_FILTER},
1190 : {ACTION_MULTI_SERVER_HEADER_FILTER, FT_SERVER_HEADER_FILTER},
1191 : {ACTION_MULTI_CLIENT_HEADER_TAGGER, FT_CLIENT_HEADER_TAGGER},
1192 : {ACTION_MULTI_SERVER_HEADER_TAGGER, FT_SERVER_HEADER_TAGGER},
1193 : {ACTION_MULTI_CLIENT_BODY_FILTER, FT_CLIENT_BODY_FILTER}
1194 : };
1195 61960 : int errors = 0;
1196 : int i;
1197 :
1198 433720 : for (i = 0; i < SZ(filter_map); i++)
1199 : {
1200 371760 : errors += referenced_filters_are_missing(csp, cur_action,
1201 : filter_map[i].multi_index, filter_map[i].filter_type);
1202 : }
1203 :
1204 61960 : return errors;
1205 :
1206 : }
1207 :
1208 :
1209 : /*********************************************************************
1210 : *
1211 : * Function : load_one_actions_file
1212 : *
1213 : * Description : Read and parse a action file and add to files
1214 : * list.
1215 : *
1216 : * Parameters :
1217 : * 1 : csp = Current client state (buffers, headers, etc...)
1218 : * 2 : fileid = File index to load.
1219 : *
1220 : * Returns : 0 => Ok, everything else is an error.
1221 : *
1222 : *********************************************************************/
1223 : #ifndef FUZZ
1224 : static
1225 : #endif
1226 12392 : int load_one_actions_file(struct client_state *csp, int fileid)
1227 : {
1228 :
1229 : /*
1230 : * Parser mode.
1231 : * Note: Keep these in the order they occur in the file, they are
1232 : * sometimes tested with <=
1233 : */
1234 : enum {
1235 : MODE_START_OF_FILE = 1,
1236 : MODE_SETTINGS = 2,
1237 : MODE_DESCRIPTION = 3,
1238 : MODE_ALIAS = 4,
1239 : MODE_ACTIONS = 5
1240 : } mode;
1241 :
1242 : FILE *fp;
1243 : struct url_actions *last_perm;
1244 : struct url_actions *perm;
1245 : char *buf;
1246 : struct file_list *fs;
1247 12392 : struct action_spec * cur_action = NULL;
1248 12392 : int cur_action_used = 0;
1249 12392 : struct action_alias * alias_list = NULL;
1250 12392 : unsigned long linenum = 0;
1251 12392 : mode = MODE_START_OF_FILE;
1252 :
1253 12392 : if (!check_file_changed(current_actions_file[fileid], csp->config->actions_file[fileid], &fs))
1254 : {
1255 : /* No need to load */
1256 6196 : csp->actions_list[fileid] = current_actions_file[fileid];
1257 6196 : return 0;
1258 : }
1259 6196 : if (!fs)
1260 : {
1261 0 : log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': %E. "
1262 : "Note that beginning with Privoxy 3.0.7, actions files have to be specified "
1263 0 : "with their complete file names.", csp->config->actions_file[fileid]);
1264 0 : return 1; /* never get here */
1265 : }
1266 :
1267 6196 : fs->f = last_perm = zalloc_or_die(sizeof(*last_perm));
1268 :
1269 6196 : if ((fp = fopen(csp->config->actions_file[fileid], "r")) == NULL)
1270 : {
1271 0 : log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
1272 0 : csp->config->actions_file[fileid]);
1273 0 : return 1; /* never get here */
1274 : }
1275 :
1276 6196 : log_error(LOG_LEVEL_INFO, "Loading actions file: %s", csp->config->actions_file[fileid]);
1277 :
1278 164194 : while (read_config_line(fp, &linenum, &buf) != NULL)
1279 : {
1280 157998 : if (*buf == '{')
1281 : {
1282 : /* It's a header block */
1283 68156 : if (buf[1] == '{')
1284 : {
1285 : /* It's {{settings}} or {{alias}} */
1286 6196 : size_t len = strlen(buf);
1287 6196 : char * start = buf + 2;
1288 6196 : char * end = buf + len - 1;
1289 6196 : if ((len < (size_t)5) || (*end-- != '}') || (*end-- != '}'))
1290 : {
1291 : /* too short */
1292 0 : fclose(fp);
1293 0 : log_error(LOG_LEVEL_FATAL,
1294 : "can't load actions file '%s': invalid line (%lu): %s",
1295 0 : csp->config->actions_file[fileid], linenum, buf);
1296 0 : return 1; /* never get here */
1297 : }
1298 :
1299 : /* Trim leading and trailing whitespace. */
1300 6196 : end[1] = '\0';
1301 6196 : chomp(start);
1302 :
1303 6196 : if (*start == '\0')
1304 : {
1305 : /* too short */
1306 0 : fclose(fp);
1307 0 : log_error(LOG_LEVEL_FATAL,
1308 : "can't load actions file '%s': invalid line (%lu): {{ }}",
1309 0 : csp->config->actions_file[fileid], linenum);
1310 0 : return 1; /* never get here */
1311 : }
1312 :
1313 : /*
1314 : * An actionsfile can optionally contain the following blocks.
1315 : * They *MUST* be in this order, to simplify processing:
1316 : *
1317 : * {{settings}}
1318 : * name=value...
1319 : *
1320 : * {{description}}
1321 : * ...free text, format TBD, but no line may start with a '{'...
1322 : *
1323 : * {{alias}}
1324 : * name=actions...
1325 : *
1326 : * The actual actions must be *after* these special blocks.
1327 : * None of these special blocks may be repeated.
1328 : *
1329 : */
1330 6196 : if (0 == strcmpic(start, "settings"))
1331 : {
1332 : /* it's a {{settings}} block */
1333 3098 : if (mode >= MODE_SETTINGS)
1334 : {
1335 : /* {{settings}} must be first thing in file and must only
1336 : * appear once.
1337 : */
1338 0 : fclose(fp);
1339 0 : log_error(LOG_LEVEL_FATAL,
1340 : "can't load actions file '%s': line %lu: {{settings}} must only appear once, and it must be before anything else.",
1341 0 : csp->config->actions_file[fileid], linenum);
1342 : }
1343 3098 : mode = MODE_SETTINGS;
1344 : }
1345 3098 : else if (0 == strcmpic(start, "description"))
1346 : {
1347 : /* it's a {{description}} block */
1348 0 : if (mode >= MODE_DESCRIPTION)
1349 : {
1350 : /* {{description}} is a singleton and only {{settings}} may proceed it
1351 : */
1352 0 : fclose(fp);
1353 0 : log_error(LOG_LEVEL_FATAL,
1354 : "can't load actions file '%s': line %lu: {{description}} must only appear once, and only a {{settings}} block may be above it.",
1355 0 : csp->config->actions_file[fileid], linenum);
1356 : }
1357 0 : mode = MODE_DESCRIPTION;
1358 : }
1359 3098 : else if (0 == strcmpic(start, "alias"))
1360 : {
1361 : /* it's an {{alias}} block */
1362 3098 : if (mode >= MODE_ALIAS)
1363 : {
1364 : /* {{alias}} must be first thing in file, possibly after
1365 : * {{settings}} and {{description}}
1366 : *
1367 : * {{alias}} must only appear once.
1368 : *
1369 : * Note that these are new restrictions introduced in
1370 : * v2.9.10 in order to make actionsfile editing simpler.
1371 : * (Otherwise, reordering actionsfile entries without
1372 : * completely rewriting the file becomes non-trivial)
1373 : */
1374 0 : fclose(fp);
1375 0 : log_error(LOG_LEVEL_FATAL,
1376 : "can't load actions file '%s': line %lu: {{alias}} must only appear once, and it must be before all actions.",
1377 0 : csp->config->actions_file[fileid], linenum);
1378 : }
1379 3098 : mode = MODE_ALIAS;
1380 : }
1381 : else
1382 : {
1383 : /* invalid {{something}} block */
1384 0 : fclose(fp);
1385 0 : log_error(LOG_LEVEL_FATAL,
1386 : "can't load actions file '%s': invalid line (%lu): {{%s}}",
1387 0 : csp->config->actions_file[fileid], linenum, start);
1388 0 : return 1; /* never get here */
1389 : }
1390 : }
1391 : else
1392 : {
1393 : /* It's an actions block */
1394 :
1395 : char *actions_buf;
1396 : char * end;
1397 :
1398 : /* set mode */
1399 61960 : mode = MODE_ACTIONS;
1400 :
1401 : /* free old action */
1402 61960 : if (cur_action)
1403 : {
1404 55764 : if (!cur_action_used)
1405 : {
1406 0 : free_action_spec(cur_action);
1407 : }
1408 55764 : cur_action = NULL;
1409 : }
1410 61960 : cur_action_used = 0;
1411 61960 : cur_action = zalloc_or_die(sizeof(*cur_action));
1412 61960 : init_action(cur_action);
1413 :
1414 : /*
1415 : * Copy the buffer before messing with it as we may need the
1416 : * unmodified version in for the fatal error messages. Given
1417 : * that this is not a common event, we could instead simply
1418 : * read the line again.
1419 : *
1420 : * buf + 1 to skip the leading '{'
1421 : */
1422 61960 : actions_buf = end = strdup_or_die(buf + 1);
1423 :
1424 : /* check we have a trailing } and then trim it */
1425 61960 : if (strlen(actions_buf))
1426 : {
1427 61960 : end += strlen(actions_buf) - 1;
1428 : }
1429 61960 : if (*end != '}')
1430 : {
1431 : /* No closing } */
1432 0 : fclose(fp);
1433 0 : freez(actions_buf);
1434 0 : log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': "
1435 : "Missing trailing '}' in action section starting at line (%lu): %s",
1436 0 : csp->config->actions_file[fileid], linenum, buf);
1437 0 : return 1; /* never get here */
1438 : }
1439 61960 : *end = '\0';
1440 :
1441 : /* trim any whitespace immediately inside {} */
1442 61960 : chomp(actions_buf);
1443 :
1444 61960 : if (get_actions(actions_buf, alias_list, cur_action))
1445 : {
1446 : /* error */
1447 0 : fclose(fp);
1448 0 : freez(actions_buf);
1449 0 : log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': "
1450 : "can't completely parse the action section starting at line (%lu): %s",
1451 0 : csp->config->actions_file[fileid], linenum, buf);
1452 0 : return 1; /* never get here */
1453 : }
1454 :
1455 61960 : if (action_spec_is_valid(csp, cur_action))
1456 : {
1457 0 : log_error(LOG_LEVEL_ERROR, "Invalid action section in file '%s', "
1458 : "starting at line %lu: %s",
1459 0 : csp->config->actions_file[fileid], linenum, buf);
1460 : }
1461 :
1462 61960 : freez(actions_buf);
1463 : }
1464 : }
1465 89842 : else if (mode == MODE_SETTINGS)
1466 : {
1467 : /*
1468 : * Part of the {{settings}} block.
1469 : * For now only serves to check if the file's minimum Privoxy
1470 : * version requirement is met, but we may want to read & check
1471 : * permissions when we go multi-user.
1472 : */
1473 3098 : if (!strncmp(buf, "for-privoxy-version=", 20))
1474 : {
1475 : char *version_string, *fields[3];
1476 : int num_fields;
1477 :
1478 3098 : version_string = strdup_or_die(buf + 20);
1479 :
1480 3098 : num_fields = ssplit(version_string, ".", fields, SZ(fields));
1481 :
1482 3098 : if (num_fields < 1 || atoi(fields[0]) == 0)
1483 : {
1484 0 : log_error(LOG_LEVEL_ERROR,
1485 : "While loading actions file '%s': invalid line (%lu): %s",
1486 0 : csp->config->actions_file[fileid], linenum, buf);
1487 : }
1488 3098 : else if ( (atoi(fields[0]) > VERSION_MAJOR)
1489 3098 : || ((num_fields > 1) && (atoi(fields[1]) > VERSION_MINOR))
1490 3098 : || ((num_fields > 2) && (atoi(fields[2]) > VERSION_POINT)))
1491 : {
1492 0 : fclose(fp);
1493 0 : log_error(LOG_LEVEL_FATAL,
1494 : "Actions file '%s', line %lu requires newer Privoxy version: %s",
1495 0 : csp->config->actions_file[fileid], linenum, buf);
1496 0 : return 1; /* never get here */
1497 : }
1498 3098 : free(version_string);
1499 : }
1500 : }
1501 86744 : else if (mode == MODE_DESCRIPTION)
1502 : {
1503 : /*
1504 : * Part of the {{description}} block.
1505 : * Ignore for now.
1506 : */
1507 : }
1508 86744 : else if (mode == MODE_ALIAS)
1509 : {
1510 : /*
1511 : * define an alias
1512 : */
1513 : char actions_buf[BUFFER_SIZE];
1514 : struct action_alias * new_alias;
1515 :
1516 21686 : char * start = strchr(buf, '=');
1517 21686 : char * end = start;
1518 :
1519 21686 : if ((start == NULL) || (start == buf))
1520 : {
1521 0 : log_error(LOG_LEVEL_FATAL,
1522 : "can't load actions file '%s': invalid alias line (%lu): %s",
1523 0 : csp->config->actions_file[fileid], linenum, buf);
1524 0 : return 1; /* never get here */
1525 : }
1526 :
1527 21686 : new_alias = zalloc_or_die(sizeof(*new_alias));
1528 :
1529 : /* Eat any the whitespace before the '=' */
1530 21686 : end--;
1531 96038 : while ((*end == ' ') || (*end == '\t'))
1532 : {
1533 : /*
1534 : * we already know we must have at least 1 non-ws char
1535 : * at start of buf - no need to check
1536 : */
1537 74352 : end--;
1538 : }
1539 21686 : end[1] = '\0';
1540 :
1541 : /* Eat any the whitespace after the '=' */
1542 21686 : start++;
1543 43372 : while ((*start == ' ') || (*start == '\t'))
1544 : {
1545 21686 : start++;
1546 : }
1547 21686 : if (*start == '\0')
1548 : {
1549 0 : log_error(LOG_LEVEL_FATAL,
1550 : "can't load actions file '%s': invalid alias line (%lu): %s",
1551 0 : csp->config->actions_file[fileid], linenum, buf);
1552 0 : return 1; /* never get here */
1553 : }
1554 :
1555 21686 : new_alias->name = strdup_or_die(buf);
1556 :
1557 21686 : strlcpy(actions_buf, start, sizeof(actions_buf));
1558 :
1559 21686 : if (get_actions(actions_buf, alias_list, new_alias->action))
1560 : {
1561 : /* error */
1562 0 : fclose(fp);
1563 0 : log_error(LOG_LEVEL_FATAL,
1564 : "can't load actions file '%s': invalid alias line (%lu): %s = %s",
1565 0 : csp->config->actions_file[fileid], linenum, buf, start);
1566 0 : return 1; /* never get here */
1567 : }
1568 :
1569 : /* add to list */
1570 21686 : new_alias->next = alias_list;
1571 21686 : alias_list = new_alias;
1572 : }
1573 65058 : else if (mode == MODE_ACTIONS)
1574 : {
1575 : /* it's an URL pattern */
1576 :
1577 : /* allocate a new node */
1578 65058 : perm = zalloc_or_die(sizeof(*perm));
1579 :
1580 65058 : perm->action = cur_action;
1581 65058 : cur_action_used = 1;
1582 :
1583 : /* Save the URL pattern */
1584 65058 : if (create_pattern_spec(perm->url, buf))
1585 : {
1586 0 : fclose(fp);
1587 0 : log_error(LOG_LEVEL_FATAL,
1588 : "can't load actions file '%s': line %lu: cannot create URL or TAG pattern from: %s",
1589 0 : csp->config->actions_file[fileid], linenum, buf);
1590 0 : return 1; /* never get here */
1591 : }
1592 :
1593 : /* add it to the list */
1594 65058 : last_perm->next = perm;
1595 65058 : last_perm = perm;
1596 : }
1597 0 : else if (mode == MODE_START_OF_FILE)
1598 : {
1599 : /* oops - please have a {} line as 1st line in file. */
1600 0 : fclose(fp);
1601 0 : log_error(LOG_LEVEL_FATAL,
1602 : "can't load actions file '%s': line %lu should begin with a '{': %s",
1603 0 : csp->config->actions_file[fileid], linenum, buf);
1604 0 : return 1; /* never get here */
1605 : }
1606 : else
1607 : {
1608 : /* How did we get here? This is impossible! */
1609 0 : fclose(fp);
1610 0 : log_error(LOG_LEVEL_FATAL,
1611 : "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1612 0 : csp->config->actions_file[fileid], mode);
1613 0 : return 1; /* never get here */
1614 : }
1615 157998 : freez(buf);
1616 : }
1617 :
1618 6196 : fclose(fp);
1619 :
1620 6196 : if (!cur_action_used)
1621 : {
1622 0 : free_action_spec(cur_action);
1623 : }
1624 6196 : free_alias_list(alias_list);
1625 :
1626 : /* the old one is now obsolete */
1627 6196 : if (current_actions_file[fileid])
1628 : {
1629 0 : current_actions_file[fileid]->unloader = unload_actions_file;
1630 : }
1631 :
1632 6196 : fs->next = files->next;
1633 6196 : files->next = fs;
1634 6196 : current_actions_file[fileid] = fs;
1635 :
1636 6196 : csp->actions_list[fileid] = fs;
1637 :
1638 6196 : return(0);
1639 :
1640 : }
1641 :
1642 :
1643 : /*********************************************************************
1644 : *
1645 : * Function : actions_to_text
1646 : *
1647 : * Description : Converts a actionsfile entry from the internal
1648 : * structure into a text line. The output is split
1649 : * into one line for each action with line continuation.
1650 : *
1651 : * Parameters :
1652 : * 1 : action = The action to format.
1653 : *
1654 : * Returns : A string. Caller must free it.
1655 : * NULL on out-of-memory error.
1656 : *
1657 : *********************************************************************/
1658 542 : char * actions_to_text(const struct action_spec *action)
1659 : {
1660 542 : unsigned long mask = action->mask;
1661 542 : unsigned long add = action->add;
1662 542 : char *result = strdup_or_die("");
1663 : struct list_entry * lst;
1664 :
1665 : /* sanity - prevents "-feature +feature" */
1666 542 : mask |= add;
1667 :
1668 :
1669 : #define DEFINE_ACTION_BOOL(__name, __bit) \
1670 : if (!(mask & __bit)) \
1671 : { \
1672 : string_append(&result, " -" __name " \\\n"); \
1673 : } \
1674 : else if (add & __bit) \
1675 : { \
1676 : string_append(&result, " +" __name " \\\n"); \
1677 : }
1678 :
1679 : #define DEFINE_ACTION_STRING(__name, __bit, __index) \
1680 : if (!(mask & __bit)) \
1681 : { \
1682 : string_append(&result, " -" __name " \\\n"); \
1683 : } \
1684 : else if (add & __bit) \
1685 : { \
1686 : string_append(&result, " +" __name "{"); \
1687 : string_append(&result, action->string[__index]); \
1688 : string_append(&result, "} \\\n"); \
1689 : }
1690 :
1691 : #define DEFINE_ACTION_MULTI(__name, __index) \
1692 : if (action->multi_remove_all[__index]) \
1693 : { \
1694 : string_append(&result, " -" __name " \\\n"); \
1695 : } \
1696 : else \
1697 : { \
1698 : lst = action->multi_remove[__index]->first; \
1699 : while (lst) \
1700 : { \
1701 : string_append(&result, " -" __name "{"); \
1702 : string_append(&result, lst->str); \
1703 : string_append(&result, "} \\\n"); \
1704 : lst = lst->next; \
1705 : } \
1706 : } \
1707 : lst = action->multi_add[__index]->first; \
1708 : while (lst) \
1709 : { \
1710 : string_append(&result, " +" __name "{"); \
1711 : string_append(&result, lst->str); \
1712 : string_append(&result, "} \\\n"); \
1713 : lst = lst->next; \
1714 : }
1715 :
1716 : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1717 :
1718 : #include "actionlist.h"
1719 :
1720 : #undef DEFINE_ACTION_MULTI
1721 : #undef DEFINE_ACTION_STRING
1722 : #undef DEFINE_ACTION_BOOL
1723 : #undef DEFINE_ACTION_ALIAS
1724 :
1725 542 : return result;
1726 : }
1727 :
1728 :
1729 : /*********************************************************************
1730 : *
1731 : * Function : actions_to_html
1732 : *
1733 : * Description : Converts a actionsfile entry from numeric form
1734 : * ("mask" and "add") to a <br>-separated HTML string
1735 : * in which each action is linked to its chapter in
1736 : * the user manual.
1737 : *
1738 : * Parameters :
1739 : * 1 : csp = Client state (for config)
1740 : * 2 : action = Action spec to be converted
1741 : *
1742 : * Returns : A string. Caller must free it.
1743 : * NULL on out-of-memory error.
1744 : *
1745 : *********************************************************************/
1746 32964 : char * actions_to_html(const struct client_state *csp,
1747 : const struct action_spec *action)
1748 : {
1749 32964 : unsigned long mask = action->mask;
1750 32964 : unsigned long add = action->add;
1751 32964 : char *result = strdup_or_die("");
1752 : struct list_entry * lst;
1753 :
1754 : /* sanity - prevents "-feature +feature" */
1755 32964 : mask |= add;
1756 :
1757 :
1758 : #define DEFINE_ACTION_BOOL(__name, __bit) \
1759 : if (!(mask & __bit)) \
1760 : { \
1761 : string_append(&result, "\n<br>-"); \
1762 : string_join(&result, add_help_link(__name, csp->config)); \
1763 : } \
1764 : else if (add & __bit) \
1765 : { \
1766 : string_append(&result, "\n<br>+"); \
1767 : string_join(&result, add_help_link(__name, csp->config)); \
1768 : }
1769 :
1770 : #define DEFINE_ACTION_STRING(__name, __bit, __index) \
1771 : if (!(mask & __bit)) \
1772 : { \
1773 : string_append(&result, "\n<br>-"); \
1774 : string_join(&result, add_help_link(__name, csp->config)); \
1775 : } \
1776 : else if (add & __bit) \
1777 : { \
1778 : string_append(&result, "\n<br>+"); \
1779 : string_join(&result, add_help_link(__name, csp->config)); \
1780 : string_append(&result, "{"); \
1781 : string_join(&result, html_encode(action->string[__index])); \
1782 : string_append(&result, "}"); \
1783 : }
1784 :
1785 : #define DEFINE_ACTION_MULTI(__name, __index) \
1786 : if (action->multi_remove_all[__index]) \
1787 : { \
1788 : string_append(&result, "\n<br>-"); \
1789 : string_join(&result, add_help_link(__name, csp->config)); \
1790 : } \
1791 : else \
1792 : { \
1793 : lst = action->multi_remove[__index]->first; \
1794 : while (lst) \
1795 : { \
1796 : string_append(&result, "\n<br>-"); \
1797 : string_join(&result, add_help_link(__name, csp->config)); \
1798 : string_append(&result, "{"); \
1799 : string_join(&result, html_encode(lst->str)); \
1800 : string_append(&result, "}"); \
1801 : lst = lst->next; \
1802 : } \
1803 : } \
1804 : lst = action->multi_add[__index]->first; \
1805 : while (lst) \
1806 : { \
1807 : string_append(&result, "\n<br>+"); \
1808 : string_join(&result, add_help_link(__name, csp->config)); \
1809 : string_append(&result, "{"); \
1810 : string_join(&result, html_encode(lst->str)); \
1811 : string_append(&result, "}"); \
1812 : lst = lst->next; \
1813 : }
1814 :
1815 : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1816 :
1817 : #include "actionlist.h"
1818 :
1819 : #undef DEFINE_ACTION_MULTI
1820 : #undef DEFINE_ACTION_STRING
1821 : #undef DEFINE_ACTION_BOOL
1822 : #undef DEFINE_ACTION_ALIAS
1823 :
1824 : /* trim leading <br> */
1825 32964 : if (result && *result)
1826 : {
1827 32964 : char * s = result;
1828 32964 : result = strdup(result + 5);
1829 32964 : free(s);
1830 : }
1831 :
1832 32964 : return result;
1833 : }
1834 :
1835 :
1836 : /*********************************************************************
1837 : *
1838 : * Function : current_actions_to_html
1839 : *
1840 : * Description : Converts a current action spec to a <br> separated HTML
1841 : * text in which each action is linked to its chapter in
1842 : * the user manual.
1843 : *
1844 : * Parameters :
1845 : * 1 : csp = Client state (for config)
1846 : * 2 : action = Current action spec to be converted
1847 : *
1848 : * Returns : A string. Caller must free it.
1849 : * NULL on out-of-memory error.
1850 : *
1851 : *********************************************************************/
1852 2678 : char *current_action_to_html(const struct client_state *csp,
1853 : const struct current_action_spec *action)
1854 : {
1855 2678 : unsigned long flags = action->flags;
1856 : struct list_entry * lst;
1857 2678 : char *result = strdup_or_die("");
1858 2678 : char *active = strdup_or_die("");
1859 2678 : char *inactive = strdup_or_die("");
1860 :
1861 : #define DEFINE_ACTION_BOOL(__name, __bit) \
1862 : if (flags & __bit) \
1863 : { \
1864 : string_append(&active, "\n<br>+"); \
1865 : string_join(&active, add_help_link(__name, csp->config)); \
1866 : } \
1867 : else \
1868 : { \
1869 : string_append(&inactive, "\n<br>-"); \
1870 : string_join(&inactive, add_help_link(__name, csp->config)); \
1871 : }
1872 :
1873 : #define DEFINE_ACTION_STRING(__name, __bit, __index) \
1874 : if (flags & __bit) \
1875 : { \
1876 : string_append(&active, "\n<br>+"); \
1877 : string_join(&active, add_help_link(__name, csp->config)); \
1878 : string_append(&active, "{"); \
1879 : string_join(&active, html_encode(action->string[__index])); \
1880 : string_append(&active, "}"); \
1881 : } \
1882 : else \
1883 : { \
1884 : string_append(&inactive, "\n<br>-"); \
1885 : string_join(&inactive, add_help_link(__name, csp->config)); \
1886 : }
1887 :
1888 : #define DEFINE_ACTION_MULTI(__name, __index) \
1889 : lst = action->multi[__index]->first; \
1890 : if (lst == NULL) \
1891 : { \
1892 : string_append(&inactive, "\n<br>-"); \
1893 : string_join(&inactive, add_help_link(__name, csp->config)); \
1894 : } \
1895 : else \
1896 : { \
1897 : while (lst) \
1898 : { \
1899 : string_append(&active, "\n<br>+"); \
1900 : string_join(&active, add_help_link(__name, csp->config)); \
1901 : string_append(&active, "{"); \
1902 : string_join(&active, html_encode(lst->str)); \
1903 : string_append(&active, "}"); \
1904 : lst = lst->next; \
1905 : } \
1906 : }
1907 :
1908 : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1909 :
1910 : #include "actionlist.h"
1911 :
1912 : #undef DEFINE_ACTION_MULTI
1913 : #undef DEFINE_ACTION_STRING
1914 : #undef DEFINE_ACTION_BOOL
1915 : #undef DEFINE_ACTION_ALIAS
1916 :
1917 2678 : if (active != NULL)
1918 : {
1919 2678 : string_append(&result, active);
1920 2678 : freez(active);
1921 : }
1922 2678 : string_append(&result, "\n<br>");
1923 2678 : if (inactive != NULL)
1924 : {
1925 2678 : string_append(&result, inactive);
1926 2678 : freez(inactive);
1927 : }
1928 2678 : return result;
1929 : }
1930 :
1931 :
1932 : /*********************************************************************
1933 : *
1934 : * Function : action_to_line_of_text
1935 : *
1936 : * Description : Converts a action spec to a single text line
1937 : * listing the enabled actions.
1938 : *
1939 : * Parameters :
1940 : * 1 : action = Current action spec to be converted
1941 : *
1942 : * Returns : A string. Caller must free it.
1943 : * Out-of-memory errors are fatal.
1944 : *
1945 : *********************************************************************/
1946 35226 : char *actions_to_line_of_text(const struct current_action_spec *action)
1947 : {
1948 : char buffer[200];
1949 : struct list_entry *lst;
1950 : char *active;
1951 35226 : const unsigned long flags = action->flags;
1952 :
1953 35226 : active = strdup_or_die("");
1954 :
1955 : #define DEFINE_ACTION_BOOL(__name, __bit) \
1956 : if (flags & __bit) \
1957 : { \
1958 : snprintf(buffer, sizeof(buffer), "+%s ", __name); \
1959 : string_append(&active, buffer); \
1960 : } \
1961 :
1962 : #define DEFINE_ACTION_STRING(__name, __bit, __index) \
1963 : if (flags & __bit) \
1964 : { \
1965 : snprintf(buffer, sizeof(buffer), "+%s{%s} ", \
1966 : __name, action->string[__index]); \
1967 : string_append(&active, buffer); \
1968 : } \
1969 :
1970 : #define DEFINE_ACTION_MULTI(__name, __index) \
1971 : lst = action->multi[__index]->first; \
1972 : while (lst != NULL) \
1973 : { \
1974 : snprintf(buffer, sizeof(buffer), "+%s{%s} ", \
1975 : __name, lst->str); \
1976 : string_append(&active, buffer); \
1977 : lst = lst->next; \
1978 : } \
1979 :
1980 : #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1981 :
1982 : #include "actionlist.h"
1983 :
1984 : #undef DEFINE_ACTION_MULTI
1985 : #undef DEFINE_ACTION_STRING
1986 : #undef DEFINE_ACTION_BOOL
1987 : #undef DEFINE_ACTION_ALIAS
1988 :
1989 35226 : if (active == NULL)
1990 : {
1991 0 : log_error(LOG_LEVEL_FATAL, "Out of memory in action_to_line_of_text()");
1992 : }
1993 :
1994 35226 : return active;
1995 : }
|