Fix spelling
[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_TAGGING,
237             "Tag '%s' for client %s expired %ld 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          log_error(LOG_LEVEL_TAGGING, "Enlisting tag '%s' for client %s.",
247             enabled_tags->name, client_address);
248          enlist(tag_list, enabled_tags->name);
249       }
250       enabled_tags = enabled_tags->next;
251    }
252
253    privoxy_mutex_unlock(&client_tags_mutex);
254 }
255
256
257 /*********************************************************************
258  *
259  * Function    :  get_next_tag_timeout_for_client
260  *
261  * Description :  Figures out when the next temporarily enabled tag
262  *                for the client will have timed out.
263  *
264  * Parameters  :
265  *          1  :  client_address = Address of the client
266  *
267  * Returns     :  Lowest timeout in seconds
268  *
269  *********************************************************************/
270 time_t get_next_tag_timeout_for_client(const char *client_address)
271 {
272    struct client_specific_tag *enabled_tags;
273    time_t next_timeout = 0;
274    const time_t now = time(NULL);
275
276    privoxy_mutex_lock(&client_tags_mutex);
277
278    enabled_tags = get_tags_for_client(client_address);
279    while (enabled_tags != NULL)
280    {
281       log_error(LOG_LEVEL_TAGGING,
282          "Evaluating tag '%s' for client %s. End of life %ld.",
283          enabled_tags->name, client_address, enabled_tags->end_of_life);
284       if (enabled_tags->end_of_life)
285       {
286           time_t time_left = enabled_tags->end_of_life - now;
287           /* Add a second to make sure the tag will have expired */
288           time_left++;
289           log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
290           if (next_timeout == 0 || next_timeout > time_left)
291           {
292              next_timeout = time_left;
293           }
294        }
295        enabled_tags = enabled_tags->next;
296    }
297
298    privoxy_mutex_unlock(&client_tags_mutex);
299
300    log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
301
302    return next_timeout;
303
304 }
305
306
307 /*********************************************************************
308  *
309  * Function    :  create_client_specific_tag
310  *
311  * Description :  Allocates memory for a client specific tag
312  *                and populates it.
313  *
314  * Parameters  :
315  *          1  :  name = The name of the tag to create.
316  *          2  :  time_to_live = 0, or the number of seconds
317  *                               the tag remains activated.
318  *
319  * Returns     :  Pointer to populated tag
320  *
321  *********************************************************************/
322 static struct client_specific_tag *create_client_specific_tag(const char *name,
323    const time_t time_to_live)
324 {
325    struct client_specific_tag *tag;
326
327    tag = zalloc_or_die(sizeof(struct client_specific_tag));
328    tag->name = strdup_or_die(name);
329    tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
330
331    return tag;
332
333 }
334
335 /*********************************************************************
336  *
337  * Function    :  add_tag_for_client
338  *
339  * Description :  Adds the tag for the client.
340  *
341  * Parameters  :
342  *          1  :  client_address = Address of the client
343  *          2  :  tag = The tag to add.
344  *          3  :  time_to_live = 0, or the number of seconds
345  *                               the tag remains activated.
346  *
347  * Returns     :  void
348  *
349  *********************************************************************/
350 static void add_tag_for_client(const char *client_address,
351    const char *tag, const time_t time_to_live)
352 {
353    struct requested_tags *clients_with_tags;
354    struct client_specific_tag *enabled_tags;
355
356    validate_requested_tags();
357
358    if (requested_tags == NULL)
359    {
360       /* XXX: Code duplication. */
361       requested_tags = zalloc_or_die(sizeof(struct requested_tags));
362       requested_tags->client = strdup_or_die(client_address);
363       requested_tags->tags = create_client_specific_tag(tag, time_to_live);
364
365       validate_requested_tags();
366       return;
367    }
368    else
369    {
370       clients_with_tags = requested_tags;
371       while (clients_with_tags->next != NULL)
372       {
373          if (!strcmp(clients_with_tags->client, client_address))
374          {
375             break;
376          }
377          clients_with_tags = clients_with_tags->next;
378       }
379       if (strcmp(clients_with_tags->client, client_address))
380       {
381          /* Client does not have tags yet, add new structure */
382          clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
383          clients_with_tags->next->prev = clients_with_tags;
384          clients_with_tags = clients_with_tags->next;
385          clients_with_tags->client = strdup_or_die(client_address);
386          clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
387
388          validate_requested_tags();
389
390          return;
391       }
392    }
393
394    enabled_tags = clients_with_tags->tags;
395    while (enabled_tags != NULL)
396    {
397       if (enabled_tags->next == NULL)
398       {
399          enabled_tags->next = create_client_specific_tag(tag, time_to_live);
400          enabled_tags->next->prev = enabled_tags;
401          break;
402       }
403       enabled_tags = enabled_tags->next;
404    }
405
406    validate_requested_tags();
407 }
408
409
410 /*********************************************************************
411  *
412  * Function    :  remove_tag_for_client
413  *
414  * Description :  Removes the tag for the client.
415  *
416  * Parameters  :
417  *          1  :  client_address = Address of the client
418  *          2  :  tag = The tag to remove.
419  *
420  * Returns     :  void
421  *
422  *********************************************************************/
423 static void remove_tag_for_client(const char *client_address, const char *tag)
424 {
425    struct requested_tags *clients_with_tags;
426    struct client_specific_tag *enabled_tags;
427
428    validate_requested_tags();
429
430    clients_with_tags = requested_tags;
431    while (clients_with_tags != NULL && clients_with_tags->client != NULL)
432    {
433       if (!strcmp(clients_with_tags->client, client_address))
434       {
435          break;
436       }
437       clients_with_tags = clients_with_tags->next;
438    }
439
440    assert(clients_with_tags != NULL);
441    if (clients_with_tags == NULL)
442    {
443       log_error(LOG_LEVEL_ERROR,
444          "Tried to remove tag %s for tag-less client %s",
445          tag, client_address);
446    }
447    enabled_tags = clients_with_tags->tags;
448    while (enabled_tags != NULL)
449    {
450       if (!strcmp(enabled_tags->name, tag))
451       {
452          if (enabled_tags->next != NULL)
453          {
454             enabled_tags->next->prev = enabled_tags->prev;
455             if (enabled_tags == clients_with_tags->tags)
456             {
457                /* Tag is first in line */
458                clients_with_tags->tags = enabled_tags->next;
459             }
460          }
461          if (enabled_tags->prev != NULL)
462          {
463             /* Tag has preceding tag */
464             enabled_tags->prev->next = enabled_tags->next;
465          }
466          if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
467          {
468             /* Tag is the only one */
469             if (clients_with_tags->next != NULL)
470             {
471                /* Client has following client */
472                clients_with_tags->next->prev = clients_with_tags->prev;
473             }
474             if (clients_with_tags->prev != NULL)
475             {
476                /* Client has preceding client */
477                clients_with_tags->prev->next = clients_with_tags->next;
478             }
479             if (clients_with_tags == requested_tags)
480             {
481                /*
482                 * We're in the process of removing the last tag,
483                 * mark the global list as empty.
484                 */
485                requested_tags = NULL;
486             }
487             freez(clients_with_tags->client);
488             freez(clients_with_tags);
489          }
490          freez(enabled_tags->name);
491          freez(enabled_tags);
492          break;
493       }
494
495       enabled_tags = enabled_tags->next;
496    }
497
498    validate_requested_tags();
499
500 }
501
502
503 /*********************************************************************
504  *
505  * Function    :  client_has_requested_tag
506  *
507  * Description :  Checks whether or not the given client requested
508  *                the tag.
509  *
510  * Parameters  :
511  *          1  :  client_address = Address of the client
512  *          2  :  tag = Tag to check.
513  *
514  * Returns     :  TRUE or FALSE.
515  *
516  *********************************************************************/
517 int client_has_requested_tag(const char *client_address, const char *tag)
518 {
519    struct client_specific_tag *enabled_tags;
520
521    enabled_tags = get_tags_for_client(client_address);
522
523    while (enabled_tags != NULL)
524    {
525       if (!strcmp(enabled_tags->name, tag))
526       {
527          return TRUE;
528       }
529       enabled_tags = enabled_tags->next;
530    }
531
532    return FALSE;
533
534 }
535
536 /*********************************************************************
537  *
538  * Function    :  enable_client_specific_tag
539  *
540  * Description :  Enables a client-specific-tag for the client
541  *
542  * Parameters  :
543  *          1  :  csp = Current client state (buffers, headers, etc...)
544  *          2  :  tag_name = The name of the tag to enable
545  *          3  :  time_to_live = If not 0, the number of seconds the
546  *                               tag should stay enabled.
547  *
548  * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
549  *
550  *********************************************************************/
551 jb_err enable_client_specific_tag(struct client_state *csp,
552    const char *tag_name, const time_t time_to_live)
553 {
554    struct client_tag_spec *tag;
555
556    privoxy_mutex_lock(&client_tags_mutex);
557
558    tag = get_client_specific_tag(csp->config->client_tags, tag_name);
559    if (tag == NULL)
560    {
561       privoxy_mutex_unlock(&client_tags_mutex);
562       return JB_ERR_PARSE;
563    }
564
565    if (client_has_requested_tag(csp->client_address, tag_name))
566    {
567       log_error(LOG_LEVEL_TAGGING,
568          "Tag '%s' already enabled for client '%s'.",
569          tag->name, csp->client_address);
570    }
571    else
572    {
573       add_tag_for_client(csp->client_address, tag_name, time_to_live);
574       log_error(LOG_LEVEL_TAGGING,
575          "Tag '%s' enabled for client '%s'. TTL: %ld.",
576          tag->name, csp->client_address, time_to_live);
577    }
578
579    privoxy_mutex_unlock(&client_tags_mutex);
580
581    return JB_ERR_OK;
582
583 }
584
585 /*********************************************************************
586  *
587  * Function    :  disable_client_specific_tag
588  *
589  * Description :  Disables a client-specific-tag for the client
590  *
591  * Parameters  :
592  *          1  :  csp = Current client state (buffers, headers, etc...)
593  *          2  :  tag_name = The name of the tag to disable
594  *
595  * Returns     :  JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
596  *
597  *********************************************************************/
598 jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
599 {
600    struct client_tag_spec *tag;
601
602    privoxy_mutex_lock(&client_tags_mutex);
603
604    tag = get_client_specific_tag(csp->config->client_tags, tag_name);
605    if (tag == NULL)
606    {
607       privoxy_mutex_unlock(&client_tags_mutex);
608       return JB_ERR_PARSE;
609    }
610
611    if (client_has_requested_tag(csp->client_address, tag_name))
612    {
613       remove_tag_for_client(csp->client_address, tag_name);
614       log_error(LOG_LEVEL_TAGGING,
615          "Tag '%s' disabled for client '%s'.", tag->name, csp->client_address);
616    }
617    else
618    {
619       log_error(LOG_LEVEL_TAGGING,
620          "Tag '%s' currently not set for client '%s'.",
621          tag->name, csp->client_address);
622    }
623
624    privoxy_mutex_unlock(&client_tags_mutex);
625    return JB_ERR_OK;
626
627 }
628
629
630 /*********************************************************************
631  *
632  * Function    :  client_tag_match
633  *
634  * Description :  Compare a client tag against a client tag pattern.
635  *
636  * Parameters  :
637  *          1  :  pattern = a TAG pattern
638  *          2  :  tag = Client tag to match
639  *
640  * Returns     :  Nonzero if the tag matches the pattern, else 0.
641  *
642  *********************************************************************/
643 int client_tag_match(const struct pattern_spec *pattern,
644                      const struct list *tags)
645 {
646    struct list_entry *tag;
647
648    if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
649    {
650       /*
651        * It's not a client pattern and thus shouldn't
652        * be matched against client tags.
653        */
654       return 0;
655    }
656
657    assert(tags);
658
659    for (tag = tags->first; tag != NULL; tag = tag->next)
660    {
661       if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
662       {
663          log_error(LOG_LEVEL_TAGGING, "Client tag '%s' matches.", tag->str);
664          return 1;
665       }
666    }
667
668    return 0;
669
670 }
671
672
673 /*********************************************************************
674  *
675  * Function    :  set_client_address
676  *
677  * Description :  Sets the client address that will be used to enable,
678  *                disable, or apply client tags.
679  *
680  * Parameters  :
681  *          1  :  csp = Current client state (buffers, headers, etc...)
682  *          2  :  headers = Client headers
683  *
684  * Returns     :  void.
685  *
686  *********************************************************************/
687 void set_client_address(struct client_state *csp, const struct list *headers)
688 {
689    if (csp->config->trust_x_forwarded_for)
690    {
691       const char *client_address;
692
693       client_address = get_header_value(headers, "X-Forwarded-For:");
694       if (client_address != NULL)
695       {
696          csp->client_address = strdup_or_die(client_address);
697          log_error(LOG_LEVEL_HEADER,
698             "Got client address %s from X-Forwarded-For header",
699             csp->client_address);
700       }
701    }
702
703    if (csp->client_address == NULL)
704    {
705       csp->client_address = strdup_or_die(csp->ip_addr_str);
706    }
707 }
708
709 #else
710 #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
711 #endif /* def FEATURE_CLIENT_TAGS */