1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/client-tags.c,v $
5 * Purpose : Functions related to client-specific tags.
7 * Copyright : Copyright (C) 2016-2017 Fabian Keil <fk@fabiankeil.de>
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.
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.
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.
27 **********************************************************************/
31 #ifdef FEATURE_CLIENT_TAGS
34 #include <sys/types.h>
47 struct client_specific_tag
53 struct client_specific_tag *next;
54 struct client_specific_tag *prev;
58 * This struct represents tags that have been requested by clients
62 char *client; /**< The IP address of the client that requested the tag */
64 /**< List of tags the client requested .... */
65 struct client_specific_tag *tags;
67 struct requested_tags *next;
68 struct requested_tags *prev;
71 struct requested_tags *requested_tags;
72 static void remove_tag_for_client(const char *client_address, const char *tag);
74 /*********************************************************************
76 * Function : validate_tag_list
78 * Description : Validates the given tag list
81 * 1 : enabled_tags = The tags to validate
85 *********************************************************************/
86 static void validate_tag_list(struct client_specific_tag *enabled_tags)
88 while (enabled_tags != NULL)
90 if (enabled_tags->name == NULL)
92 assert(enabled_tags->name != NULL);
93 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
95 if (enabled_tags->next != NULL)
97 if (enabled_tags->next->prev != enabled_tags)
99 assert(enabled_tags->next->prev == enabled_tags);
100 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
103 enabled_tags = enabled_tags->next;
107 /*********************************************************************
109 * Function : validate_requested_tags
111 * Description : Validates the requested_tags list
117 *********************************************************************/
118 static jb_err validate_requested_tags()
120 struct requested_tags *requested_tag;
122 for (requested_tag = requested_tags; requested_tag != NULL;
123 requested_tag = requested_tag->next)
125 if (requested_tag->client == NULL)
127 assert(requested_tag->client != NULL);
128 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
130 validate_tag_list(requested_tag->tags);
131 if (requested_tag->next != NULL)
133 if (requested_tag->next->prev != requested_tag)
135 assert(requested_tag->next->prev == requested_tag);
136 log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
145 /*********************************************************************
147 * Function : get_client_specific_tag
149 * Description : Returns the data for a client-specific-tag specified
153 * 1 : tag_list = The tag list to check
154 * 2 : name = The tag name to look up
156 * Returns : Pointer to tag structure or NULL on error.
158 *********************************************************************/
159 static struct client_tag_spec *get_client_specific_tag(
160 struct client_tag_spec *tag_list, const char *name)
162 struct client_tag_spec *tag;
164 for (tag = tag_list; tag != NULL; tag = tag->next)
166 if (tag->name != NULL && !strcmp(tag->name, name))
172 log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
179 /*********************************************************************
181 * Function : get_tags_for_client
183 * Description : Returns the list of tags the client opted-in.
186 * 1 : client_address = Address of the client
188 * Returns : Pointer to tag structure or NULL on error.
190 *********************************************************************/
191 static struct client_specific_tag *get_tags_for_client(const char *client_address)
193 struct requested_tags *requested_tag;
195 for (requested_tag = requested_tags; requested_tag != NULL;
196 requested_tag = requested_tag->next)
198 if (!strcmp(requested_tag->client, client_address))
200 return requested_tag->tags;
208 /*********************************************************************
210 * Function : get_tag_list_for_client
212 * Description : Provides a list of tag names the client opted-in.
213 * Other tag attributes are not part of the list.
216 * 1 : tag_list = The list to fill in.
217 * 2 : client_address = Address of the client
219 * Returns : Pointer to tag list.
221 *********************************************************************/
222 void get_tag_list_for_client(struct list *tag_list,
223 const char *client_address)
225 struct client_specific_tag *enabled_tags;
226 const time_t now = time(NULL);
228 privoxy_mutex_lock(&client_tags_mutex);
230 enabled_tags = get_tags_for_client(client_address);
231 while (enabled_tags != NULL)
233 if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
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;
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);
250 enabled_tags = enabled_tags->next;
253 privoxy_mutex_unlock(&client_tags_mutex);
257 /*********************************************************************
259 * Function : get_next_tag_timeout_for_client
261 * Description : Figures out when the next temporarily enabled tag
262 * for the client will have timed out.
265 * 1 : client_address = Address of the client
267 * Returns : Lowest timeout in seconds
269 *********************************************************************/
270 time_t get_next_tag_timeout_for_client(const char *client_address)
272 struct client_specific_tag *enabled_tags;
273 time_t next_timeout = 0;
274 const time_t now = time(NULL);
276 privoxy_mutex_lock(&client_tags_mutex);
278 enabled_tags = get_tags_for_client(client_address);
279 while (enabled_tags != NULL)
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)
286 time_t time_left = enabled_tags->end_of_life - now;
287 /* Add a second to make sure the tag will have expired */
289 log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
290 if (next_timeout == 0 || next_timeout > time_left)
292 next_timeout = time_left;
295 enabled_tags = enabled_tags->next;
298 privoxy_mutex_unlock(&client_tags_mutex);
300 log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
307 /*********************************************************************
309 * Function : create_client_specific_tag
311 * Description : Allocates memory for a client specific tag
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.
319 * Returns : Pointer to populated tag
321 *********************************************************************/
322 static struct client_specific_tag *create_client_specific_tag(const char *name,
323 const time_t time_to_live)
325 struct client_specific_tag *tag;
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;
335 /*********************************************************************
337 * Function : add_tag_for_client
339 * Description : Adds the tag for the client.
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.
349 *********************************************************************/
350 static void add_tag_for_client(const char *client_address,
351 const char *tag, const time_t time_to_live)
353 struct requested_tags *clients_with_tags;
354 struct client_specific_tag *enabled_tags;
356 validate_requested_tags();
358 if (requested_tags == NULL)
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);
365 validate_requested_tags();
370 clients_with_tags = requested_tags;
371 while (clients_with_tags->next != NULL)
373 if (!strcmp(clients_with_tags->client, client_address))
377 clients_with_tags = clients_with_tags->next;
379 if (strcmp(clients_with_tags->client, client_address))
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);
388 validate_requested_tags();
394 enabled_tags = clients_with_tags->tags;
395 while (enabled_tags != NULL)
397 if (enabled_tags->next == NULL)
399 enabled_tags->next = create_client_specific_tag(tag, time_to_live);
400 enabled_tags->next->prev = enabled_tags;
403 enabled_tags = enabled_tags->next;
406 validate_requested_tags();
410 /*********************************************************************
412 * Function : remove_tag_for_client
414 * Description : Removes the tag for the client.
417 * 1 : client_address = Address of the client
418 * 2 : tag = The tag to remove.
422 *********************************************************************/
423 static void remove_tag_for_client(const char *client_address, const char *tag)
425 struct requested_tags *clients_with_tags;
426 struct client_specific_tag *enabled_tags;
428 validate_requested_tags();
430 clients_with_tags = requested_tags;
431 while (clients_with_tags != NULL && clients_with_tags->client != NULL)
433 if (!strcmp(clients_with_tags->client, client_address))
437 clients_with_tags = clients_with_tags->next;
440 assert(clients_with_tags != NULL);
441 if (clients_with_tags == NULL)
443 log_error(LOG_LEVEL_ERROR,
444 "Tried to remove tag %s for tag-less client %s",
445 tag, client_address);
447 enabled_tags = clients_with_tags->tags;
448 while (enabled_tags != NULL)
450 if (!strcmp(enabled_tags->name, tag))
452 if (enabled_tags->next != NULL)
454 enabled_tags->next->prev = enabled_tags->prev;
455 if (enabled_tags == clients_with_tags->tags)
457 /* Tag is first in line */
458 clients_with_tags->tags = enabled_tags->next;
461 if (enabled_tags->prev != NULL)
463 /* Tag has preceding tag */
464 enabled_tags->prev->next = enabled_tags->next;
466 if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
468 /* Tag is the only one */
469 if (clients_with_tags->next != NULL)
471 /* Client has following client */
472 clients_with_tags->next->prev = clients_with_tags->prev;
474 if (clients_with_tags->prev != NULL)
476 /* Client has preceding client */
477 clients_with_tags->prev->next = clients_with_tags->next;
479 if (clients_with_tags == requested_tags)
482 * We're in the process of removing the last tag,
483 * mark the global list as empty.
485 requested_tags = NULL;
487 freez(clients_with_tags->client);
488 freez(clients_with_tags);
490 freez(enabled_tags->name);
495 enabled_tags = enabled_tags->next;
498 validate_requested_tags();
503 /*********************************************************************
505 * Function : client_has_requested_tag
507 * Description : Checks whether or not the given client requested
511 * 1 : client_address = Address of the client
512 * 2 : tag = Tag to check.
514 * Returns : TRUE or FALSE.
516 *********************************************************************/
517 int client_has_requested_tag(const char *client_address, const char *tag)
519 struct client_specific_tag *enabled_tags;
521 enabled_tags = get_tags_for_client(client_address);
523 while (enabled_tags != NULL)
525 if (!strcmp(enabled_tags->name, tag))
529 enabled_tags = enabled_tags->next;
536 /*********************************************************************
538 * Function : enable_client_specific_tag
540 * Description : Enables a client-specific-tag for the client
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.
548 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
550 *********************************************************************/
551 jb_err enable_client_specific_tag(struct client_state *csp,
552 const char *tag_name, const time_t time_to_live)
554 struct client_tag_spec *tag;
556 privoxy_mutex_lock(&client_tags_mutex);
558 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
561 privoxy_mutex_unlock(&client_tags_mutex);
565 if (client_has_requested_tag(csp->client_address, tag_name))
567 log_error(LOG_LEVEL_TAGGING,
568 "Tag '%s' already enabled for client '%s'", tag->name, csp->client_address);
572 add_tag_for_client(csp->client_address, tag_name, time_to_live);
573 log_error(LOG_LEVEL_TAGGING,
574 "Tag '%s' enabled for client '%s'. TTL: %ld.",
575 tag->name, csp->client_address, time_to_live);
578 privoxy_mutex_unlock(&client_tags_mutex);
584 /*********************************************************************
586 * Function : disable_client_specific_tag
588 * Description : Disables a client-specific-tag for the client
591 * 1 : csp = Current client state (buffers, headers, etc...)
592 * 2 : tag_name = The name of the tag to disable
594 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
596 *********************************************************************/
597 jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
599 struct client_tag_spec *tag;
601 privoxy_mutex_lock(&client_tags_mutex);
603 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
606 privoxy_mutex_unlock(&client_tags_mutex);
610 if (client_has_requested_tag(csp->client_address, tag_name))
612 remove_tag_for_client(csp->client_address, tag_name);
613 log_error(LOG_LEVEL_TAGGING,
614 "Tag '%s' disabled for client '%s'", tag->name, csp->client_address);
618 log_error(LOG_LEVEL_TAGGING,
619 "Tag '%s' currently not set for client '%s'",
620 tag->name, csp->client_address);
623 privoxy_mutex_unlock(&client_tags_mutex);
629 /*********************************************************************
631 * Function : client_tag_match
633 * Description : Compare a client tag against a client tag pattern.
636 * 1 : pattern = a TAG pattern
637 * 2 : tag = Client tag to match
639 * Returns : Nonzero if the tag matches the pattern, else 0.
641 *********************************************************************/
642 int client_tag_match(const struct pattern_spec *pattern,
643 const struct list *tags)
645 struct list_entry *tag;
647 if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
650 * It's not a client pattern and thus shouldn't
651 * be matched against client tags.
658 for (tag = tags->first; tag != NULL; tag = tag->next)
660 if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
662 log_error(LOG_LEVEL_TAGGING, "Client tag '%s' matches", tag->str);
672 /*********************************************************************
674 * Function : set_client_address
676 * Description : Sets the client address that will be used to enable,
677 * disable, or apply client tags.
680 * 1 : csp = Current client state (buffers, headers, etc...)
681 * 2 : headers = Client headers
685 *********************************************************************/
686 void set_client_address(struct client_state *csp, const struct list *headers)
688 if (csp->config->trust_x_forwarded_for)
690 const char *client_address;
692 client_address = get_header_value(headers, "X-Forwarded-For:");
693 if (client_address != NULL)
695 csp->client_address = strdup_or_die(client_address);
696 log_error(LOG_LEVEL_HEADER,
697 "Got client address %s from X-Forwarded-For header",
698 csp->client_address);
702 if (csp->client_address == NULL)
704 csp->client_address = strdup_or_die(csp->ip_addr_str);
709 #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
710 #endif /* def FEATURE_CLIENT_TAGS */