current client-tags.c, NONE, 1.1 client-tags.h, NONE, 1.1

Fabian Keil <[email protected]>
Newsgroups gmane.comp.web.privoxy.cvs
Message-ID <[email protected]>
Update of /cvsroot/ijbswa/current
In directory sfp-cvs-1.v30.ch3.sourceforge.com:/tmp/cvs-serv6400

Added Files:
	client-tags.c client-tags.h 
Log Message:
Implement client-specific tags

... which allow Privoxy admins to pre-define tags that
are set for all requests from clients that previously
opted-in through the CGI interface.

They are useful in multi-user setups where admins may
want to allow users to disable certain actions and filters
for themselves without affecting others.

In single-user setups they are useful to allow more
fine-grained toggling. For example to disable request
blocking while still crunching cookies, or to disable
experimental filters only.

This is an experimental feature, to enable it configure
with --enable-client-tags. The syntax and behaviour may
change in future versions.

Implements TODO list item #144 and #145.
Funded by a donation from Robert Klemme.


--- NEW FILE: client-tags.c ---
/*********************************************************************
 *
 * File        :  $Source: /cvsroot/ijbswa/current/client-tags.c,v $
 *
 * Purpose     :  Functions related to client-specific tags.
 *
 * Copyright   :  Copyright (C) 2016 Fabian Keil <[email protected]>
 *
 *                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;

}

--- NEW FILE: client-tags.h ---
#ifndef CLIENT_TAGS_H_INCLUDED
#define CLIENT_TAGS_H_INCLUDED
#define CLIENT_TAGS_H_VERSION "$Id: client-tags.h,v 1.1 2016/03/17 10:42:27 fabiankeil Exp $"
/*********************************************************************
 *
 * File        :  $Source: /cvsroot/ijbswa/current/client-tags.h,v $
 *
 * Purpose     :  Declares functions for client-specific tags.
 *
 * Copyright   :  Copyright (C) 2016 Fabian Keil <[email protected]>
 *
 *                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


------------------------------------------------------------------------------
Transform Data into Opportunity.
Accelerate data analysis in your applications with
Intel Data Analytics Acceleration Library.
Click to learn more.
http://pubads.g.doubleclick.net/gampad/clk?id=278785231&iu=/4140
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.