- Adding Doxygen-style comments to structures and #defines.
[privoxy.git] / cgiedit.c
index 85f2586..348feb0 100644 (file)
--- a/cgiedit.c
+++ b/cgiedit.c
@@ -1,4 +1,4 @@
-const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.21 2002/03/22 18:02:48 jongfoster Exp $";
+const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.38 2002/05/03 23:00:38 jongfoster Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
@@ -16,7 +16,7 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.21 2002/03/22 18:02:48 jongfoster
  *                Stick to the short names in this file for consistency.
  *
  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
- *                Privoxy team.  http://ijbswa.sourceforge.net
+ *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
  *                by and Copyright (C) 1997 Anonymous Coders and
@@ -42,6 +42,71 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.21 2002/03/22 18:02:48 jongfoster
  *
  * Revisions   :
  *    $Log: cgiedit.c,v $
+ *    Revision 1.38  2002/05/03 23:00:38  jongfoster
+ *    Support for templates for "standard actions" buttons.
+ *    See bug #549871
+ *
+ *    Revision 1.37  2002/04/30 11:14:52  oes
+ *    Made csp the first parameter in *action_to_html
+ *
+ *    Revision 1.36  2002/04/26 21:53:30  jongfoster
+ *    Fixing a memory leak.  (Near, but not caused by, my earlier commit).
+ *
+ *    Revision 1.35  2002/04/26 21:50:02  jongfoster
+ *    Honouring default exports in edit-actions-for-url-filter template.
+ *
+ *    Revision 1.34  2002/04/26 12:54:17  oes
+ *    Adaptions to changes in actions.c
+ *
+ *    Revision 1.33  2002/04/24 02:17:47  oes
+ *     - Moved get_char_param, get_string_param and get_number_param to cgi.c
+ *     - Comments
+ *     - Activated Jon's code for editing multiple AFs
+ *     - cgi_edit_list_actions now provides context-sensitive
+ *       help, looks up all action sets from standard.action and
+ *       makes buttons for them in the catchall section
+ *     - cgi_edit_action_submit now honors a p parameter, looks up
+ *       the corresponding action set, and sets the catchall pattern's
+ *       actions accordingly.
+ *
+ *    Revision 1.32  2002/04/19 16:55:31  jongfoster
+ *    Fixing newline problems.  If we do our own text file newline
+ *    mangling, we don't want the library to do any, so we need to
+ *    open the files in *binary* mode.
+ *
+ *    Revision 1.31  2002/04/18 19:21:08  jongfoster
+ *    Added code to detect "conventional" action files, that start
+ *    with a set of actions for all URLs (the pattern "/").
+ *    These are special-cased in the "edit-actions-list" CGI, so
+ *    that a special UI can be written for them.
+ *
+ *    Revision 1.30  2002/04/10 13:38:35  oes
+ *    load_template signature changed
+ *
+ *    Revision 1.29  2002/04/08 16:59:08  oes
+ *    Fixed comment
+ *
+ *    Revision 1.28  2002/03/27 12:30:29  oes
+ *    Deleted unsused variable
+ *
+ *    Revision 1.27  2002/03/26 23:06:04  jongfoster
+ *    Removing duplicate @ifs on the toggle page
+ *
+ *    Revision 1.26  2002/03/26 22:59:17  jongfoster
+ *    Fixing /toggle to display status consistently.
+ *
+ *    Revision 1.25  2002/03/26 22:29:54  swa
+ *    we have a new homepage!
+ *
+ *    Revision 1.24  2002/03/24 15:23:33  jongfoster
+ *    Name changes
+ *
+ *    Revision 1.23  2002/03/24 13:32:41  swa
+ *    name change related issues
+ *
+ *    Revision 1.22  2002/03/24 13:25:43  swa
+ *    name change related issues
+ *
  *    Revision 1.21  2002/03/22 18:02:48  jongfoster
  *    Fixing remote toggle
  *
@@ -187,7 +252,6 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.21 2002/03/22 18:02:48 jongfoster
 #include <ctype.h>
 #include <string.h>
 #include <assert.h>
-#include <limits.h>
 #include <sys/stat.h>
 
 #ifdef _WIN32
