Relocate CERT_INFO_PREFIX to ssl.c
[privoxy.git] / client-tags.c
index 165f820..5cb873d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Purpose     :  Functions related to client-specific tags.
  *
- * Copyright   :  Copyright (C) 2016 Fabian Keil <fk@fabiankeil.de>
+ * Copyright   :  Copyright (C) 2016-2017 Fabian Keil <fk@fabiankeil.de>
  *
  *                This program is free software; you can redistribute it
  *                and/or modify it under the terms of the GNU General
@@ -28,6 +28,8 @@
 
 #include "config.h"
 
+#ifdef FEATURE_CLIENT_TAGS
+
 #include <stdio.h>
 #include <sys/types.h>
 #include <stdlib.h>
@@ -40,6 +42,7 @@
 #include "jcc.h"
 #include "miscutil.h"
 #include "errlog.h"
+#include "parsers.h"
 
 struct client_specific_tag
 {
@@ -230,8 +233,8 @@ void get_tag_list_for_client(struct list *tag_list,
       if (enabled_tags->end_of_life && (enabled_tags->end_of_life < now))
       {
          struct client_specific_tag *next_tag = enabled_tags->next;
-         log_error(LOG_LEVEL_INFO,
-            "Tag '%s' for client %s expired %u seconds ago. Deleting it.",
+         log_error(LOG_LEVEL_TAGGING,
+            "Tag '%s' for client %s expired %ld seconds ago. Deleting it.",
             enabled_tags->name, client_address,
             (now - enabled_tags->end_of_life));
          remove_tag_for_client(client_address, enabled_tags->name);
@@ -240,6 +243,8 @@ void get_tag_list_for_client(struct list *tag_list,
       }
       else
       {
+         log_error(LOG_LEVEL_TAGGING, "Enlisting tag '%s' for client %s",
+            enabled_tags->name, client_address);
          enlist(tag_list, enabled_tags->name);
       }
       enabled_tags = enabled_tags->next;
@@ -249,6 +254,84 @@ void get_tag_list_for_client(struct list *tag_list,
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  get_next_tag_timeout_for_client
+ *
+ * Description :  Figures out when the next temporarily enabled tag
+ *                for the client will have timed out.
+ *
+ * Parameters  :
+ *          1  :  client_address = Address of the client
+ *
+ * Returns     :  Lowest timeout in seconds
+ *
+ *********************************************************************/
+time_t get_next_tag_timeout_for_client(const char *client_address)
+{
+   struct client_specific_tag *enabled_tags;
+   time_t next_timeout = 0;
+   const time_t now = time(NULL);
+
+   privoxy_mutex_lock(&client_tags_mutex);
+
+   enabled_tags = get_tags_for_client(client_address);
+   while (enabled_tags != NULL)
+   {
+      log_error(LOG_LEVEL_TAGGING,
+         "Evaluating tag '%s' for client %s. End of life %ld",
+         enabled_tags->name, client_address, enabled_tags->end_of_life);
+      if (enabled_tags->end_of_life)
+      {
+          time_t time_left = enabled_tags->end_of_life - now;
+          /* Add a second to make sure the tag will have expired */
+          time_left++;
+          log_error(LOG_LEVEL_CGI, "%ld > %ld?", next_timeout, time_left);
+          if (next_timeout == 0 || next_timeout > time_left)
+          {
+             next_timeout = time_left;
+          }
+       }
+       enabled_tags = enabled_tags->next;
+   }
+
+   privoxy_mutex_unlock(&client_tags_mutex);
+
+   log_error(LOG_LEVEL_CGI, "Next timeout in %ld seconds", next_timeout);
+
+   return next_timeout;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  create_client_specific_tag
+ *
+ * Description :  Allocates memory for a client specific tag
+ *                and populates it.
+ *
+ * Parameters  :
+ *          1  :  name = The name of the tag to create.
+ *          2  :  time_to_live = 0, or the number of seconds
+ *                               the tag remains activated.
+ *
+ * Returns     :  Pointer to populated tag
+ *
+ *********************************************************************/
+static struct client_specific_tag *create_client_specific_tag(const char *name,
+   const time_t time_to_live)
+{
+   struct client_specific_tag *tag;
+
+   tag = zalloc_or_die(sizeof(struct client_specific_tag));
+   tag->name = strdup_or_die(name);
+   tag->end_of_life = time_to_live ? (time(NULL) + time_to_live) : 0;
+
+   return tag;
+
+}
+
 /*********************************************************************
  *
  * Function    :  add_tag_for_client
@@ -277,10 +360,7 @@ static void add_tag_for_client(const char *client_address,
       /* XXX: Code duplication. */
       requested_tags = zalloc_or_die(sizeof(struct requested_tags));
       requested_tags->client = strdup_or_die(client_address);
-      requested_tags->tags = zalloc_or_die(sizeof(struct client_specific_tag));
-      requested_tags->tags->name = strdup_or_die(tag);
-      requested_tags->tags->end_of_life = time_to_live ?
-         (time(NULL) + time_to_live) : 0;
+      requested_tags->tags = create_client_specific_tag(tag, time_to_live);
 
       validate_requested_tags();
       return;
@@ -303,10 +383,7 @@ static void add_tag_for_client(const char *client_address,
          clients_with_tags->next->prev = clients_with_tags;
          clients_with_tags = clients_with_tags->next;
          clients_with_tags->client = strdup_or_die(client_address);
-         clients_with_tags->tags = zalloc_or_die(sizeof(struct client_specific_tag));
-         clients_with_tags->tags->name = strdup_or_die(tag);
-         clients_with_tags->tags->end_of_life = time_to_live ?
-            (time(NULL) + time_to_live) : 0;
+         clients_with_tags->tags = create_client_specific_tag(tag, time_to_live);
 
          validate_requested_tags();
 
@@ -319,10 +396,7 @@ static void add_tag_for_client(const char *client_address,
    {
       if (enabled_tags->next == NULL)
       {
-         enabled_tags->next = zalloc_or_die(sizeof(struct client_specific_tag));
-         enabled_tags->next->name = strdup_or_die(tag);
-         clients_with_tags->tags->end_of_life = time_to_live ?
-            (time(NULL) + time_to_live) : 0;
+         enabled_tags->next = create_client_specific_tag(tag, time_to_live);
          enabled_tags->next->prev = enabled_tags;
          break;
       }
@@ -363,6 +437,13 @@ static void remove_tag_for_client(const char *client_address, const char *tag)
       clients_with_tags = clients_with_tags->next;
    }
 
+   assert(clients_with_tags != NULL);
+   if (clients_with_tags == NULL)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Tried to remove tag %s for tag-less client %s",
+         tag, client_address);
+   }
    enabled_tags = clients_with_tags->tags;
    while (enabled_tags != NULL)
    {
@@ -395,17 +476,16 @@ static void remove_tag_for_client(const char *client_address, const char *tag)
                /* Client has preceding client */
                clients_with_tags->prev->next = clients_with_tags->next;
             }
-            freez(clients_with_tags->client);
             if (clients_with_tags == requested_tags)
             {
-               /* Removing last tag */
-               freez(requested_tags);
-               clients_with_tags = requested_tags;
-            }
-            else
-            {
-               freez(clients_with_tags);
+               /*
+                * We're in the process of removing the last tag,
+                * mark the global list as empty.
+                */
+               requested_tags = NULL;
             }
+            freez(clients_with_tags->client);
+            freez(clients_with_tags);
          }
          freez(enabled_tags->name);
          freez(enabled_tags);
@@ -482,17 +562,17 @@ jb_err enable_client_specific_tag(struct client_state *csp,
       return JB_ERR_PARSE;
    }
 
-   if (client_has_requested_tag(csp->ip_addr_str, tag_name))
+   if (client_has_requested_tag(csp->client_address, tag_name))
    {
-      log_error(LOG_LEVEL_ERROR,
-         "Tag '%s' already enabled for client '%s'", tag->name, csp->ip_addr_str);
+      log_error(LOG_LEVEL_TAGGING,
+         "Tag '%s' already enabled for client '%s'", tag->name, csp->client_address);
    }
    else
    {
-      add_tag_for_client(csp->ip_addr_str, tag_name, time_to_live);
-      log_error(LOG_LEVEL_INFO,
-         "Tag '%s' enabled for client '%s'. TTL: %d.",
-         tag->name, csp->ip_addr_str, time_to_live);
+      add_tag_for_client(csp->client_address, tag_name, time_to_live);
+      log_error(LOG_LEVEL_TAGGING,
+         "Tag '%s' enabled for client '%s'. TTL: %ld.",
+         tag->name, csp->client_address, time_to_live);
    }
 
    privoxy_mutex_unlock(&client_tags_mutex);
@@ -527,17 +607,17 @@ jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_nam
       return JB_ERR_PARSE;
    }
 
-   if (client_has_requested_tag(csp->ip_addr_str, tag_name))
+   if (client_has_requested_tag(csp->client_address, tag_name))
    {
-      remove_tag_for_client(csp->ip_addr_str, tag_name);
-      log_error(LOG_LEVEL_INFO,
-         "Tag '%s' disabled for client '%s'", tag->name, csp->ip_addr_str);
+      remove_tag_for_client(csp->client_address, tag_name);
+      log_error(LOG_LEVEL_TAGGING,
+         "Tag '%s' disabled for client '%s'", tag->name, csp->client_address);
    }
    else
    {
-      log_error(LOG_LEVEL_ERROR,
+      log_error(LOG_LEVEL_TAGGING,
          "Tag '%s' currently not set for client '%s'",
-         tag->name, csp->ip_addr_str);
+         tag->name, csp->client_address);
    }
 
    privoxy_mutex_unlock(&client_tags_mutex);
@@ -579,6 +659,7 @@ int client_tag_match(const struct pattern_spec *pattern,
    {
       if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
       {
+         log_error(LOG_LEVEL_TAGGING, "Client tag '%s' matches", tag->str);
          return 1;
       }
    }
@@ -586,3 +667,44 @@ int client_tag_match(const struct pattern_spec *pattern,
    return 0;
 
 }
+
+
+/*********************************************************************
+ *
+ * Function    :  set_client_address
+ *
+ * Description :  Sets the client address that will be used to enable,
+ *                disable, or apply client tags.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  headers = Client headers
+ *
+ * Returns     :  void.
+ *
+ *********************************************************************/
+void set_client_address(struct client_state *csp, const struct list *headers)
+{
+   if (csp->config->trust_x_forwarded_for)
+   {
+      const char *client_address;
+
+      client_address = get_header_value(headers, "X-Forwarded-For:");
+      if (client_address != NULL)
+      {
+         csp->client_address = strdup_or_die(client_address);
+         log_error(LOG_LEVEL_HEADER,
+            "Got client address %s from X-Forwarded-For header",
+            csp->client_address);
+      }
+   }
+
+   if (csp->client_address == NULL)
+   {
+      csp->client_address = strdup_or_die(csp->ip_addr_str);
+   }
+}
+
+#else
+#error Compiling client-tags.c without FEATURE_CLIENT_TAGS
+#endif /* def FEATURE_CLIENT_TAGS */