# Note: Makefile is built automatically from Makefile.in
#
-# $Id: GNUmakefile.in,v 1.234 2016/02/02 13:08:03 fabiankeil Exp $
+# $Id: GNUmakefile.in,v 1.235 2016/02/13 11:18:02 fabiankeil Exp $
#
# Written by and Copyright (C) 2001-2014 members of the
# Privoxy team. http://www.privoxy.org/
C_OBJS = $(C_SRC:.c=.@OBJEXT@)
C_HDRS = $(C_SRC:.c=.h) project.h actionlist.h
+CLIENT_TAG_SRC = @FEATURE_CLIENT_TAGS_ONLY@client-tags.c
+CLIENT_TAG_OBJS = @FEATURE_CLIENT_TAGS_ONLY@client-tags.@OBJEXT@
+
W32_SRC = @WIN_ONLY@w32log.c w32taskbar.c win32.c w32svrapi.c
W32_FILES = @WIN_ONLY@w32.res
W32_OBJS = @WIN_ONLY@$(W32_SRC:.c=.@OBJEXT@) $(W32_FILES)
# PThreads library, if needed.
PTHREAD_LIB = @PTHREAD_ONLY@@PTHREAD_LIB@
-SRCS = $(C_SRC) $(W32_SRC) $(PCRS_SRC) $(PCRE_SRC) $(REGEX_SRC)
-OBJS = $(C_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(PCRE_OBJS) $(REGEX_OBJS)
+SRCS = $(C_SRC) $(CLIENT_TAG_SRC) $(W32_SRC) $(PCRS_SRC) $(PCRE_SRC) $(REGEX_SRC)
+OBJS = $(C_OBJS) $(CLIENT_TAG_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(PCRE_OBJS) $(REGEX_OBJS)
HDRS = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(PCRE_OBJS) $(REGEX_HDRS)
LIBS = @LIBS@ $(W32_LIB) $(SOCKET_LIB) $(PTHREAD_LIB)
-const char cgi_rcs[] = "$Id: cgi.c,v 1.160 2014/10/18 11:31:52 fabiankeil Exp $";
+const char cgi_rcs[] = "$Id: cgi.c,v 1.161 2016/02/26 12:32:26 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgi.c,v $
cgi_show_version,
"View the source code version numbers",
TRUE },
+#ifdef FEATURE_CLIENT_TAGS
+ { "show-client-tags",
+ cgi_show_client_tags,
+ "Show the tags that can be set based on the client's address (opt-in)",
+ FALSE },
+#endif
{ "show-request",
cgi_show_request,
"View the request headers",
-const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.134 2016/02/26 12:32:09 fabiankeil Exp $";
+const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.135 2016/03/04 13:22:22 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgisimple.c,v $
#include "parsers.h"
#include "urlmatch.h"
#include "errlog.h"
+#ifdef FEATURE_CLIENT_TAGS
+#include "client-tags.h"
+#endif
const char cgisimple_h_rcs[] = CGISIMPLE_H_VERSION;
}
+#ifdef FEATURE_CLIENT_TAGS
+/*********************************************************************
+ *
+ * Function : cgi_show_client_tags
+ *
+ * Description : Shows the tags that can be set based on the client
+ * address (opt-in).
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : rsp = http_response data structure for output
+ * 3 : parameters = map of cgi parameters
+ *
+ * CGI Parameters : none
+ *
+ * Returns : JB_ERR_OK on success
+ * JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_show_client_tags(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters)
+{
+ struct map *exports;
+ struct client_tag_spec *this_tag;
+ jb_err err = JB_ERR_OK;
+ const char *toggled_tag;
+ const char *toggle_state;
+ const char *tag_expires;
+ time_t time_to_live;
+ char *client_tags = strdup_or_die("");
+ char buf[1000];
+
+ assert(csp);
+ assert(rsp);
+ assert(parameters);
+
+ if (NULL == (exports = default_exports(csp, "show-client-tags")))
+ {
+ return JB_ERR_MEMORY;
+ }
+
+ toggled_tag = lookup(parameters, "tag");
+ if (*toggled_tag != '\0')
+ {
+ tag_expires = lookup(parameters, "expires");
+ if (*tag_expires == '0')
+ {
+ time_to_live = 0;
+ }
+ else
+ {
+ time_to_live = csp->config->client_tag_lifetime;
+ }
+ toggle_state = lookup(parameters, "toggle-state");
+ if (*toggle_state == '1')
+ {
+ enable_client_specific_tag(csp, toggled_tag, time_to_live);
+ }
+ else
+ {
+ disable_client_specific_tag(csp, toggled_tag);
+ }
+ }
+
+ this_tag = csp->config->client_tags;
+ if (this_tag->name == NULL)
+ {
+ if (!err) err = string_append(&client_tags, "<p>No tags available.</p>\n");
+ }
+ else
+ {
+ if (!err)
+ {
+ err = string_append(&client_tags, "<table border=\"1\">\n"
+ "<tr><th>Tag name</th>\n"
+ "<th>Current state</th><th>Change state</th><th>Description</th></tr>\n");
+ }
+ while ((this_tag != NULL) && (this_tag->name != NULL))
+ {
+ int tag_state;
+
+ privoxy_mutex_lock(&client_tags_mutex);
+ tag_state = client_has_requested_tag(csp->ip_addr_str, this_tag->name);
+ privoxy_mutex_unlock(&client_tags_mutex);
+ if (!err) err = string_append(&client_tags, "<tr><td>");
+ if (!err) err = string_append(&client_tags, this_tag->name);
+ if (!err) err = string_append(&client_tags, "</td><td>");
+ if (!err) err = string_append(&client_tags, tag_state == 1 ? "Enabled" : "Disabled");
+ snprintf(buf, sizeof(buf),
+ "</td><td><a href=\"/show-client-tags?tag=%s&toggle-state=%d&expires=0\">%s</a>",
+ this_tag->name, !tag_state, tag_state == 1 ? "Disable" : "Enable");
+ if (!err) err = string_append(&client_tags, buf);
+ if (tag_state == 0)
+ {
+ snprintf(buf, sizeof(buf), ". <a href=\"/show-client-tags?"
+ "tag=%s&toggle-state=1&expires=1\">Enable temporarily</a>",
+ this_tag->name);
+ if (!err) err = string_append(&client_tags, buf);
+ }
+ if (!err) err = string_append(&client_tags, "</td><td>");
+ if (!err) err = string_append(&client_tags, this_tag->description);
+ if (!err) err = string_append(&client_tags, "</td></tr>\n");
+ if (err)
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+ this_tag = this_tag->next;
+ }
+ if (!err) err = string_append(&client_tags, "</table>\n");
+ }
+
+ if (map(exports, "client-tags", 1, client_tags, 0))
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ if (map(exports, "client-ip-addr", 1, csp->ip_addr_str, 1))
+ {
+ free_map(exports);
+ return JB_ERR_MEMORY;
+ }
+
+ return template_fill_for_cgi(csp, "show-client-tags", exports, rsp);
+}
+#endif /* def FEATURE_CLIENT_TAGS */
+
+
/*********************************************************************
*
* Function : cgi_send_banner
1,
#else
0,
+#endif
+ },
+ {
+ "FEATURE_CLIENT_TAGS",
+#ifdef FEATURE_CLIENT_TAGS
+ 1,
+#else
+ 0,
#endif
},
{
#ifndef CGISIMPLE_H_INCLUDED
#define CGISIMPLE_H_INCLUDED
-#define CGISIMPLE_H_VERSION "$Id: cgisimple.h,v 1.18 2011/09/04 11:10:56 fabiankeil Exp $"
+#define CGISIMPLE_H_VERSION "$Id: cgisimple.h,v 1.19 2013/11/24 14:23:28 fabiankeil Exp $"
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/cgisimple.h,v $
extern jb_err cgi_show_request (struct client_state *csp,
struct http_response *rsp,
const struct map *parameters);
+#ifdef FEATURE_CLIENT_TAGS
+extern jb_err cgi_show_client_tags(struct client_state *csp,
+ struct http_response *rsp,
+ const struct map *parameters);
+#endif
extern jb_err cgi_transparent_image (struct client_state *csp,
struct http_response *rsp,
const struct map *parameters);
--- /dev/null
+/*********************************************************************
+ *
+ * File : $Source: /cvsroot/ijbswa/current/client-tags.c,v $
+ *
+ * Purpose : Functions related to client-specific tags.
+ *
+ * Copyright : Copyright (C) 2016 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
+ * Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * The GNU General Public License should be included with
+ * this file. If not, you can view it at
+ * http://www.gnu.org/copyleft/gpl.html
+ * or write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ **********************************************************************/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+
+#include "project.h"
+#include "list.h"
+#include "jcc.h"
+#include "miscutil.h"
+#include "errlog.h"
+
+struct client_specific_tag
+{
+ char *name;
+
+ time_t end_of_life;
+
+ struct client_specific_tag *next;
+ struct client_specific_tag *prev;
+};
+
+/**
+ * This struct represents tags that have been requested by clients
+ */
+struct requested_tags
+{
+ char *client; /**< The IP address of the client that requested the tag */
+
+ /**< List of tags the client requested .... */
+ struct client_specific_tag *tags;
+
+ struct requested_tags *next;
+ struct requested_tags *prev;
+};
+
+struct requested_tags *requested_tags;
+static void remove_tag_for_client(const char *client_address, const char *tag);
+
+/*********************************************************************
+ *
+ * Function : validate_tag_list
+ *
+ * Description : Validates the given tag list
+ *
+ * Parameters :
+ * 1 : enabled_tags = The tags to validate
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+static void validate_tag_list(struct client_specific_tag *enabled_tags)
+{
+ while (enabled_tags != NULL)
+ {
+ if (enabled_tags->name == NULL)
+ {
+ assert(enabled_tags->name != NULL);
+ log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Tag without name detected");
+ }
+ if (enabled_tags->next != NULL)
+ {
+ if (enabled_tags->next->prev != enabled_tags)
+ {
+ assert(enabled_tags->next->prev == enabled_tags);
+ log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Invalid backlink detected");
+ }
+ }
+ enabled_tags = enabled_tags->next;
+ }
+}
+
+/*********************************************************************
+ *
+ * Function : validate_requested_tags
+ *
+ * Description : Validates the requested_tags list
+ *
+ * Parameters : N/A
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+static jb_err validate_requested_tags()
+{
+ struct requested_tags *requested_tag;
+
+ for (requested_tag = requested_tags; requested_tag != NULL;
+ requested_tag = requested_tag->next)
+ {
+ if (requested_tag->client == NULL)
+ {
+ assert(requested_tag->client != NULL);
+ log_error(LOG_LEVEL_FATAL, "validate_tag_list(): Client not registered");
+ }
+ validate_tag_list(requested_tag->tags);
+ if (requested_tag->next != NULL)
+ {
+ if (requested_tag->next->prev != requested_tag)
+ {
+ assert(requested_tag->next->prev == requested_tag);
+ log_error(LOG_LEVEL_FATAL, "validate_requested_tags(): Invalid backlink detected");
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_client_specific_tag
+ *
+ * Description : Returns the data for a client-specific-tag specified
+ * by name.
+ *
+ * Parameters :
+ * 1 : tag_list = The tag list to check
+ * 2 : name = The tag name to look up
+ *
+ * Returns : Pointer to tag structure or NULL on error.
+ *
+ *********************************************************************/
+static struct client_tag_spec *get_client_specific_tag(
+ struct client_tag_spec *tag_list, const char *name)
+{
+ struct client_tag_spec *tag;
+
+ for (tag = tag_list; tag != NULL; tag = tag->next)
+ {
+ if (tag->name != NULL && !strcmp(tag->name, name))
+ {
+ return tag;
+ }
+ }
+
+ log_error(LOG_LEVEL_ERROR, "No such tag: '%s'", name);
+
+ return NULL;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_tags_for_client
+ *
+ * Description : Returns the list of tags the client opted-in.
+ *
+ * Parameters :
+ * 1 : client_address = Address of the client
+ *
+ * Returns : Pointer to tag structure or NULL on error.
+ *
+ *********************************************************************/
+static struct client_specific_tag *get_tags_for_client(const char *client_address)
+{
+ struct requested_tags *requested_tag;
+
+ for (requested_tag = requested_tags; requested_tag != NULL;
+ requested_tag = requested_tag->next)
+ {
+ if (!strcmp(requested_tag->client, client_address))
+ {
+ return requested_tag->tags;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*********************************************************************
+ *
+ * Function : get_tag_list_for_client
+ *
+ * Description : Provides a list of tag names the client opted-in.
+ * Other tag attributes are not part of the list.
+ *
+ * Parameters :
+ * 1 : tag_list = The list to fill in.
+ * 2 : client_address = Address of the client
+ *
+ * Returns : Pointer to tag list.
+ *
+ *********************************************************************/
+void get_tag_list_for_client(struct list *tag_list,
+ const char *client_address)
+{
+ struct client_specific_tag *enabled_tags;
+ const time_t now = time(NULL);
+
+ privoxy_mutex_lock(&client_tags_mutex);
+
+ enabled_tags = get_tags_for_client(client_address);
+ while (enabled_tags != NULL)
+ {
+ 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.",
+ enabled_tags->name, client_address,
+ (now - enabled_tags->end_of_life));
+ remove_tag_for_client(client_address, enabled_tags->name);
+ enabled_tags = next_tag;
+ continue;
+ }
+ else
+ {
+ enlist(tag_list, enabled_tags->name);
+ }
+ enabled_tags = enabled_tags->next;
+ }
+
+ privoxy_mutex_unlock(&client_tags_mutex);
+}
+
+
+/*********************************************************************
+ *
+ * Function : add_tag_for_client
+ *
+ * Description : Adds the tag for the client.
+ *
+ * Parameters :
+ * 1 : client_address = Address of the client
+ * 2 : tag = The tag to add.
+ * 3 : time_to_live = 0, or the number of seconds
+ * the tag remains activated.
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+static void add_tag_for_client(const char *client_address,
+ const char *tag, const time_t time_to_live)
+{
+ struct requested_tags *clients_with_tags;
+ struct client_specific_tag *enabled_tags;
+
+ validate_requested_tags();
+
+ if (requested_tags == NULL)
+ {
+ /* 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;
+
+ validate_requested_tags();
+ return;
+ }
+ else
+ {
+ clients_with_tags = requested_tags;
+ while (clients_with_tags->next != NULL)
+ {
+ if (!strcmp(clients_with_tags->client, client_address))
+ {
+ break;
+ }
+ clients_with_tags = clients_with_tags->next;
+ }
+ if (strcmp(clients_with_tags->client, client_address))
+ {
+ /* Client does not have tags yet, add new structure */
+ clients_with_tags->next = zalloc_or_die(sizeof(struct requested_tags));
+ 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;
+
+ validate_requested_tags();
+
+ return;
+ }
+ }
+
+ enabled_tags = clients_with_tags->tags;
+ while (enabled_tags != NULL)
+ {
+ 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->prev = enabled_tags;
+ break;
+ }
+ enabled_tags = enabled_tags->next;
+ }
+
+ validate_requested_tags();
+}
+
+
+/*********************************************************************
+ *
+ * Function : remove_tag_for_client
+ *
+ * Description : Removes the tag for the client.
+ *
+ * Parameters :
+ * 1 : client_address = Address of the client
+ * 2 : tag = The tag to remove.
+ *
+ * Returns : void
+ *
+ *********************************************************************/
+static void remove_tag_for_client(const char *client_address, const char *tag)
+{
+ struct requested_tags *clients_with_tags;
+ struct client_specific_tag *enabled_tags;
+
+ validate_requested_tags();
+
+ clients_with_tags = requested_tags;
+ while (clients_with_tags != NULL && clients_with_tags->client != NULL)
+ {
+ if (!strcmp(clients_with_tags->client, client_address))
+ {
+ break;
+ }
+ clients_with_tags = clients_with_tags->next;
+ }
+
+ enabled_tags = clients_with_tags->tags;
+ while (enabled_tags != NULL)
+ {
+ if (!strcmp(enabled_tags->name, tag))
+ {
+ if (enabled_tags->next != NULL)
+ {
+ enabled_tags->next->prev = enabled_tags->prev;
+ if (enabled_tags == clients_with_tags->tags)
+ {
+ /* Tag is first in line */
+ clients_with_tags->tags = enabled_tags->next;
+ }
+ }
+ if (enabled_tags->prev != NULL)
+ {
+ /* Tag has preceding tag */
+ enabled_tags->prev->next = enabled_tags->next;
+ }
+ if (enabled_tags->prev == NULL && enabled_tags->next == NULL)
+ {
+ /* Tag is the only one */
+ if (clients_with_tags->next != NULL)
+ {
+ /* Client has following client */
+ clients_with_tags->next->prev = clients_with_tags->prev;
+ }
+ if (clients_with_tags->prev != NULL)
+ {
+ /* 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);
+ }
+ }
+ freez(enabled_tags->name);
+ freez(enabled_tags);
+ break;
+ }
+
+ enabled_tags = enabled_tags->next;
+ }
+
+ validate_requested_tags();
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : client_has_requested_tag
+ *
+ * Description : Checks whether or not the given client requested
+ * the tag.
+ *
+ * Parameters :
+ * 1 : client_address = Address of the client
+ * 2 : tag = Tag to check.
+ *
+ * Returns : TRUE or FALSE.
+ *
+ *********************************************************************/
+int client_has_requested_tag(const char *client_address, const char *tag)
+{
+ struct client_specific_tag *enabled_tags;
+
+ enabled_tags = get_tags_for_client(client_address);
+
+ while (enabled_tags != NULL)
+ {
+ if (!strcmp(enabled_tags->name, tag))
+ {
+ return TRUE;
+ }
+ enabled_tags = enabled_tags->next;
+ }
+
+ return FALSE;
+
+}
+
+/*********************************************************************
+ *
+ * Function : enable_client_specific_tag
+ *
+ * Description : Enables a client-specific-tag for the client
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : tag_name = The name of the tag to enable
+ * 3 : time_to_live = If not 0, the number of seconds the
+ * tag should stay enabled.
+ *
+ * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
+ *
+ *********************************************************************/
+jb_err enable_client_specific_tag(struct client_state *csp,
+ const char *tag_name, const time_t time_to_live)
+{
+ struct client_tag_spec *tag;
+
+ privoxy_mutex_lock(&client_tags_mutex);
+
+ tag = get_client_specific_tag(csp->config->client_tags, tag_name);
+ if (tag == NULL)
+ {
+ privoxy_mutex_unlock(&client_tags_mutex);
+ return JB_ERR_PARSE;
+ }
+
+ if (client_has_requested_tag(csp->ip_addr_str, tag_name))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Tag '%s' already enabled for client '%s'", tag->name, csp->ip_addr_str);
+ }
+ 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'", tag->name, csp->ip_addr_str);
+ }
+
+ privoxy_mutex_unlock(&client_tags_mutex);
+
+ return JB_ERR_OK;
+
+}
+
+/*********************************************************************
+ *
+ * Function : disable_client_specific_tag
+ *
+ * Description : Disables a client-specific-tag for the client
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ * 2 : tag_name = The name of the tag to disable
+ *
+ * Returns : JB_ERR_OK on success, JB_ERR_MEMORY or JB_ERR_PARSE.
+ *
+ *********************************************************************/
+jb_err disable_client_specific_tag(struct client_state *csp, const char *tag_name)
+{
+ struct client_tag_spec *tag;
+
+ privoxy_mutex_lock(&client_tags_mutex);
+
+ tag = get_client_specific_tag(csp->config->client_tags, tag_name);
+ if (tag == NULL)
+ {
+ privoxy_mutex_unlock(&client_tags_mutex);
+ return JB_ERR_PARSE;
+ }
+
+ if (client_has_requested_tag(csp->ip_addr_str, 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);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Tag '%s' currently not set for client '%s'",
+ tag->name, csp->ip_addr_str);
+ }
+
+ privoxy_mutex_unlock(&client_tags_mutex);
+ return JB_ERR_OK;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : client_tag_match
+ *
+ * Description : Compare a client tag against a client tag pattern.
+ *
+ * Parameters :
+ * 1 : pattern = a TAG pattern
+ * 2 : tag = Client tag to match
+ *
+ * Returns : Nonzero if the tag matches the pattern, else 0.
+ *
+ *********************************************************************/
+int client_tag_match(const struct pattern_spec *pattern,
+ const struct list *tags)
+{
+ struct list_entry *tag;
+
+ if (!(pattern->flags & PATTERN_SPEC_CLIENT_TAG_PATTERN))
+ {
+ /*
+ * It's not a client pattern and thus shouldn't
+ * be matched against client tags.
+ */
+ return 0;
+ }
+
+ assert(tags);
+
+ for (tag = tags->first; tag != NULL; tag = tag->next)
+ {
+ if (0 == regexec(pattern->pattern.tag_regex, tag->str, 0, NULL, 0))
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+
+}
--- /dev/null
+#ifndef CLIENT_TAGS_H_INCLUDED
+#define CLIENT_TAGS_H_INCLUDED
+#define CLIENT_TAGS_H_VERSION "$Id:$"
+/*********************************************************************
+ *
+ * File : $Source: $
+ *
+ * Purpose : Declares functions for client-specific tags.
+ *
+ * Copyright : Copyright (C) 2016 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
+ * Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * The GNU General Public License should be included with
+ * this file. If not, you can view it at
+ * http://www.gnu.org/copyleft/gpl.html
+ * or write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *********************************************************************/
+
+extern int client_tag_match(const struct pattern_spec *pattern,
+ const struct list *tags);
+extern void get_tag_list_for_client(struct list *tag_list,
+ const char *client_address);
+extern jb_err disable_client_specific_tag(struct client_state *csp,
+ const char *tag_name);
+extern jb_err enable_client_specific_tag(struct client_state *csp,
+ const char *tag_name,
+ const time_t time_to_live);
+extern int client_has_requested_tag(const char *client_address,
+ const char *tag);
+#endif
dnl Process this file with autoconf to produce a configure script.
dnl
-dnl $Id: configure.in,v 1.190 2016/01/16 12:33:16 fabiankeil Exp $
+dnl $Id: configure.in,v 1.191 2016/02/02 13:08:17 fabiankeil Exp $
dnl
dnl Written by and Copyright (C) 2001-2014 the
dnl Privoxy team. http://www.privoxy.org/
dnl AutoConf Initialization
dnl =================================================================
-AC_REVISION($Revision: 1.190 $)
+AC_REVISION($Revision: 1.191 $)
AC_INIT(jcc.c)
if test ! -f config.h.in; then
AC_DEFINE(FEATURE_STRPTIME_SANITY_CHECKS)
fi])
+AC_ARG_ENABLE(client-tags,
+[ --enable-client-tags Enable client-specific tags],
+[if test $enableval = yes; then
+ FEATURE_CLIENT_TAGS_ONLY=""
+ AC_DEFINE(FEATURE_CLIENT_TAGS,1,[Define to enable client-specific tags.])
+ else
+ FEATURE_CLIENT_TAGS_ONLY="#"
+fi])
+AC_SUBST(FEATURE_CLIENT_TAGS_ONLY)
+
dnl pcre/pcrs is needed for CGI anyway, so
dnl the choice is only between static and
dnl dynamic:
-const char filters_rcs[] = "$Id: filters.c,v 1.199 2016/01/16 12:33:35 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.200 2016/02/26 12:29:38 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/filters.c,v $
#include "deanimate.h"
#include "urlmatch.h"
#include "loaders.h"
+#ifdef FEATURE_CLIENT_TAGS
+#include "client-tags.h"
+#endif
#ifdef _WIN32
#include "win32.h"
static filter_function_ptr get_filter_function(const struct client_state *csp);
static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
static jb_err prepare_for_filtering(struct client_state *csp);
+static void apply_url_actions(struct current_action_spec *action,
+ struct http_request *http,
+#ifdef FEATURE_CLIENT_TAGS
+ const struct list *client_tags,
+#endif
+ struct url_actions *b);
#ifdef FEATURE_ACL
#ifdef HAVE_RFC2553
return;
}
+#ifdef FEATURE_CLIENT_TAGS
+ apply_url_actions(csp->action, http, csp->client_tags, b);
+#else
apply_url_actions(csp->action, http, b);
+#endif
}
return;
}
-
/*********************************************************************
*
* Function : apply_url_actions
* Parameters :
* 1 : action = Destination.
* 2 : http = Current URL
- * 3 : b = list of URL actions to apply
+ * 3 : client_tags = list of client tags
+ * 4 : b = list of URL actions to apply
*
* Returns : N/A
*
*********************************************************************/
-void apply_url_actions(struct current_action_spec *action,
- struct http_request *http,
- struct url_actions *b)
+static void apply_url_actions(struct current_action_spec *action,
+ struct http_request *http,
+#ifdef FEATURE_CLIENT_TAGS
+ const struct list *client_tags,
+#endif
+ struct url_actions *b)
{
if (b == NULL)
{
{
merge_current_action(action, b->action);
}
+#ifdef FEATURE_CLIENT_TAGS
+ if (client_tag_match(b->url, client_tags))
+ {
+ merge_current_action(action, b->action);
+ }
+#endif
}
}
#ifndef FILTERS_H_INCLUDED
#define FILTERS_H_INCLUDED
-#define FILTERS_H_VERSION "$Id: filters.h,v 1.45 2013/11/24 14:23:28 fabiankeil Exp $"
+#define FILTERS_H_VERSION "$Id: filters.h,v 1.46 2013/12/24 13:32:51 fabiankeil Exp $"
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/filters.h,v $
*/
extern void get_url_actions(struct client_state *csp,
struct http_request *http);
-extern void apply_url_actions(struct current_action_spec *action,
- struct http_request *http,
- struct url_actions *b);
extern struct re_filterfile_spec *get_filter(const struct client_state *csp,
const char *requested_name,
-const char jcc_rcs[] = "$Id: jcc.c,v 1.440 2016/01/16 12:33:36 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.441 2016/02/26 12:29:38 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.c,v $
#include "cgi.h"
#include "loadcfg.h"
#include "urlmatch.h"
+#ifdef FEATURE_CLIENT_TAGS
+#include "client-tags.h"
+#endif
const char jcc_h_rcs[] = JCC_H_VERSION;
const char project_h_rcs[] = PROJECT_H_VERSION;
#ifdef FEATURE_EXTERNAL_FILTERS
privoxy_mutex_t external_filter_mutex;
#endif
+#ifdef FEATURE_CLIENT_TAGS
+privoxy_mutex_t client_tags_mutex;
+#endif
#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
privoxy_mutex_t resolver_mutex;
http = csp->http;
+#if FEATURE_CLIENT_TAGS
+ get_tag_list_for_client(csp->client_tags, csp->ip_addr_str);
+#endif
if (receive_client_request(csp) != JB_ERR_OK)
{
return;
free_http_request(csp->http);
destroy_list(csp->headers);
destroy_list(csp->tags);
+#ifdef FEATURE_CLIENT_TAGS
+ destroy_list(csp->client_tags);
+#endif
free_current_action(csp->action);
if (NULL != csp->fwd)
{
#ifdef FEATURE_EXTERNAL_FILTERS
privoxy_mutex_init(&external_filter_mutex);
#endif
+#ifdef FEATURE_CLIENT_TAGS
+ privoxy_mutex_init(&client_tags_mutex);
+#endif
/*
* XXX: The assumptions below are a bit naive
#ifndef JCC_H_INCLUDED
#define JCC_H_INCLUDED
-#define JCC_H_VERSION "$Id: jcc.h,v 1.34 2014/06/02 06:19:06 fabiankeil Exp $"
+#define JCC_H_VERSION "$Id: jcc.h,v 1.35 2014/06/02 06:22:21 fabiankeil Exp $"
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/jcc.h,v $
extern privoxy_mutex_t external_filter_mutex;
#endif
+#ifdef FEATURE_CLIENT_TAGS
+extern privoxy_mutex_t client_tags_mutex;
+#endif
+
#ifndef HAVE_GMTIME_R
extern privoxy_mutex_t gmtime_mutex;
#endif /* ndef HAVE_GMTIME_R */
-const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.146 2016/02/26 12:29:38 fabiankeil Exp $";
+const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.147 2016/02/26 12:30:59 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
#define hash_allow_cgi_request_crunching 258915987U /* "allow-cgi-request-crunching" */
#define hash_buffer_limit 1881726070U /* "buffer-limit */
#define hash_client_header_order 2701453514U /* "client-header-order" */
+#define hash_client_specific_tag 3353703383U /* "client-specific-tag" */
+#define hash_client_tag_lifetime 647957580U /* "client-tag-lifetime" */
#define hash_compression_level 2464423563U /* "compression-level" */
#define hash_confdir 1978389U /* "confdir" */
#define hash_connection_sharing 1348841265U /* "connection-sharing" */
static void savearg(char *command, char *argument, struct configuration_spec * config);
+#ifdef FEATURE_CLIENT_TAGS
+static void free_client_specific_tags(struct client_tag_spec *tag_list);
+#endif
/*********************************************************************
*
list_remove_all(config->trust_info);
#endif /* def FEATURE_TRUST */
+#ifdef FEATURE_CLIENT_TAGS
+ free_client_specific_tags(config->client_tags);
+#endif
+
freez(config);
}
#endif
+#ifdef FEATURE_CLIENT_TAGS
+/*********************************************************************
+ *
+ * Function : register_tag
+ *
+ * Description : Registers a client-specific-tag and its description
+ *
+ * Parameters :
+ * 1 : config: The tag list
+ * 2 : name: The name of the client-specific-tag
+ * 3 : description: The human-readable description for the tag
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+static void register_tag(struct client_tag_spec *tag_list,
+ const char *name, const char *description)
+{
+ struct client_tag_spec *new_tag;
+ struct client_tag_spec *last_tag;
+
+ last_tag = tag_list;
+ while (last_tag->next != NULL)
+ {
+ last_tag = last_tag->next;
+ }
+ if (last_tag->name == NULL)
+ {
+ /* First entry */
+ new_tag = last_tag;
+ }
+ else
+ {
+ new_tag = zalloc_or_die(sizeof(struct client_tag_spec));
+ }
+ new_tag->name = strdup_or_die(name);
+ new_tag->description = strdup_or_die(description);
+ if (new_tag != last_tag)
+ {
+ last_tag->next = new_tag;
+ }
+}
+
+
+/*********************************************************************
+ *
+ * Function : free_client_specific_tags
+ *
+ * Description : Frees client-specific tags and their descriptions
+ *
+ * Parameters :
+ * 1 : tag_list: The tag list to free
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+static void free_client_specific_tags(struct client_tag_spec *tag_list)
+{
+ struct client_tag_spec *this_tag;
+ struct client_tag_spec *next_tag;
+
+ next_tag = tag_list;
+ do
+ {
+ this_tag = next_tag;
+ next_tag = next_tag->next;
+
+ freez(this_tag->name);
+ freez(this_tag->description);
+
+ if (this_tag != tag_list)
+ {
+ freez(this_tag);
+ }
+ } while (next_tag != NULL);
+}
+#endif /* def FEATURE_CLIENT_TAGS */
+
+
/*********************************************************************
*
* Function : parse_numeric_value
parse_client_header_order(config->ordered_client_headers, arg);
break;
+/* *************************************************************************
+ * client-specific-tag tag-name description
+ * *************************************************************************/
+#ifdef FEATURE_CLIENT_TAGS
+ case hash_client_specific_tag:
+ {
+ char *name;
+ char *description;
+
+ name = arg;
+ description = strstr(arg, " ");
+ if (description == NULL)
+ {
+ log_error(LOG_LEVEL_FATAL,
+ "client-specific-tag '%s' lacks a description.", name);
+ }
+ *description = '\0';
+ description++;
+ register_tag(config->client_tags, name, description);
+ }
+ break;
+#endif /* def FEATURE_CLIENT_TAGS */
+
+/* *************************************************************************
+ * client-tag-lifetime ttl
+ * *************************************************************************/
+#ifdef FEATURE_CLIENT_TAGS
+ case hash_client_tag_lifetime:
+ {
+ int ttl = parse_numeric_value(cmd, arg);
+ if (0 <= ttl)
+ {
+ config->client_tag_lifetime = (unsigned)ttl;
+ }
+ else
+ {
+ log_error(LOG_LEVEL_FATAL,
+ "client-tag-lifetime can't be negative.");
+ }
+ break;
+ }
+#endif /* def FEATURE_CLIENT_TAGS */
+
/* *************************************************************************
* confdir directory-name
* *************************************************************************/
#ifndef PROJECT_H_INCLUDED
#define PROJECT_H_INCLUDED
/** Version string. */
-#define PROJECT_H_VERSION "$Id: project.h,v 1.211 2016/01/16 12:30:28 fabiankeil Exp $"
+#define PROJECT_H_VERSION "$Id: project.h,v 1.212 2016/01/16 12:30:43 fabiankeil Exp $"
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/project.h,v $
/** Pattern spec bitmap: It's a NO-RESPONSE-TAG pattern. */
#define PATTERN_SPEC_NO_RESPONSE_TAG_PATTERN 0x00000008UL
+/** Pattern spec bitmap: It's a CLIENT-TAG pattern. */
+#define PATTERN_SPEC_CLIENT_TAG_PATTERN 0x00000010UL
+
/**
* An I/O buffer. Holds a string which can be appended to, and can have data
* removed from the beginning.
/** List of all tags that apply to this request */
struct list tags[1];
+#ifdef FEATURE_CLIENT_TAGS
+ /** List of all tags that apply to this client (assigned based on address) */
+ struct list client_tags[1];
+#endif
+
/** MIME-Type key, see CT_* above */
unsigned int content_type;
/** Maximum number of loaders (actions, re_filter, ...) */
#define NLOADERS 8
+/**
+ * This struct represents a client-spcific-tag and it's description
+ */
+struct client_tag_spec
+{
+ char *name; /**< Name from "client-specific-tag bla" directive */
+ char *description; /**< Description from "client-specific-tag-description " directive */
+ struct client_tag_spec *next; /**< The pointer for chaining. */
+};
/** configuration_spec::feature_flags: CGI actions editor. */
#define RUNTIME_FEATURE_CGI_EDIT_ACTIONS 1U
#endif /* def FEATURE_TRUST */
+#ifdef FEATURE_CLIENT_TAGS
+ struct client_tag_spec client_tags[1];
+
+ /* Maximum number of seconds a temporarily enabled tag stays enabled. */
+ unsigned int client_tag_lifetime;
+#endif /* def FEATURE_CLIENT_TAGS */
+
#ifdef FEATURE_ACL
/** The access control list (ACL). */
--- /dev/null
+##########################################################
+#
+# Show-Request-CGI Output template for Privoxy.
+#
+#
+# USING HTML TEMPLATES:
+# ---------------------
+#
+# Template files are written win plain HTML, with a few
+# additions:
+#
+# - Lines that start with a '#' character like this one
+# are ignored
+#
+# - Each item in the below list of exported symbols will
+# be replaced by dynamically generated text, if they
+# are enclosed in '@'-characters. E.g. The string @version@
+# will be replaced by the version number of Privoxy.
+#
+# - One special application of this is to make whole blocks
+# of the HTML template disappear if the condition <name>
+# is not given. Simply enclose the block between the two
+# strings @if-<name>start and if-<name>-end@. The strings
+# should be placed in HTML comments (<!-- -->), so the
+# html structure won't be messed when the magic happens.
+#
+# USABLE SYMBOLS IN THIS TEMPLATE:
+# --------------------------------
+#
+# my-ip-addr:
+# The IP-address that the client used to reach this proxy
+# my-hostname:
+# The hostname associated with my-ip-addr
+# admin-address:
+# The email address of the pxoxy's administrator, as configured
+# in the config file
+# default-cgi:
+# The URL for the "main menu" builtin CGI of this proxy
+# menu:
+# List of <li> elements linking to the other available CGIs
+# version:
+# The version number of the proxy software
+# code-status:
+# The development status of the proxy software: "alpha", "beta",
+# or "stable".
+# homepage:
+# The URL of the SourceForge ijbswa project, who maintains this
+# software.
+# client-request:
+# The request and headers that the client sent.
+# processed-request:
+# What we would have rewritten this request to, if this had not
+# been intercepted.
+#
+# CONDITIONAL SYMBOLS FOR THIS TEMPLATE AND THEIR DEPANDANT SYMBOLS:
+# ------------------------------------------------------------------
+#
+# unstable:
+# this is an alpha or beta release of the proxy software
+# have-adminaddr-info:
+# An e-mail address for the local Privoxy administrator has
+# been specified and is available through the "admin-address"
+# symbol
+# have-proxy-info:
+# A URL for online documentation about this proxy has been
+# specified and is available through the "proxy-info-url"
+# symbol
+# have-help-info:
+# If either have-proxy-info is true or have-adminaddr-info is
+# true, have-help-info is true. Used to conditionally include
+# a grey box for any and all help info.
+#
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+
+<head>
+ <title>Privoxy@@my-hostname@</title>
+ <meta http-equiv="Content-Style-Type" content="text/css">
+ <meta http-equiv="Content-Script-Type" content="text/javascript">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="robots" content="noindex,nofollow">
+ <link rel="stylesheet" type="text/css" href="@default-cgi@send-stylesheet">
+ <link rel="shortcut icon" href="@default-cgi@favicon.ico" type="image/x-icon">
+</head>
+
+<body>
+
+ <table cellpadding="20" cellspacing="10" border="0" width="100%">
+ <tr>
+ <td class="title">
+
+#include mod-title
+
+ </td>
+ </tr>
+
+<!-- @if-unstable-start -->
+# This will only appear if CODE_STATUS is "alpha" or "beta". See configure.in
+ <tr>
+ <td class="warning">
+
+#include mod-unstable-warning
+
+ </td>
+ </tr>
+<!-- if-unstable-end@ -->
+
+ <tr>
+ <td class="box">
+ <h2>Show client tags</h2>
+ <p>
+ Here you see the client tags that are or can be optionally set based for
+ client with address @client-ip-addr@:
+ </p>
+
+ @client-tags@
+
+ </td>
+ </tr>
+
+ <tr>
+ <td class="box">
+ <h2>More Privoxy:</h2>
+ <ul>@menu@<li><a href="@user-manual@">Documentation</a></li></ul>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="info">
+
+#include mod-support-and-service
+
+ </td>
+ </tr>
+
+<!-- @if-have-help-info-start -->
+ <tr>
+ <td class="info">
+
+#include mod-local-help
+
+ </td>
+ </tr>
+<!-- if-have-help-info-end@ -->
+
+ </table>
+
+</body>
+</html>
@endif-FEATURE_CGI_EDIT_ACTIONS@web-based actions file
editor@if-FEATURE_CGI_EDIT_ACTIONS-then@</a>@else-not-FEATURE_CGI_EDIT_ACTIONS@@endif-FEATURE_CGI_EDIT_ACTIONS@.</td>
</tr>
+ <tr>
+ <td><code>FEATURE_CLIENT_TAGS</code></td>
+ <td>@if-FEATURE_CLIENT_TAGS-then@ Yes @else-not-FEATURE_CLIENT_TAGS@ No @endif-FEATURE_CLIENT_TAGS@</td>
+ <td>
+ Allows clients to request to be tagged.
+ </td>
+ </tr>
<tr>
<td><code>FEATURE_COMPRESSION</code></td>
<td>@if-FEATURE_COMPRESSION-then@ Yes @else-not-FEATURE_COMPRESSION@ No @endif-FEATURE_COMPRESSION@</td>
-const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.86 2015/12/27 12:47:17 fabiankeil Exp $";
+const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.87 2016/02/26 12:29:39 fabiankeil Exp $";
/*********************************************************************
*
* File : $Source: /cvsroot/ijbswa/current/urlmatch.c,v $
const unsigned flag;
} tag_pattern[] = {
{ "TAG:", 4, PATTERN_SPEC_TAG_PATTERN},
+ #ifdef FEATURE_CLIENT_TAGS
+ { "CLIENT-TAG:", 11, PATTERN_SPEC_CLIENT_TAG_PATTERN},
+ #endif
{ "NO-REQUEST-TAG:", 15, PATTERN_SPEC_NO_REQUEST_TAG_PATTERN},
{ "NO-RESPONSE-TAG:", 16, PATTERN_SPEC_NO_RESPONSE_TAG_PATTERN}
};