@@ -213,23 +277,49 @@ const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
 
 #ifdef FEATURE_CGI_EDIT_ACTIONS
 
+/**
+ * A line in an editable_file.
+ */
 struct file_line
 {
+   /** Next entry in the linked list */
    struct file_line * next;
+   
+   /** The raw data, to write out if this line is unmodified. */
    char * raw;
+   
+   /** Comments and/or whitespace to put before this line if it's modified
+       and then written out. */
    char * prefix;
+
+   /** The actual data, as a string.  Line continuation and comment removal
+       are performed on the data read from file before it's stored here, so
+       it will be a single line of data.  */
    char * unprocessed;
+   
+   /** The type of data on this line.  One of the FILE_LINE_xxx constants. */
    int type;
 
+   /** The actual data, processed into some sensible data type. */
    union
    {
+
+      /** An action specification. */
       struct action_spec action[1];
 
+      /** A name=value pair. */
       struct
       {
+
+         /** The name in the name=value pair. */
          char * name;
+
+         /** The value in the name=value pair, as a string. */
          char * svalue;
+
+         /** The value in the name=value pair, as an integer. */
          int ivalue;
+
       } setting;
 
       /* Add more data types here... e.g.
@@ -246,46 +336,67 @@ struct file_line
       */
 
    } data;
+
 };
 
+/** This file_line has not been processed yet. */
 #define FILE_LINE_UNPROCESSED           1
+
+/** This file_line is blank. Can only appear at the end of a file, due to
+    the way the parser works. */
 #define FILE_LINE_BLANK                 2
+
+/** This file_line says {{alias}}. */
 #define FILE_LINE_ALIAS_HEADER          3
+
+/** This file_line defines an alias. */
 #define FILE_LINE_ALIAS_ENTRY           4
+
+/** This file_line defines an {action}. */
 #define FILE_LINE_ACTION                5
+
+/** This file_line specifies a URL pattern. */
 #define FILE_LINE_URL                   6
+
+/** This file_line says {{settings}}. */
 #define FILE_LINE_SETTINGS_HEADER       7
+
+/** This file_line is in a {{settings}} block. */
 #define FILE_LINE_SETTINGS_ENTRY        8
+
+/** This file_line says {{description}}. */
 #define FILE_LINE_DESCRIPTION_HEADER    9
+
+/** This file_line is in a {{description}} block. */
 #define FILE_LINE_DESCRIPTION_ENTRY    10
 
 
+/**
+ * A configuration file, in a format that can be edited and written back to
+ * disk.
+ */
 struct editable_file
 {
-   struct file_line * lines;
-   const char * filename;     /* Full pathname - e.g. "/etc/junkbuster/wibble.action" */
-   const char * identifier;   /* Filename stub - e.g. "wibble".  Use for CGI param. */
-                              /* Pre-encoded with url_encode() for ease of use. */
-   const char * version_str;  /* Last modification time, as a string.  For CGI param */
-                              /* Can be used in URL without using url_param(). */
-   unsigned version;          /* Last modification time - prevents chaos with
-                               * the browser's "back" button.  Note that this is a
-                               * time_t cast to an unsigned.  When comparing, always
-                               * cast the time_t to an unsigned, and *NOT* vice-versa.
-                               * This may lose the top few bits, but they're not
-                               * significant anyway.
-                               */
-   int newline;               /* Newline convention - one of the NEWLINE_xxx constants.
-                               * Note that changing this after the file has been
-                               * read in will cause a mess.
-                               */
-   struct file_line * parse_error; /* On parse error, this is the offending line. */
-   const char * parse_error_text;  /* On parse error, this is the problem.
-                                    * (Statically allocated) */
+   struct file_line * lines;  /**< The contents of the file.  A linked list of lines. */
+   const char * filename;     /**< Full pathname - e.g. "/etc/privoxy/wibble.action". */
+   const char * identifier;   /**< Filename stub - e.g. "wibble".  Use for CGI param. */
+                              /**< Pre-encoded with url_encode() for ease of use. */
+   const char * version_str;  /**< Last modification time, as a string.  For CGI param. */
+                              /**< Can be used in URL without using url_param(). */
+   unsigned version;          /**< Last modification time - prevents chaos with
+                                   the browser's "back" button.  Note that this is a
+                                   time_t cast to an unsigned.  When comparing, always
+                                   cast the time_t to an unsigned, and *NOT* vice-versa.
+                                   This may lose the top few bits, but they're not
+                                   significant anyway. */
+   int newline;               /**< Newline convention - one of the NEWLINE_xxx constants.
+                                   Note that changing this after the file has been
+                                   read in will cause a mess. */
+   struct file_line * parse_error; /**< On parse error, this is the offending line. */
+   const char * parse_error_text;  /**< On parse error, this is the problem.
+                                        (Statically allocated) */
 };
 
