1 /*********************************************************************
3 * File : $Source: /cvsroot/ijbswa/current/client-tags.c,v $
5 * Purpose : Functions related to client-specific tags.
7 * Copyright : Copyright (C) 2016 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>
46 struct client_specific_tag
52 struct client_specific_tag *next;
53 struct client_specific_tag *prev;
57 * This struct represents tags that have been requested by clients
61 char *client; /**< The IP address of the client that requested the tag */
63 /**< List of tags the client requested .... */
64 struct client_specific_tag *tags;
66 struct requested_tags *next;
67 struct requested_tags *prev;
70 struct requested_tags *requested_tags;
71 static void remove_tag_for_client(const char *client_address, const char *tag);
73 /*********************************************************************
75 * Function : validate_tag_list
77 * Description : Validates the given tag list
80 * 1 : enabled_tags = The tags to validate
84 *********************************************************************/
85 static void validate_tag_list(struct client_specific_tag *enabled_tags)
87 while (enabled_tags != NULL)
89 if (enabled_tags->name == NULL)
91 assert(enabled_tags->name != NULL);
92 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
94 if (enabled_tags->next != NULL)
96 if (enabled_tags->next->prev != enabled_tags)
98 assert(enabled_tags->next->prev == enabled_tags);
99 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
102 enabled_tags = enabled_tags->next;
106 /*********************************************************************
108 * Function : validate_requested_tags
110 * Description : Validates the requested_tags list
116 *********************************************************************/
117 static jb_err validate_requested_tags()
119 struct requested_tags *requested_tag;
121 for (requested_tag = requested_tags; requested_tag != NULL;
122 requested_tag = requested_tag->next)
124 if (requested_tag->client == NULL)
126 assert(requested_tag->client != NULL);
127 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
129 validate_tag_list(requested_tag->tags);
130 if (requested_tag->next != NULL)
132 if (requested_tag->next->prev != requested_tag)
134 assert(requested_tag->next->prev == requested_tag);
135 log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
144 /*********************************************************************
146 * Function : get_client_specific_tag
148 * Description : Returns the data for a client-specific-tag specified
152 * 1 : tag_list = The tag list to check
153 * 2 : name = The tag name to look up
155 * Returns : Pointer to tag structure or NULL on error.
157 *********************************************************************/
158 static struct client_tag_spec *get_client_specific_tag(
159 struct client_tag_spec *tag_list, const char *name)
161 struct client_tag_spec *tag;
163 for (tag = tag_list; tag != NULL; tag = tag->next)
165 if (tag->name != NULL && !strcmp(tag->name, name))
171 log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
178 /*********************************************************************
180 * Function : get_tags_for_client
182 * Description : Returns the list of tags the client opted-in.
185 * 1 : client_address = Address of the client
187 * Returns : Pointer to tag structure or NULL on error.
189 *********************************************************************/
190 static struct client_specific_tag *get_tags_for_client(const char *client_address)
192 struct requested_tags *requested_tag;
194 for (requested_tag = requested_tags; requested_tag != NULL;
195 requested_tag = requested_tag->next)
197 if (!strcmp(requested_tag->client, client_address))
199 return requested_tag->tags;
207 /*********************************************************************
209 * Function : get_tag_list_for_client
211 * Description : Provides a list of tag names the client opted-in.
212 * Other tag attributes are not part of the list.
215 * 1 : tag_list = The list to fill in.
216 * 2 : client_address = Address of the client
218 * Returns : Pointer to tag list.
220 *********************************************************************/
221 void get_tag_list_for_client(struct list *tag_list,
222 const char *client_address)
224 struct client_specific_tag *enabled_tags;
225 const time_t now = time(NULL);
227 privoxy_mutex_lock(&client_tags_mutex);
229 enabled_tags = get_tags_for_client(client_address);
230 while (enabled_tags != NULL)
232 if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
234 struct client_specific_tag *next_tag = enabled_tags->next;
235 log_error(LOG_LEVEL_INFO,
236 "Tag '%s' for client %s expired %u seconds ago. Deleting it.",
237 enabled_tags->name, client_address,
238 (now - enabled_tags->end_of_life));
239 remove_tag_for_client(client_address, enabled_tags->name);
240 enabled_tags = next_tag;
245 enlist(tag_list, enabled_tags->name);
247 enabled_tags = enabled_tags->next;
250 privoxy_mutex_unlock(&client_tags_mutex);
254 /*********************************************************************
256 * Function : add_tag_for_client
258 * Description : Adds the tag for the client.
261 * 1 : client_address = Address of the client
262 * 2 : tag = The tag to add.
263 * 3 : time_to_live = 0, or the number of seconds
264 * the tag remains activated.
268 *********************************************************************/
269 static void add_tag_for_client(const char *client_address,
270 const char *tag, const time_t time_to_live)
272 struct requested_tags *clients_with_tags;
273 struct client_specific_tag *enabled_tags;
275 validate_requested_tags();
277 if (requested_tags == NULL)
279 /* XXX: Code duplication. */
280 requested_tags = zalloc_or_die(sizeof(struct requested_tags));
281 requested_tags->client = strdup_or_die(client_address);
282 requested_tags->tags = zalloc_or_die(sizeof(struct client_specific_tag));
283 requested_tags->tags->name = strdup_or_die(tag);
284 requested_tags->tags->end_of_life = time_to_live ?
285 (time(NULL) + time_to_live) : 0;
287 validate_requested_tags();
292 clients_with_tags = requested_tags;
293 while (clients_with_tags->next != NULL)
295 if (!strcmp(clients_with_tags->client, client_address))
299 clients_with_tags = clients_with_tags->next;
301 if (strcmp(clients_with_tags->client, client_address))
303 /* Client does not have tags yet, add new structure */
304 clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
305 clients_with_tags->next->prev = clients_with_tags;
306 clients_with_tags = clients_with_tags->next;
307 clients_with_tags->client = strdup_or_die(client_address);
308 clients_with_tags->tags = zalloc_or_die(sizeof(struct client_specific_tag));
309 clients_with_tags->tags->name = strdup_or_die(tag);
310 clients_with_tags->tags->end_of_life = time_to_live ?
311 (time(NULL) + time_to_live) : 0;
313 validate_requested_tags();
319 enabled_tags = clients_with_tags->tags;
320 while (enabled_tags != NULL)
322 if (enabled_tags->next == NULL)
324 enabled_tags->next = zalloc_or_die(sizeof(struct client_specific_tag));
325 enabled_tags->next->name = strdup_or_die(tag);
326 clients_with_tags->tags->end_of_life = time_to_live ?
327 (time(NULL) + time_to_live) : 0;
328 enabled_tags->next->prev = enabled_tags;
331 enabled_tags = enabled_tags->next;
334 validate_requested_tags();
338 /*********************************************************************
340 * Function : remove_tag_for_client
342 * Description : Removes the tag for the client.
345 * 1 : client_address = Address of the client
346 * 2 : tag = The tag to remove.
350 *********************************************************************/
351 static void remove_tag_for_client(const char *client_address, const char *tag)
353 struct requested_tags *clients_with_tags;
354 struct client_specific_tag *enabled_tags;
356 validate_requested_tags();
358 clients_with_tags = requested_tags;
359 while (clients_with_tags != NULL && clients_with_tags->client != NULL)
361 if (!strcmp(clients_with_tags->client, client_address))
365 clients_with_tags = clients_with_tags->next;
368 enabled_tags = clients_with_tags->tags;
369 while (enabled_tags != NULL)
371 if (!strcmp(enabled_tags->name, tag))
373 if (enabled_tags->next != NULL)
375 enabled_tags->next->prev = enabled_tags->prev;
376 if (enabled_tags == clients_with_tags->tags)
378 /* Tag is first in line */
379 clients_with_tags->tags = enabled_tags->next;
382 if (enabled_tags->prev != NULL)
384 /* Tag has preceding tag */
385 enabled_tags->prev->next = enabled_tags->next;
387 if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
389 /* Tag is the only one */
390 if (clients_with_tags->next != NULL)
392 /* Client has following client */
393 clients_with_tags->next->prev = clients_with_tags->prev;
395 if (clients_with_tags->prev != NULL)
397 /* Client has preceding client */
398 clients_with_tags->prev->next = clients_with_tags->next;
400 freez(clients_with_tags->client);
401 if (clients_with_tags == requested_tags)
403 /* Removing last tag */
404 freez(requested_tags);
405 clients_with_tags = requested_tags;
409 freez(clients_with_tags);
412 freez(enabled_tags->name);
417 enabled_tags = enabled_tags->next;
420 validate_requested_tags();
425 /*********************************************************************
427 * Function : client_has_requested_tag
429 * Description : Checks whether or not the given client requested
433 * 1 : client_address = Address of the client
434 * 2 : tag = Tag to check.
436 * Returns : TRUE or FALSE.
438 *********************************************************************/
439 int client_has_requested_tag(const char *client_address, const char *tag)
441 struct client_specific_tag *enabled_tags;
443 enabled_tags = get_tags_for_client(client_address);
445 while (enabled_tags != NULL)
447 if (!strcmp(enabled_tags->name, tag))
451 enabled_tags = enabled_tags->next;
458 /*********************************************************************
460 * Function : enable_client_specific_tag
462 * Description : Enables a client-specific-tag for the client
465 * 1 : csp = Current client state (buffers, headers, etc...)
466 * 2 : tag_name = The name of the tag to enable
467 * 3 : time_to_live = If not 0, the number of seconds the
468 * tag should stay enabled.
470 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
472 *********************************************************************/
473 jb_err enable_client_specific_tag(struct client_state *csp,
474 const char *tag_name, const time_t time_to_live)
476 struct client_tag_spec *tag;
478 privoxy_mutex_lock(&client_tags_mutex);
480 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
483 privoxy_mutex_unlock(&client_tags_mutex);
487 if (client_has_requested_tag(csp->ip_addr_str, tag_name))
489 log_error(LOG_LEVEL_ERROR,
490 "Tag '%s' already enabled for client '%s'", tag->name, csp->ip_addr_str);
494 add_tag_for_client(csp->ip_addr_str, tag_name, time_to_live);
495 log_error(LOG_LEVEL_INFO,
496 "Tag '%s' enabled for client '%s'. TTL: %d.",
497 tag->name, csp->ip_addr_str, time_to_live);
500 privoxy_mutex_unlock(&client_tags_mutex);
506 /*********************************************************************
508 * Function : disable_client_specific_tag
510 * Description : Disables a client-specific-tag for the client
513 * 1 : csp = Current client state (buffers, headers, etc...)
514 * 2 : tag_name = The name of the tag to disable
516 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
518 *********************************************************************/
519 jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
521 struct client_tag_spec *tag;
523 privoxy_mutex_lock(&client_tags_mutex);
525 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
528 privoxy_mutex_unlock(&client_tags_mutex);
532 if (client_has_requested_tag(csp->ip_addr_str, tag_name))
534 remove_tag_for_client(csp->ip_addr_str, tag_name);
535 log_error(LOG_LEVEL_INFO,
536 "Tag '%s' disabled for client '%s'", tag->name, csp->ip_addr_str);
540 log_error(LOG_LEVEL_ERROR,
541 "Tag '%s' currently not set for client '%s'",
542 tag->name, csp->ip_addr_str);
545 privoxy_mutex_unlock(&client_tags_mutex);
551 /*********************************************************************
553 * Function : client_tag_match
555 * Description : Compare a client tag against a client tag pattern.
558 * 1 : pattern = a TAG pattern
559 * 2 : tag = Client tag to match
561 * Returns : Nonzero if the tag matches the pattern, else 0.
563 *********************************************************************/
564 int client_tag_match(const struct pattern_spec *pattern,
565 const struct list *tags)
567 struct list_entry *tag;
569 if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
572 * It's not a client pattern and thus shouldn't
573 * be matched against client tags.
580 for (tag = tags->first; tag != NULL; tag = tag->next)
582 if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
592 #error Compiling client-tags.c without FEATURE_CLIENT_TAGS
593 #endif /* def FEATURE_CLIENT_TAGS */