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 **********************************************************************/
32 #include <sys/types.h>
44 struct client_specific_tag
50 struct client_specific_tag *next;
51 struct client_specific_tag *prev;
55 * This struct represents tags that have been requested by clients
59 char *client; /**< The IP address of the client that requested the tag */
61 /**< List of tags the client requested .... */
62 struct client_specific_tag *tags;
64 struct requested_tags *next;
65 struct requested_tags *prev;
68 struct requested_tags *requested_tags;
69 static void remove_tag_for_client(const char *client_address, const char *tag);
71 /*********************************************************************
73 * Function : validate_tag_list
75 * Description : Validates the given tag list
78 * 1 : enabled_tags = The tags to validate
82 *********************************************************************/
83 static void validate_tag_list(struct client_specific_tag *enabled_tags)
85 while (enabled_tags != NULL)
87 if (enabled_tags->name == NULL)
89 assert(enabled_tags->name != NULL);
90 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
92 if (enabled_tags->next != NULL)
94 if (enabled_tags->next->prev != enabled_tags)
96 assert(enabled_tags->next->prev == enabled_tags);
97 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
100 enabled_tags = enabled_tags->next;
104 /*********************************************************************
106 * Function : validate_requested_tags
108 * Description : Validates the requested_tags list
114 *********************************************************************/
115 static jb_err validate_requested_tags()
117 struct requested_tags *requested_tag;
119 for (requested_tag = requested_tags; requested_tag != NULL;
120 requested_tag = requested_tag->next)
122 if (requested_tag->client == NULL)
124 assert(requested_tag->client != NULL);
125 log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
127 validate_tag_list(requested_tag->tags);
128 if (requested_tag->next != NULL)
130 if (requested_tag->next->prev != requested_tag)
132 assert(requested_tag->next->prev == requested_tag);
133 log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
142 /*********************************************************************
144 * Function : get_client_specific_tag
146 * Description : Returns the data for a client-specific-tag specified
150 * 1 : tag_list = The tag list to check
151 * 2 : name = The tag name to look up
153 * Returns : Pointer to tag structure or NULL on error.
155 *********************************************************************/
156 static struct client_tag_spec *get_client_specific_tag(
157 struct client_tag_spec *tag_list, const char *name)
159 struct client_tag_spec *tag;
161 for (tag = tag_list; tag != NULL; tag = tag->next)
163 if (tag->name != NULL && !strcmp(tag->name, name))
169 log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
176 /*********************************************************************
178 * Function : get_tags_for_client
180 * Description : Returns the list of tags the client opted-in.
183 * 1 : client_address = Address of the client
185 * Returns : Pointer to tag structure or NULL on error.
187 *********************************************************************/
188 static struct client_specific_tag *get_tags_for_client(const char *client_address)
190 struct requested_tags *requested_tag;
192 for (requested_tag = requested_tags; requested_tag != NULL;
193 requested_tag = requested_tag->next)
195 if (!strcmp(requested_tag->client, client_address))
197 return requested_tag->tags;
205 /*********************************************************************
207 * Function : get_tag_list_for_client
209 * Description : Provides a list of tag names the client opted-in.
210 * Other tag attributes are not part of the list.
213 * 1 : tag_list = The list to fill in.
214 * 2 : client_address = Address of the client
216 * Returns : Pointer to tag list.
218 *********************************************************************/
219 void get_tag_list_for_client(struct list *tag_list,
220 const char *client_address)
222 struct client_specific_tag *enabled_tags;
223 const time_t now = time(NULL);
225 privoxy_mutex_lock(&client_tags_mutex);
227 enabled_tags = get_tags_for_client(client_address);
228 while (enabled_tags != NULL)
230 if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
232 struct client_specific_tag *next_tag = enabled_tags->next;
233 log_error(LOG_LEVEL_INFO,
234 "Tag '%s' for client %s expired %u seconds ago. Deleting it.",
235 enabled_tags->name, client_address,
236 (now - enabled_tags->end_of_life));
237 remove_tag_for_client(client_address, enabled_tags->name);
238 enabled_tags = next_tag;
243 enlist(tag_list, enabled_tags->name);
245 enabled_tags = enabled_tags->next;
248 privoxy_mutex_unlock(&client_tags_mutex);
252 /*********************************************************************
254 * Function : add_tag_for_client
256 * Description : Adds the tag for the client.
259 * 1 : client_address = Address of the client
260 * 2 : tag = The tag to add.
261 * 3 : time_to_live = 0, or the number of seconds
262 * the tag remains activated.
266 *********************************************************************/
267 static void add_tag_for_client(const char *client_address,
268 const char *tag, const time_t time_to_live)
270 struct requested_tags *clients_with_tags;
271 struct client_specific_tag *enabled_tags;
273 validate_requested_tags();
275 if (requested_tags == NULL)
277 /* XXX: Code duplication. */
278 requested_tags = zalloc_or_die(sizeof(struct requested_tags));
279 requested_tags->client = strdup_or_die(client_address);
280 requested_tags->tags = zalloc_or_die(sizeof(struct client_specific_tag));
281 requested_tags->tags->name = strdup_or_die(tag);
282 requested_tags->tags->end_of_life = time_to_live ?
283 (time(NULL) + time_to_live) : 0;
285 validate_requested_tags();
290 clients_with_tags = requested_tags;
291 while (clients_with_tags->next != NULL)
293 if (!strcmp(clients_with_tags->client, client_address))
297 clients_with_tags = clients_with_tags->next;
299 if (strcmp(clients_with_tags->client, client_address))
301 /* Client does not have tags yet, add new structure */
302 clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
303 clients_with_tags->next->prev = clients_with_tags;
304 clients_with_tags = clients_with_tags->next;
305 clients_with_tags->client = strdup_or_die(client_address);
306 clients_with_tags->tags = zalloc_or_die(sizeof(struct client_specific_tag));
307 clients_with_tags->tags->name = strdup_or_die(tag);
308 clients_with_tags->tags->end_of_life = time_to_live ?
309 (time(NULL) + time_to_live) : 0;
311 validate_requested_tags();
317 enabled_tags = clients_with_tags->tags;
318 while (enabled_tags != NULL)
320 if (enabled_tags->next == NULL)
322 enabled_tags->next = zalloc_or_die(sizeof(struct client_specific_tag));
323 enabled_tags->next->name = strdup_or_die(tag);
324 clients_with_tags->tags->end_of_life = time_to_live ?
325 (time(NULL) + time_to_live) : 0;
326 enabled_tags->next->prev = enabled_tags;
329 enabled_tags = enabled_tags->next;
332 validate_requested_tags();
336 /*********************************************************************
338 * Function : remove_tag_for_client
340 * Description : Removes the tag for the client.
343 * 1 : client_address = Address of the client
344 * 2 : tag = The tag to remove.
348 *********************************************************************/
349 static void remove_tag_for_client(const char *client_address, const char *tag)
351 struct requested_tags *clients_with_tags;
352 struct client_specific_tag *enabled_tags;
354 validate_requested_tags();
356 clients_with_tags = requested_tags;
357 while (clients_with_tags != NULL && clients_with_tags->client != NULL)
359 if (!strcmp(clients_with_tags->client, client_address))
363 clients_with_tags = clients_with_tags->next;
366 enabled_tags = clients_with_tags->tags;
367 while (enabled_tags != NULL)
369 if (!strcmp(enabled_tags->name, tag))
371 if (enabled_tags->next != NULL)
373 enabled_tags->next->prev = enabled_tags->prev;
374 if (enabled_tags == clients_with_tags->tags)
376 /* Tag is first in line */
377 clients_with_tags->tags = enabled_tags->next;
380 if (enabled_tags->prev != NULL)
382 /* Tag has preceding tag */
383 enabled_tags->prev->next = enabled_tags->next;
385 if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
387 /* Tag is the only one */
388 if (clients_with_tags->next != NULL)
390 /* Client has following client */
391 clients_with_tags->next->prev = clients_with_tags->prev;
393 if (clients_with_tags->prev != NULL)
395 /* Client has preceding client */
396 clients_with_tags->prev->next = clients_with_tags->next;
398 freez(clients_with_tags->client);
399 if (clients_with_tags == requested_tags)
401 /* Removing last tag */
402 freez(requested_tags);
403 clients_with_tags = requested_tags;
407 freez(clients_with_tags);
410 freez(enabled_tags->name);
415 enabled_tags = enabled_tags->next;
418 validate_requested_tags();
423 /*********************************************************************
425 * Function : client_has_requested_tag
427 * Description : Checks whether or not the given client requested
431 * 1 : client_address = Address of the client
432 * 2 : tag = Tag to check.
434 * Returns : TRUE or FALSE.
436 *********************************************************************/
437 int client_has_requested_tag(const char *client_address, const char *tag)
439 struct client_specific_tag *enabled_tags;
441 enabled_tags = get_tags_for_client(client_address);
443 while (enabled_tags != NULL)
445 if (!strcmp(enabled_tags->name, tag))
449 enabled_tags = enabled_tags->next;
456 /*********************************************************************
458 * Function : enable_client_specific_tag
460 * Description : Enables a client-specific-tag for the client
463 * 1 : csp = Current client state (buffers, headers, etc...)
464 * 2 : tag_name = The name of the tag to enable
465 * 3 : time_to_live = If not 0, the number of seconds the
466 * tag should stay enabled.
468 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
470 *********************************************************************/
471 jb_err enable_client_specific_tag(struct client_state *csp,
472 const char *tag_name, const time_t time_to_live)
474 struct client_tag_spec *tag;
476 privoxy_mutex_lock(&client_tags_mutex);
478 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
481 privoxy_mutex_unlock(&client_tags_mutex);
485 if (client_has_requested_tag(csp->ip_addr_str, tag_name))
487 log_error(LOG_LEVEL_ERROR,
488 "Tag '%s' already enabled for client '%s'", tag->name, csp->ip_addr_str);
492 add_tag_for_client(csp->ip_addr_str, tag_name, time_to_live);
493 log_error(LOG_LEVEL_INFO,
494 "Tag '%s' enabled for client '%s'. TTL: %d.",
495 tag->name, csp->ip_addr_str, time_to_live);
498 privoxy_mutex_unlock(&client_tags_mutex);
504 /*********************************************************************
506 * Function : disable_client_specific_tag
508 * Description : Disables a client-specific-tag for the client
511 * 1 : csp = Current client state (buffers, headers, etc...)
512 * 2 : tag_name = The name of the tag to disable
514 * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
516 *********************************************************************/
517 jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
519 struct client_tag_spec *tag;
521 privoxy_mutex_lock(&client_tags_mutex);
523 tag = get_client_specific_tag(csp->config->client_tags, tag_name);
526 privoxy_mutex_unlock(&client_tags_mutex);
530 if (client_has_requested_tag(csp->ip_addr_str, tag_name))
532 remove_tag_for_client(csp->ip_addr_str, tag_name);
533 log_error(LOG_LEVEL_INFO,
534 "Tag '%s' disabled for client '%s'", tag->name, csp->ip_addr_str);
538 log_error(LOG_LEVEL_ERROR,
539 "Tag '%s' currently not set for client '%s'",
540 tag->name, csp->ip_addr_str);
543 privoxy_mutex_unlock(&client_tags_mutex);
549 /*********************************************************************
551 * Function : client_tag_match
553 * Description : Compare a client tag against a client tag pattern.
556 * 1 : pattern = a TAG pattern
557 * 2 : tag = Client tag to match
559 * Returns : Nonzero if the tag matches the pattern, else 0.
561 *********************************************************************/
562 int client_tag_match(const struct pattern_spec *pattern,
563 const struct list *tags)
565 struct list_entry *tag;
567 if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
570 * It's not a client pattern and thus shouldn't
571 * be matched against client tags.
578 for (tag = tags->first; tag != NULL; tag = tag->next)
580 if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))