-#define CGI_ACTION_PARAM_LEN_MAX 500
-
 /* FIXME: Following non-static functions should be prototyped in .h or made static */
 
 /* Functions to read and write arbitrary config files */
@@ -333,17 +444,12 @@ static jb_err get_file_name_param(struct client_state *csp,
                                   const char *suffix,
                                   char **pfilename,
                                   const char **pparam);
-static jb_err get_number_param(struct client_state *csp,
-                               const struct map *parameters,
-                               char *name,
-                               unsigned *pvalue);
+
 static jb_err get_url_spec_param(struct client_state *csp,
                                  const struct map *parameters,
                                  const char *name,
                                  char **pvalue);
-static jb_err get_string_param(const struct map *parameters,
-                               const char *param_name,
-                               const char **pparam);
+
 
 /* Internal actionsfile <==> HTML conversion functions */
 static jb_err map_radio(struct map * exports,
@@ -416,7 +522,7 @@ static jb_err map_copy_parameter_html(struct map *out,
 #if 0 /* unused function */
 /*********************************************************************
  *
- * Function    :  map_copy_parameter_html
+ * Function    :  map_copy_parameter_url
  *
  * Description :  Copy a CGI parameter from one map to another, URL
  *                encoding it.
@@ -719,8 +825,7 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
  * Description :  Write a complete file to disk.
  *
  * Parameters  :
- *          1  :  filename = File to write to.
- *          2  :  file = Data structure to write.
+ *          1  :  file = File to write.
  *
  * Returns     :  JB_ERR_OK     on success
  *                JB_ERR_FILE   on error writing to file.
@@ -740,11 +845,7 @@ jb_err edit_write_file(struct editable_file * file)
    assert(file);
    assert(file->filename);
 
-#if defined(AMIGA) || defined(__OS2__)
-   if (NULL == (fp = fopen(file->filename, "w")))
-#else
-   if (NULL == (fp = fopen(file->filename, "wt")))
-#endif /* def AMIGA */
+   if (NULL == (fp = fopen(file->filename, "wb")))
    {
       return JB_ERR_FILE;
    }
@@ -772,9 +873,6 @@ jb_err edit_write_file(struct editable_file * file)
          }
          if (cur_line->unprocessed)
          {
-            /* This should be a single line - sanity check. */
-            assert(NULL == strchr(cur_line->unprocessed, '\r'));
-            assert(NULL == strchr(cur_line->unprocessed, '\n'));
 
             if (NULL != strchr(cur_line->unprocessed, '#'))
             {
@@ -913,7 +1011,7 @@ void edit_free_file(struct editable_file * file)
 
 /*********************************************************************
  *
- * Function    :  edit_free_file
+ * Function    :  edit_free_file_lines
  *
  * Description :  Free an entire linked list of file lines.
  *
@@ -1035,8 +1133,8 @@ static int match_actions_file_header_line(const char * line, const char * name)
  * Parameters  :
  *          1  :  line = String from file.  Must not start with
  *                       whitespace (else infinite loop!)
- *          2  :  name = Destination for name
- *          2  :  name = Destination for value
+ *          2  :  pname = Destination for name
+ *          2  :  pvalue = Destination for value
  *
  * Returns     :  JB_ERR_OK     on success
  *                JB_ERR_MEMORY on out-of-memory
@@ -1414,6 +1512,7 @@ jb_err edit_parse_actions_file(struct editable_file * file)
  *                     at EOF but it will not have been closed.
  *          2  :  pfile = Destination for a linked list of file_lines.
  *                        Will be set to NULL on error.
+ *          3  :  newline = How to handle newlines.
  *
  * Returns     :  JB_ERR_OK     on success
  *                JB_ERR_MEMORY on out-of-memory
@@ -1571,11 +1670,7 @@ jb_err edit_read_file(struct client_state *csp,
       }
    }
 
-#if defined(AMIGA) || defined(__OS2__)
-   if (NULL == (fp = fopen(filename,"r")))
-#else
-   if (NULL == (fp = fopen(filename,"rt")))
-#endif /* def AMIGA */
+   if (NULL == (fp = fopen(filename,"rb")))
    {
       free(filename);
       return JB_ERR_FILE;
@@ -1774,9 +1869,7 @@ static jb_err get_file_name_param(struct client_state *csp,
 {
    const char *param;
    const char *s;
-#if 0 /* Patch to make 3.0.0 work properly. */
    char *name;
-#endif /* 0 - Patch to make 3.0.0 work properly. */
    char *fullpath;
    char ch;
    int len;
@@ -1820,13 +1913,6 @@ static jb_err get_file_name_param(struct client_state *csp,
       }
    }
 
-   /*
-    * FIXME Following is a hack to make 3.0.0 work properly.
-    * Change "#if 0" --> "#if 1" below when we have modular action
-    * files.
-    *    -- Jon
-    */
-#if 0 /* Patch to make 3.0.0 work properly. */
    /* Append extension */
    name = malloc(len + strlen(suffix) + 1);
    if (name == NULL)
@@ -1839,16 +1925,7 @@ static jb_err get_file_name_param(struct client_state *csp,
    /* Prepend path */
    fullpath = make_path(csp->config->confdir, name);
    free(name);
-#else /* 1 - Patch to make 3.0.0 work properly. */
-   if ((csp->actions_list == NULL)
-    || (csp->actions_list->filename == NULL))
-   {
-      return JB_ERR_CGI_PARAMS;
-   }
 
-   fullpath = ( (csp->actions_list && csp->actions_list->filename)
-             ? strdup(csp->actions_list->filename) : NULL);
-#endif /* 1 - Patch to make 3.0.0 work properly. */
    if (fullpath == NULL)
    {
       return JB_ERR_MEMORY;
@@ -1861,188 +1938,6 @@ static jb_err get_file_name_param(struct client_state *csp,
 }
 
 
-/*********************************************************************
- *
- * Function    :  get_char_param
- *
- * Description :  Get a single-character parameter passed to a CGI
- *                function.
- *
- * Parameters  :
- *          1  :  parameters = map of cgi parameters
- *          2  :  param_name = The name of the parameter to read
- *
- * Returns     :  Uppercase character on success, '\0' on error.
- *
- *********************************************************************/
-static char get_char_param(const struct map *parameters,
-                           const char *param_name)
-{
-   char ch;
-
-   assert(parameters);
-   assert(param_name);
-
-   ch = *(lookup(parameters, param_name));
-   if ((ch >= 'a') && (ch <= 'z'))
-   {
-      ch = ch - 'a' + 'A';
-   }
-
-   return ch;
-}
-
-
-/*********************************************************************
- *
- * Function    :  get_string_param
- *
- * Description :  Get a string paramater, to be used as an
- *                ACTION_STRING or ACTION_MULTI paramater.
- *                Validates the input to prevent stupid/malicious
- *                users from corrupting their action file.
- *
- * Parameters  :
- *          1  :  parameters = map of cgi parameters
- *          2  :  param_name = The name of the parameter to read
- *          3  :  pparam = destination for paramater.  Allocated as
- *                part of the map "parameters", so don't free it.
- *                Set to NULL if not specified.
- *
- * Returns     :  JB_ERR_OK         on success, or if the paramater
- *                                  was not specified.
- *                JB_ERR_MEMORY     on out-of-memory.
- *                JB_ERR_CGI_PARAMS if the paramater is not valid.
- *
- *********************************************************************/
-static jb_err get_string_param(const struct map *parameters,
-                               const char *param_name,
-                               const char **pparam)
-{
-   const char *param;
-   const char *s;
-   char ch;
-
-   assert(parameters);
-   assert(param_name);
-   assert(pparam);
-
-   *pparam = NULL;
-
-   param = lookup(parameters, param_name);
-   if (!*param)
-   {
-      return JB_ERR_OK;
-   }
-
-   if (strlen(param) >= CGI_ACTION_PARAM_LEN_MAX)
-   {
-      /*
-       * Too long.
-       *
-       * Note that the length limit is arbitrary, it just seems
-       * sensible to limit it to *something*.  There's no
-       * technical reason for any limit at all.
-       */
-      return JB_ERR_CGI_PARAMS;
-   }
-
-   /* Check every character to see if it's legal */
-   s = param;
-   while ((ch = *s++) != '\0')
-   {
-      if ( ((unsigned char)ch < (unsigned char)' ')
-        || (ch == '}') )
-      {
-         /* Probable hack attempt, or user accidentally used '}'. */
-         return JB_ERR_CGI_PARAMS;
-      }
-   }
-
-   /* Success */
-   *pparam = param;
-
-   return JB_ERR_OK;
-}
-
-
-/*********************************************************************
- *
- * Function    :  get_number_param
- *
- * Description :  Get a non-negative integer from the parameters
- *                passed to a CGI function.
- *
- * Parameters  :
- *          1  :  csp = Current client state (buffers, headers, etc...)
- *          2  :  parameters = map of cgi parameters
- *          3  :  name = Name of CGI parameter to read
- *          4  :  pvalue = destination for value.
- *                         Set to -1 on error.
- *
- * Returns     :  JB_ERR_OK         on success
- *                JB_ERR_MEMORY     on out-of-memory
- *                JB_ERR_CGI_PARAMS if the parameter was not specified
- *                                  or is not valid.
- *
- *********************************************************************/
-static jb_err get_number_param(struct client_state *csp,
-                               const struct map *parameters,
-                               char *name,
-                               unsigned *pvalue)
-{
-   const char *param;
-   char ch;
-   unsigned value;
-
-   assert(csp);
-   assert(parameters);
-   assert(name);
-   assert(pvalue);
-
-   *pvalue = 0; 
-
-   param = lookup(parameters, name);
-   if (!*param)
-   {
-      return JB_ERR_CGI_PARAMS;
-   }
-
-   /* We don't use atoi because I want to check this carefully... */
-
-   value = 0;
-   while ((ch = *param++) != '\0')
-   {
-      if ((ch < '0') || (ch > '9'))
-      {
-         return JB_ERR_CGI_PARAMS;
-      }
-
-      ch -= '0';
-
-      /* Note:
-       *
-       * <limits.h> defines UINT_MAX
-       *
-       * (UINT_MAX - ch) / 10 is the largest number that
-       *     can be safely multiplied by 10 then have ch added.
-       */
-      if (value > ((UINT_MAX - (unsigned)ch) / 10U))
-      {
-         return JB_ERR_CGI_PARAMS;
-      }
-
-      value = value * 10 + ch;
-   }
-
-   /* Success */
-   *pvalue = value;
-
-   return JB_ERR_OK;
-
-}
-
-
 /*********************************************************************
  *
  * Function    :  get_url_spec_param
@@ -2385,10 +2280,10 @@ jb_err cgi_error_file(struct client_state *csp,
 
 /*********************************************************************
  *
- * Function    :  cgi_error_bad_param
+ * Function    :  cgi_error_disabled
  *
- * Description :  CGI function that is called if the parameters
- *                (query string) for a CGI were wrong.
+ * Description :  CGI function that is called if the actions editor
+ *                is called although it's disabled in config
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -2446,13 +2341,13 @@ jb_err cgi_edit_actions(struct client_state *csp,
    }
 
    /* FIXME: Incomplete */
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       return JB_ERR_MEMORY;
    }
    if (enlist_unique_header(rsp->headers, "Location",
-      CGI_PREFIX "edit-actions-list?f=ijb"))
+      CGI_PREFIX "edit-actions-list?f=default"))
    {
       free(rsp->status);
       rsp->status = NULL;
@@ -2493,7 +2388,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
    char * url_template;
    char * sections;
    char * urls;
-   char buf[50];
+   char buf[150];
    char * s;
    struct map * exports;
    struct map * section_exports;
@@ -2502,7 +2397,10 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
    struct file_line * cur_line;
    unsigned line_number = 0;
    unsigned prev_section_line_number = ((unsigned) (-1));
-   int url_1_2;
+   int i, url_1_2;
+   struct file_list * fl;
+   struct url_actions * b;
+   char * buttons = NULL;
    jb_err err;
 
    if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
@@ -2510,6 +2408,13 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
       return cgi_error_disabled(csp, rsp);
    }
 
+   if (NULL == (exports = default_exports(csp, NULL)))
+   {
+      edit_free_file(file);
+      return JB_ERR_MEMORY;
+   }
+
+   /* Load actions file */
    err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
    if (err)
    {
@@ -2517,15 +2422,140 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
       return (err == JB_ERR_FILE ? JB_ERR_OK : err);
    }
 
-   if (NULL == (exports = default_exports(csp, NULL)))
+   /* Find start of actions in file */
+   cur_line = file->lines;
+   line_number = 1;
+   while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
    {
-      edit_free_file(file);
-      return JB_ERR_MEMORY;
+      cur_line = cur_line->next;
+      line_number++;
    }
 
-   err = map(exports, "f", 1, file->identifier, 1);
+   /*
+    * Conventional actions files should have a match all block
+    * at the start:
+    * cur_line             = {...global actions...}
+    * cur_line->next       = /
+    * cur_line->next->next = {...actions...} or EOF
+    */
+   if ( (cur_line != NULL)
+     && (cur_line->type == FILE_LINE_ACTION)
+     && (cur_line->next != NULL)
+     && (cur_line->next->type == FILE_LINE_URL)
+     && (0 == strcmp(cur_line->next->unprocessed, "/"))
+     && ( (cur_line->next->next == NULL)
+       || (cur_line->next->next->type != FILE_LINE_URL)
+      ) )
+   {
+      /*
+       * Generate string with buttons to set actions for "/" to
+       * any predefined set of actions (named standard.*, probably
+       * residing in standard.action).
+       */
+
+      err = template_load(csp, &section_template, "edit-actions-list-button", 0);
+      if (err)
+      {
+         edit_free_file(file);
+         free_map(exports);
+         if (err == JB_ERR_FILE)
+         {
+            return cgi_error_no_template(csp, rsp, "edit-actions-list-button");
+         }
+         return err;
+      }
+
+      err = template_fill(&section_template, exports);
+      if (err)
+      {
+         edit_free_file(file);
+         free_map(exports);
+         return err;
+      }
+
+      buttons = strdup("");
+      for (i = 0; i < MAX_ACTION_FILES; i++)
+      {
+         if (((fl = csp->actions_list[i]) != NULL) && ((b = fl->f) != NULL))
+         {
+            for (b = b->next; NULL != b; b = b->next)
+            {
+               if (!strncmp(b->url->spec, "standard.", 9) && *(b->url->spec + 9) != '\0')
+               {
+                  if (err || (NULL == (section_exports = new_map())))
+                  {
+                     freez(buttons);
+                     free(section_template);
+                     edit_free_file(file);
+                     free_map(exports);
+                     return JB_ERR_MEMORY;
+                  }
+
+                  err = map(section_exports, "button-name", 1, b->url->spec + 9, 1);
+
+                  if (err || (NULL == (s = strdup(section_template))))
+                  {
+                     free_map(section_exports);
+                     freez(buttons);
+                     free(section_template);
+                     edit_free_file(file);
+                     free_map(exports);
+                     return JB_ERR_MEMORY;
+                  }
+
+                  if (!err) err = template_fill(&s, section_exports);
+                  free_map(section_exports);
+                  if (!err) err = string_join(&buttons, s);
+               }
+            }
+         }
+      }
+      freez(section_template);
+      if (!err) err = map(exports, "all-urls-buttons", 1, buttons, 0);
+
+      /*
+       * Conventional actions file, supply extra editing help.
+       * (e.g. don't allow them to make it an unconventional one).
+       */
+      if (!err) err = map_conditional(exports, "all-urls-present", 1);
+
+      snprintf(buf, 150, "%d", line_number);
+      if (!err) err = map(exports, "all-urls-s", 1, buf, 1);
+      snprintf(buf, 150, "%d", line_number + 2);
+      if (!err) err = map(exports, "all-urls-s-next", 1, buf, 1);
+      if (!err) err = map(exports, "all-urls-actions", 1,
+                          actions_to_html(csp, cur_line->data.action), 0);
+
+       /* Skip the 2 lines */
+      cur_line = cur_line->next->next;
+      line_number += 2;
+
+      /*
+       * Note that prev_section_line_number is NOT set here.
+       * This is deliberate and not a bug.  It stops a "Move up"
+       * option appearing on the next section.  Clicking "Move
+       * up" would make the actions file unconventional, which
+       * we don't want, so we hide this option.
+       */
+   }
+   else
+   {
+      /*
+       * Non-standard actions file - does not begin with
+       * the "All URLs" section.
+       */
+      if (!err) err = map_conditional(exports, "all-urls-present", 0);
+   }
+
+   /* Set up global exports */
+
+   if (!err) err = map(exports, "f", 1, file->identifier, 1);
    if (!err) err = map(exports, "v", 1, file->version_str, 1);
 
+   /* Discourage private additions to default.action */
+
+   if (!err) err = map_conditional(exports, "default-action",
+                                   (strcmp("default", lookup(parameters, "f")) == 0));
    if (err)
    {
       edit_free_file(file);
@@ -2535,7 +2565,9 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
 
    /* Should do all global exports above this point */
 
-   err = template_load(csp, &section_template, "edit-actions-list-section");
+   /* Load templates */
+
+   err = template_load(csp, &section_template, "edit-actions-list-section", 0);
    if (err)
    {
       edit_free_file(file);
@@ -2547,7 +2579,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
       return err;
    }
 
-   err = template_load(csp, &url_template, "edit-actions-list-url");
+   err = template_load(csp, &url_template, "edit-actions-list-url", 0);
    if (err)
    {
       free(section_template);
@@ -2579,15 +2611,6 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
       return err;
    }
 
-   /* Find start of actions in file */
-   cur_line = file->lines;
-   line_number = 1;
-   while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
-   {
-      cur_line = cur_line->next;
-      line_number++;
-   }
-
    if (NULL == (sections = strdup("")))
    {
       free(section_template);
@@ -2609,10 +2632,10 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
          return JB_ERR_MEMORY;
       }
 
-      snprintf(buf, 50, "%d", line_number);
+      snprintf(buf, 150, "%d", line_number);
       err = map(section_exports, "s", 1, buf, 1);
       if (!err) err = map(section_exports, "actions", 1,
-                          actions_to_html(cur_line->data.action), 0);
+                          actions_to_html(csp, cur_line->data.action), 0);
 
       if ( (!err)
         && (cur_line->next != NULL)
@@ -2629,7 +2652,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
       if (prev_section_line_number != ((unsigned)(-1)))
       {
          /* Not last section */
-         snprintf(buf, 50, "%d", prev_section_line_number);
+         snprintf(buf, 150, "%d", prev_section_line_number);
          if (!err) err = map(section_exports, "s-prev", 1, buf, 1);
          if (!err) err = map_block_keep(section_exports, "s-prev-exists");
       }
@@ -2683,10 +2706,10 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
             return JB_ERR_MEMORY;
          }
 
-         snprintf(buf, 50, "%d", line_number);
+         snprintf(buf, 150, "%d", line_number);
          err = map(url_exports, "p", 1, buf, 1);
 
-         snprintf(buf, 50, "%d", url_1_2);
+         snprintf(buf, 150, "%d", url_1_2);
          if (!err) err = map(url_exports, "url-1-2", 1, buf, 1);
 
          if (!err) err = map(url_exports, "url-html", 1,
@@ -2753,7 +2776,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
         && (cur_line->type == FILE_LINE_ACTION))
       {
          /* Not last section */
-         snprintf(buf, 50, "%d", line_number);
+         snprintf(buf, 150, "%d", line_number);
          if (!err) err = map(section_exports, "s-next", 1, buf, 1);
          if (!err) err = map_block_keep(section_exports, "s-next-exists");
       }
@@ -2821,7 +2844,7 @@ jb_err cgi_edit_actions_list(struct client_state *csp,
 
 /*********************************************************************
  *
- * Function    :  cgi_edit_actions
+ * Function    :  cgi_edit_actions_for_url
  *
  * Description :  CGI function that edits the Actions list.
  *
@@ -2921,7 +2944,7 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
       int index = 0;
       char * filter_template;
 
-      err = template_load(csp, &filter_template, "edit-actions-for-url-filter");
+      err = template_load(csp, &filter_template, "edit-actions-for-url-filter", 0);
       if (err)
       {
          edit_free_file(file);
@@ -2933,6 +2956,8 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
          return err;
       }
 
+      err = template_fill(&filter_template, exports);
+
       result = strdup("");
 
       for (;(!err) && (filter_group != NULL); filter_group = filter_group->next)
@@ -2997,6 +3022,9 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp,
             free_map(line_exports);
          }
       }
+
+      freez(filter_template);
+
       if (!err)
       {
          err = map(exports, "filter-params", 1, result, 0);
@@ -3055,7 +3083,10 @@ jb_err cgi_edit_actions_submit(struct client_state *csp,
    char * target;
    jb_err err;
    int index;
+   const char * action_set_name;
    char ch;
+   struct file_list * fl;
+   struct url_actions * b;
 
    if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
    {
@@ -3092,7 +3123,33 @@ jb_err cgi_edit_actions_submit(struct client_state *csp,
       return JB_ERR_CGI_PARAMS;
    }
 
-   err = actions_from_radio(parameters, cur_line->data.action);
+   get_string_param(parameters, "p", &action_set_name);
+   if (action_set_name != NULL)
+   {
+      for (index = 0; index < MAX_ACTION_FILES; index++)
+      {
+         if (((fl = csp->actions_list[index]) != NULL) && ((b = fl->f) != NULL))
+         {
+            for (b = b->next; NULL != b; b = b->next)
+            {
+               if (!strncmp(b->url->spec, "standard.", 9) && !strcmp(b->url->spec + 9, action_set_name))
+               {
+                  copy_action(cur_line->data.action, b->action); 
+                  goto found;
+               }
+            }
+         }
+      }
+      edit_free_file(file);
+      return JB_ERR_CGI_PARAMS;
+
+      found: ;
+   }
+   else
+   {
+      err = actions_from_radio(parameters, cur_line->data.action);
+   }
+
    if(err)
    {
       /* Out of memory */
@@ -3134,7 +3191,6 @@ jb_err cgi_edit_actions_submit(struct client_state *csp,
          break;
       }
 
-
       value = get_char_param(parameters, key_value);
       if (value == 'Y')
       {
@@ -3218,7 +3274,7 @@ jb_err cgi_edit_actions_submit(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -3340,7 +3396,7 @@ jb_err cgi_edit_actions_url(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -3479,7 +3535,7 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -3595,7 +3651,7 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -3727,7 +3783,7 @@ jb_err cgi_edit_actions_section_remove(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -3901,7 +3957,7 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -4100,7 +4156,7 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp,
       return JB_ERR_MEMORY;
    }
 
-   rsp->status = strdup("302 Local Redirect from Junkbuster");
+   rsp->status = strdup("302 Local Redirect from Privoxy");
    if (rsp->status == NULL)
    {
       free(target);
@@ -4141,7 +4197,6 @@ jb_err cgi_toggle(struct client_state *csp,
    struct map *exports;
    char mode;
    const char *template_name;
-   jb_err err;
 
    assert(csp);
    assert(rsp);
@@ -4152,11 +4207,6 @@ jb_err cgi_toggle(struct client_state *csp,
       return cgi_error_disabled(csp, rsp);
    }
 
-   if (NULL == (exports = default_exports(csp, "toggle")))
-   {
-      return JB_ERR_MEMORY;
-   }
-
    mode = get_char_param(parameters, "set");
 
    if (mode == 'E')
@@ -4175,11 +4225,9 @@ jb_err cgi_toggle(struct client_state *csp,
       g_bToggleIJB = !g_bToggleIJB;
    }
 
-   err = map_conditional(exports, "enabled", g_bToggleIJB);
-   if (err)
+   if (NULL == (exports = default_exports(csp, "toggle")))
    {
-      free_map(exports);
-      return err;
+      return JB_ERR_MEMORY;
    }
 
    template_name = (get_char_param(parameters, "mini")