Enable 'no-brotli-accepted' client-header filter in all templates
[privoxy.git] / client-tags.c
1 /*********************************************************************
2  *
3  * File        :  $Source: /cvsroot/ijbswa/current/client-tags.c,v $
4  *
5  * Purpose     :  Functions related to client-specific tags.
6  *
7  * Copyright   :  Copyright (C) 2016-2017 Fabian Keil <fk@fabiankeil.de>
8  *
9  *                This program is free software; you can redistribute it
10  *                and/or modify it under the terms of the GNU General
11  *                Public License as published by the Free Software
12  *                Foundation; either version 2 of the License, or (at
13  *                your option) any later version.
14  *
15  *                This program is distributed in the hope that it will
16  *                be useful, but WITHOUT ANY WARRANTY; without even the
17  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
18  *                PARTICULAR PURPOSE.  See the GNU General Public
19  *                License for more details.
20  *
21  *                The GNU General Public License should be included with
22  *                this file.  If not, you can view it at
23  *                http://www.gnu.org/copyleft/gpl.html
24  *                or write to the Free Software Foundation, Inc., 59
25  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  **********************************************************************/
28
29 #include "config.h"
30
31 #ifdef FEATURE_CLIENT_TAGS
32
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <assert.h>
39
40 #include "project.h"
41 #include "list.h"
42 #include "jcc.h"
43 #include "miscutil.h"
44 #include "errlog.h"
45 #include "parsers.h"
46
47 struct client_specific_tag
48 {
49    char *name;
50
51    time_t end_of_life;
52
53    struct client_specific_tag *next;
54    struct client_specific_tag *prev;
55 };
56
57 /**
58  * This struct represents tags that have been requested by clients
59  */
60 struct requested_tags
61 {
62    char *client; /**< The IP address of the client that requested the tag */
63
64    /**< List of tags the client requested .... */
65    struct client_specific_tag *tags;
66
67    struct requested_tags *next;
68    struct requested_tags *prev;
69 };
70
71 struct requested_tags *requested_tags;
72 static void remove_tag_for_client(const char *client_address, const char *tag);
73
74 /*********************************************************************
75  *
76  * Function    :  validate_tag_list
77  *
78  * Description :  Validates the given tag list
79  *
80  * Parameters  :
81  *          1  :  enabled_tags = The tags to validate
82  *
83  * Returns     :  void
84  *
85  *********************************************************************/
86 static void validate_tag_list(struct client_specific_tag *enabled_tags)
87 {
88    while (enabled_tags != NULL)
89    {
90       if (enabled_tags->name == NULL)
91       {
92          assert(enabled_tags->name != NULL);
93          log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
94       }
95       if (enabled_tags->next != NULL)
96       {
97          if (enabled_tags->next->prev != enabled_tags)
98          {
99             assert(enabled_tags->next->prev == enabled_tags);
100             log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
101          }
102       }
103       enabled_tags = enabled_tags->next;
104    }
105 }
106
107 /*********************************************************************
108  *
109  * Function    :  validate_requested_tags
110  *
111  * Description :  Validates the requested_tags list
112  *
113  * Parameters  : N/A
114  *
115  * Returns     :  void
116  *
117  *********************************************************************/
118 static jb_err validate_requested_tags()
119 {
120    struct requested_tags *requested_tag;
121
122    for (requested_tag = requested_tags; requested_tag != NULL;
123         requested_tag = requested_tag->next)
124    {
125       if (requested_tag->client == NULL)
126       {
127          assert(requested_tag->client != NULL);
128          log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
129       }
130       validate_tag_list(requested_tag->tags);
131       if (requested_tag->next != NULL)
132       {
133          if (requested_tag->next->prev != requested_tag)
134          {
135             assert(requested_tag->next->prev == requested_tag);
136             log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
137          }
138       }
139    }
140
141    return TRUE;
142 }
143
144
145 /*********************************************************************
146  *
147  * Function    :  get_client_specific_tag
148  *
149  * Description :  Returns the data for a client-specific-tag specified
150  *                by name.
151  *
152  * Parameters  :
153  *          1  :  tag_list = The tag list to check
154  *          2  :  name =     The tag name to look up
155  *
156  * Returns     :  Pointer to tag structure or NULL on error.
157  *
158  *********************************************************************/
159 static struct client_tag_spec *get_client_specific_tag(
160    struct client_tag_spec *tag_list, const char *name)
161 {
162    struct client_tag_spec *tag;
163
164    for (tag = tag_list; tag != NULL; tag = tag->next)
165    {
166       if (tag->name != NULL && !strcmp(tag->name, name))
167       {
168          return tag;
169       }
170    }
171
172    log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
173
174    return NULL;
175
176 }
177
178
179 /*********************************************************************
180  *
181  * Function    :  get_tags_for_client
182  *
183  * Description :  Returns the list of tags the client opted-in.
184  *
185  * Parameters  :
186  *          1  :  client_address = Address of the client
187  *
188  * Returns     :  Pointer to tag structure or NULL on error.
189  *
190  *********************************************************************/
191 static struct client_specific_tag *get_tags_for_client(const char *client_address)
192 {
193    struct requested_tags *requested_tag;
194
195    for (requested_tag = requested_tags; requested_tag != NULL;
196         requested_tag = requested_tag->next)
197    {
198       if (!strcmp(requested_tag->client, client_address))
199       {
200          return requested_tag->tags;
201       }
202    }
203
204    return NULL;
205 }
206
207
208 /*********************************************************************
209  *
210  * Function    :  get_tag_list_for_client
211  *
212  * Description :  Provides a list of tag names the client opted-in.
213  *                Other tag attributes are not part of the list.
214  *
215  * Parameters  :
216  *          1  :  tag_list = The list to fill in.
217  *          2  :  client_address = Address of the client
218  *
219  * Returns     :  Pointer to tag list.
220  *
221  *********************************************************************/
222 void get_tag_list_for_client(struct list *tag_list,
223                              const char *client_address)
224 {
225    struct client_specific_tag *enabled_tags;
226    const time_t now = time(NULL);
227
228    privoxy_mutex_lock(&client_tags_mutex);
229
230    enabled_tags = get_tags_for_client(client_address);
231    while (enabled_tags != NULL)
232    {
233       if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
234       {
235          struct client_specific_tag *next_tag = enabled_tags->next;
236          log_error(LOG_LEVEL_INFO,
237             "Tag '%s' for client %s expired %u seconds ago. Deleting it.",
238             enabled_tags->name, client_address,
239             (now - enabled_tags->end_of_life));
240          remove_tag_for_client(client_address, enabled_tags->name);
241          enabled_tags = next_tag;
242          continue;
243       }
244       else
245       {
246          enlist(tag_list, enabled_tags->name);
247       }
248       enabled_tags = enabled_tags->next;
249    }
250
251    privoxy_mutex_unlock(&client_tags_mutex);
252 }
253
254
255 /*********************************************************************
256  *
257  * Function    :  get_next_tag_timeout_for_client
258  *
259  * Description :  Figures out when the next temporarily enabled tag
260  *                for the client will have timed out.
261  *
262  * Parameters  :
263  *          1  :  client_address = Address of the client
264  *
265  * Returns     :  Lowest timeout in seconds
266  *
267  *********************************************************************/
268 time_t get_next_tag_timeout_for_client(const char *client_address)
269 {
270    struct client_specific_tag *enabled_tags;
271    time_t next_timeout = 0;
272    const time_t now = time(NULL);
273
274    privoxy_mutex_lock(&client_tags_mutex);
275
276    enabled_tags = get_tags_for_client(client_address);
277    while (enabled_tags != NULL)
278    {
279       log_error(LOG_LEVEL_CGI, "Evaluating tag '%s' for client %s. End of life %d",
280          enabled_tags->name, client_address, enabled_tags->end_of_life);
281       if (enabled_tags->end_of_life)
282       {
283           time_t time_left = enabled_tags->end_of_life - now;
284           /* Add a second to make sure the tag will have expired */
285           time_left++;
286           log_error(LOG_LEVEL_CGI, "%d > %d?", next_timeout, time_left);
287           if (next_timeout == 0 || next_timeout > time_left)
288           {
289              next_timeout = time_left;
290           }
291        }
292        enabled_tags = enabled_tags->next;
293    }
294
295    privoxy_mutex_unlock(&client_tags_mutex);
296
297    log_error(LOG_LEVEL_CGI, "Next timeout in %d seconds", next_timeout);
298
299    return next_timeout;
300
301 }
302
303
304 /*********************************************************************
305  *
306  * Function    :  create_client_specific_tag
307  *
308  * Description :  Allocates memory for a client specific tag
309  *                and populates it.
310  *
311  * Parameters  :
312  *          1  :  name = The name of the tag to create.
313  *          2  :  time_to_live = 0, or the number of seconds
314  *                               the tag remains activated.
315  *
316  * Returns     :  Pointer to populated tag
317  *
318  *********************************************************************/
319 static struct client_specific_tag *create_client_specific_tag(const char *name,
320    const time_t time_to_live)
321 {
322    struct client_specific_tag *tag;
323
324    tag = zalloc_or_die(sizeof(struct client_specific_tag));
325    tag->name = strdup_or_die(name);
326    tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
327
328    return tag;
329
330 }
331
332 /*********************************************************************
333  *
334  * Function    :  add_tag_for_client
335  *
336  * Description :  Adds the tag for the client.
337  *
338  * Parameters  :
339  *          1  :  client_address = Address of the client
340  *          2  :  tag = The tag to add.
341  *          3  :  time_to_live = 0, or the number of seconds
342  *                               the tag remains activated.
343  *
344  * Returns     :  void
345  *
346  *********************************************************************/
347 static void add_tag_for_client(const char *client_address,
348    const char *tag, const time_t time_to_live)
349 {
350    struct requested_tags *clients_with_tags;
351    struct client_specific_tag *enabled_tags;
352
353    validate_requested_tags();
354
355    if (requested_tags == NULL)
356    {
357       /* XXX: Code duplication. */
358       requested_tags = zalloc_or_die(sizeof(struct requested_tags));
359       requested_tags->client = strdup_or_die(client_address);
360       requested_tags->tags = create_client_specific_tag(tag, time_to_live);
361
362       validate_requested_tags();
363       return;
364    }
365    else
366    {
367       clients_with_tags = requested_tags;
368       while (clients_with_tags->next != NULL)
369       {
370          if (!strcmp(clients_with_tags->client, client_address))
371          {
372             break;
373          }
374          clients_with_tags = clients_with_tags->next;
375       }
376       if (strcmp(clients_with_tags->client, client_address))
377       {
378          /* Client does not have tags yet, add new structure */
379          clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
380          clients_with_tags->next->prev = clients_with_tags;
381          clients_with_tags = clients_with_tags->next;
382          clients_with_tags->client = strdup_or_die(client_address);
383          clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
384
385          validate_requested_tags();
386
387          return;
388       }
389    }
390
391    enabled_tags = clients_with_tags->tags;
392    while (enabled_tags != NULL)
393    {
394       if (enabled_tags->next == NULL)
395       {
396          enabled_tags->next = create_client_specific_tag(tag, time_to_live);
397          enabled_tags->next->prev = enabled_tags;
398          break;
399       }
400       enabled_tags = enabled_tags->next;
401    }
402
403    validate_requested_tags();
404 }
405
406
407 /*********************************************************************
408  *
409  * Function    :  remove_tag_for_client
410  *
411  * Description :  Removes the tag for the client.
412  *
413  * Parameters  :
414  *          1  :  client_address = Address of the client
415  *          2  :  tag = The tag to remove.
416  *
417  * Returns     :  void
418  *
419  *********************************************************************/
420 static void remove_tag_for_client(const char *client_address, const char *tag)
421 {
422    struct requested_tags *clients_with_tags;
423    struct client_specific_tag *enabled_tags;
424
425    validate_requested_tags();
426
427    clients_with_tags = requested_tags;
428    while (clients_with_tags != NULL && clients_with_tags->client != NULL)
429    {
430       if (!strcmp(clients_with_tags->client, client_address))
431       {
432          break;
433       }
434       clients_with_tags = clients_with_tags->next;
435    }
436
437    assert(clients_with_tags != NULL);
438    if (clients_with_tags == NULL)
439    {
440       log_error(LOG_LEVEL_ERROR,
441          "Tried to remove tag %s for tag-less client %s",
442          tag, client_address);
443    }
444    enabled_tags = clients_with_tags->tags;
445    while (enabled_tags != NULL)
446    {
447       if (!strcmp(enabled_tags->name, tag))
448       {
449          if (enabled_tags->next != NULL)
450          {
451             enabled_tags->next->prev = enabled_tags->prev;
452             if (enabled_tags == clients_with_tags->tags)
453             {
454                /* Tag is first in line */
455                clients_with_tags->tags = enabled_tags->next;
456             }
457          }
458          if (enabled_tags->prev != NULL)
459          {
460             /* Tag has preceding tag */
461             enabled_tags->prev->next = enabled_tags->next;
462          }
463          if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
464          {
465             /* Tag is the only one */
466             if (clients_with_tags->next != NULL)
467             {
468                /* Client has following client */
469                clients_with_tags->next->prev = clients_with_tags->prev;
470             }
471             if (clients_with_tags->prev != NULL)
472             {
473                /* Client has preceding client */
474                clients_with_tags->prev->next = clients_with_tags->next;
475             }
476             if (clients_with_tags == requested_tags)
477             {
478                /*
479                 * We're in the process of removing the last tag,
480                 * mark the global list as empty.
481                 */
482                requested_tags = NULL;
483             }
484             freez(clients_with_tags->client);
485             freez(clients_with_tags);
486          }
487          freez(enabled_tags->name);
488          freez(enabled_tags);
489          break;
490       }
491
492       enabled_tags = enabled_tags->next;
493    }
494
495    validate_requested_tags();
496
497 }
498
499
500 /*********************************************************************
501  *
502  * Function    :  client_has_requested_tag
503  *
504  * Description :  Checks whether or not the given client requested
505  *                the tag.
506  *
507  * Parameters  :
508  *          1  :  client_address = Address of the client
509  *          2  :  tag = Tag to check.
510  *
511  * Returns     :  TRUE or FALSE.
512  *
513  *********************************************************************/
514 int client_has_requested_tag(const char *client_address, const char *tag)
515 {
516    struct client_specific_tag *enabled_tags;
517
518    enabled_tags = get_tags_for_client(client_address);
519
520    while (enabled_tags != NULL)
521    {
522       if (!strcmp(enabled_tags->name, tag))
523       {
524          return TRUE;
525       }
526       enabled_tags = enabled_tags->next;
527    }
528
529    return FALSE;
530
531 }
532
533 /*********************************************************************
534  *
535  * Function    :  enable_client_specific_tag
536  *
537  * Description :  Enables a client-specific-tag for the client
538  *
539  * Parameters  :
540  *          1  :  csp = Current client state (buffers, headers, etc...)
541  *          2  :  tag_name = The name of the tag to enable
542  *          3  :  time_to_live = If not 0, the number of seconds the
543  *                               tag should stay enabled.
544  *
545  * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
546  *
547  *********************************************************************/
548 jb_err enable_client_specific_tag(struct client_state *csp,
549    const char *tag_name, const time_t time_to_live)
550 {
551    struct client_tag_spec *tag;
552
553    privoxy_mutex_lock(&client_tags_mutex);
554
555    tag = get_client_specific_tag(csp->config->client_tags, tag_name);
556    if (tag == NULL)
557    {
558       privoxy_mutex_unlock(&client_tags_mutex);
559       return JB_ERR_PARSE;
560    }
561
562    if (client_has_requested_tag(csp->client_address, tag_name))
563    {
564       log_error(LOG_LEVEL_ERROR,
565          "Tag '%s' already enabled for client '%s'", tag->name, csp->client_address);
566    }
567    else
568    {
569       add_tag_for_client(csp->client_address, tag_name, time_to_live);
570       log_error(LOG_LEVEL_INFO,
571          "Tag '%s' enabled for client '%s'. TTL: %d.",
572          tag->name, csp->client_address, time_to_live);
573    }
574
575    privoxy_mutex_unlock(&client_tags_mutex);
576
577    return JB_ERR_OK;
578
579 }
580
581 /*********************************************************************
582  *
583  * Function    :  disable_client_specific_tag
584  *
585  * Description :  Disables a client-specific-tag for the client
586  *
587  * Parameters  :
588  *          1  :  csp = Current client state (buffers, headers, etc...)
589  *          2  :  tag_name = The name of the tag to disable
590  *
591  * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
592  *
593  *********************************************************************/
594 jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
595 {
596    struct client_tag_spec *tag;
597
598    privoxy_mutex_lock(&client_tags_mutex);
599
600    tag = get_client_specific_tag(csp->config->client_tags, tag_name);
601    if (tag == NULL)
602    {
603       privoxy_mutex_unlock(&client_tags_mutex);
604       return JB_ERR_PARSE;
605    }
606
607    if (client_has_requested_tag(csp->client_address, tag_name))
608    {
609       remove_tag_for_client(csp->client_address, tag_name);
610       log_error(LOG_LEVEL_INFO,
611          "Tag '%s' disabled for client '%s'", tag->name, csp->client_address);
612    }
613    else
614    {
615       log_error(LOG_LEVEL_ERROR,
616          "Tag '%s' currently not set for client '%s'",
617          tag->name, csp->client_address);
618    }
619
620    privoxy_mutex_unlock(&client_tags_mutex);
621    return JB_ERR_OK;
622
623 }
624
625
626 /*********************************************************************
627  *
628  * Function    :  client_tag_match
629  *
630  * Description :  Compare a client tag against a client tag pattern.
631  *
632  * Parameters  :
633  *          1  :  pattern = a TAG pattern
634  *          2  :  tag = Client tag to match
635  *
636  * Returns     :  Nonzero if the tag matches the pattern, else 0.
637  *
638  *********************************************************************/
639 int client_tag_match(const struct pattern_spec *pattern,
640                      const struct list *tags)
641 {
642    struct list_entry *tag;
643
644    if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
645    {
646       /*
647        * It's not a client pattern and thus shouldn't
648        * be matched against client tags.
649        */
650       return 0;
651    }
652
653    assert(tags);
654
655    for (tag = tags->first; tag != NULL; tag = tag->next)
656    {
657       if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
658       {
659          return 1;
660       }
661    }
662
663    return 0;
664
665 }
666
667
668 /*********************************************************************
669  *
670  * Function    :  set_client_address
671  *
672  * Description :  Sets the client address that will be used to enable,
673  *                disable, or apply client tags.
674  *
675  * Parameters  :
676  *          1  :  csp = Current client state (buffers, headers, etc...)
677  *          2  :  headers = Client headers
678  *
679  * Returns     :  void.
680  *
681  *********************************************************************/
682 void set_client_address(struct client_state *csp, const struct list *headers)
683 {
684    if (csp->config->trust_x_forwarded_for)
685    {
686       const char *client_address;
687
688       client_address = get_header_value(headers, "X-Forwarded-For:");
689       if (client_address != NULL)
690       {
691          csp->client_address = strdup_or_die(client_address);
692          log_error(LOG_LEVEL_HEADER,
693             "Got client address %s from X-Forwarded-For header",
694             csp->client_address);
695       }
696    }
697
698    if (csp->client_address == NULL)
699    {
700       csp->client_address = strdup_or_die(csp->ip_addr_str);
701    }
702 }
703
704 #else
705 #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
706 #endif /* def FEATURE_CLIENT_TAGS */