Added LICENSE, pdfs, privoxy-index and HTMLized man-page to docs
[privoxy.git] / cgiedit.c
index 87b3cb5..0f0e46d 100644 (file)
--- a/cgiedit.c
+++ b/cgiedit.c
@@ -1,4 +1,4 @@
-const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.37 2002/04/30 11:14:52 oes Exp $";
+const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.40 2002/05/19 11:34:35 jongfoster Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
@@ -42,6 +42,22 @@ const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.37 2002/04/30 11:14:52 oes Exp $"
  *
  * Revisions   :
  *    $Log: cgiedit.c,v $
+ *    Revision 1.40  2002/05/19 11:34:35  jongfoster
+ *    Handling read-only actions files better - report the actual
+ *    error, not "Out of memory"!
+ *
+ *    Bug report:
+ *    http://sourceforge.net/tracker/index.php?func=detail
+ *    &aid=557905&group_id=11118&atid=111118
+ *
+ *    Revision 1.39  2002/05/12 21:39:15  jongfoster
+ *    - Adding Doxygen-style comments to structures and #defines.
+ *    - Correcting function comments
+ *
+ *    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
  *
@@ -273,23 +289,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.
@@ -306,42 +348,65 @@ 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/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) */
+   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) */
 };
 
 /* FIXME: Following non-static functions should be prototyped in .h or made static */
@@ -373,6 +438,9 @@ jb_err cgi_error_parse(struct client_state *csp,
 jb_err cgi_error_file(struct client_state *csp,
                       struct http_response *rsp,
                       const char *filename);
+jb_err cgi_error_file_read_only(struct client_state *csp,
+                                struct http_response *rsp,
+                                const char *filename);
 jb_err cgi_error_disabled(struct client_state *csp,
                           struct http_response *rsp);
 
@@ -418,6 +486,34 @@ static jb_err map_copy_parameter_url(struct map *out,
                                      const char *name);
 #endif /* unused function */
 
+/* Internal convenience functions */
+static char *section_target(const unsigned sectionid);
+
+/*********************************************************************
+ *
+ * Function    :  section_target
+ *
+ * Description :  Given an unsigned (section id) n, produce a dynamically
+ *                allocated string of the form #l<n>, for use in link
+ *                targets.
+ *
+ * Parameters  :
+ *          1  :  sectionid = start line number of section
+ *
+ * Returns     :  String with link target, or NULL if out of
+ *                memory
+ *
+ *********************************************************************/
+static char *section_target(const unsigned sectionid)
+{
+   char buf[30];
+
+   snprintf(buf, 30, "#l%d", sectionid);
+   return(strdup(buf));
+
+}
+
+
 /*********************************************************************
  *
  * Function    :  map_copy_parameter_html
@@ -547,6 +643,7 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp,
    struct editable_file * file;
    struct file_line * cur_line;
    unsigned line_number;
+   unsigned section_start_line_number = 0;
    jb_err err;
 
    assert(csp);
@@ -575,6 +672,10 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp,
 
    for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
    {
+      if (cur_line->type == FILE_LINE_ACTION)
+      {
+         section_start_line_number = line_number;
+      }
       cur_line = cur_line->next;
    }
 
@@ -598,6 +699,7 @@ jb_err cgi_edit_actions_url_form(struct client_state *csp,
    if (!err) err = map(exports, "v", 1, file->version_str, 1);
    if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0);
    if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
+   if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0);
 
    edit_free_file(file);
 
@@ -701,6 +803,7 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
    struct editable_file * file;
    struct file_line * cur_line;
    unsigned line_number;
+   unsigned section_start_line_number = 0;
    jb_err err;
 
    assert(csp);
@@ -729,6 +832,10 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
 
    for (line_number = 1; (cur_line != NULL) && (line_number < patternid); line_number++)
    {
+      if (cur_line->type == FILE_LINE_ACTION)
+      {
+         section_start_line_number = line_number;
+      }      
       cur_line = cur_line->next;
    }
 
@@ -750,8 +857,9 @@ jb_err cgi_edit_actions_remove_url_form(struct client_state *csp,
 
    err = map(exports, "f", 1, file->identifier, 1);
    if (!err) err = map(exports, "v", 1, file->version_str, 1);
-   if (!err) err = map(exports, "s", 1, url_encode(lookup(parameters, "s")), 0);
+   if (!err) err = map(exports, "p", 1, url_encode(lookup(parameters, "p")), 0);
    if (!err) err = map(exports, "u", 1, html_encode(cur_line->unprocessed), 0);
+   if (!err) err = map(exports, "jumptarget", 1, section_target(section_start_line_number), 0);
 
    edit_free_file(file);
 
@@ -772,8 +880,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.
@@ -1081,8 +1188,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
@@ -1460,6 +1567,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
@@ -2225,6 +2333,51 @@ jb_err cgi_error_file(struct client_state *csp,
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  cgi_error_file
+ *
+ * Description :  CGI function that is called when a file cannot be
+ *                opened for writing by the CGI editor.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  rsp = http_response data structure for output
+ *          3  :  filename = The file that we can't write to
+ *
+ * CGI Parameters : none
+ *
+ * Returns     :  JB_ERR_OK on success
+ *                JB_ERR_MEMORY on out-of-memory error.
+ *
+ *********************************************************************/
+jb_err cgi_error_file_read_only(struct client_state *csp,
+                                struct http_response *rsp,
+                                const char *filename)
+{
+   struct map *exports;
+   jb_err err;
+
+   assert(csp);
+   assert(rsp);
+   assert(filename);
+
+   if (NULL == (exports = default_exports(csp, NULL)))
+   {
+      return JB_ERR_MEMORY;
+   }
+
+   err = map(exports, "f", 1, html_encode(filename), 0);
+   if (err)
+   {
+      free_map(exports);
+      return err;
+   }
+
+   return template_fill_for_cgi(csp, "cgi-error-file-read-only", exports, rsp);
+}
+
+
 /*********************************************************************
  *
  * Function    :  cgi_error_disabled
@@ -3206,12 +3359,19 @@ jb_err cgi_edit_actions_submit(struct client_state *csp,
    if (err)
    {
       /* Error writing file */
+      if (err == JB_ERR_FILE)
+      {
+         /* Read-only file. */
+         err = cgi_error_file_read_only(csp, rsp, file->identifier);
+      }
       edit_free_file(file);
       return err;
    }
 
    target = strdup(CGI_PREFIX "edit-actions-list?f=");
    string_append(&target, file->identifier);
+   string_join(&target, section_target(sectionid));
+
 
    edit_free_file(file);
 
@@ -3268,6 +3428,7 @@ jb_err cgi_edit_actions_url(struct client_state *csp,
    struct editable_file * file;
    struct file_line * cur_line;
    unsigned line_number;
+   unsigned section_start_line_number = 0;
    char * target;
    jb_err err;
 
@@ -3305,6 +3466,10 @@ jb_err cgi_edit_actions_url(struct client_state *csp,
 
    while ((cur_line != NULL) && (line_number < patternid))
    {
+      if (cur_line->type == FILE_LINE_ACTION)
+      {
+         section_start_line_number = line_number;
+      }      
       cur_line = cur_line->next;
       line_number++;
    }
@@ -3328,12 +3493,18 @@ jb_err cgi_edit_actions_url(struct client_state *csp,
    if (err)
    {
       /* Error writing file */
+      if (err == JB_ERR_FILE)
+      {
+         /* Read-only file. */
+         err = cgi_error_file_read_only(csp, rsp, file->identifier);
+      }
       edit_free_file(file);
       return err;
    }
 
    target = strdup(CGI_PREFIX "edit-actions-list?f=");
    string_append(&target, file->identifier);
+   string_join(&target, section_target(section_start_line_number));
 
    edit_free_file(file);
 
@@ -3467,12 +3638,18 @@ jb_err cgi_edit_actions_add_url(struct client_state *csp,
    if (err)
    {
       /* Error writing file */
+      if (err == JB_ERR_FILE)
+      {
+         /* Read-only file. */
+         err = cgi_error_file_read_only(csp, rsp, file->identifier);
+      }
       edit_free_file(file);
       return err;
    }
 
    target = strdup(CGI_PREFIX "edit-actions-list?f=");
    string_append(&target, file->identifier);
+   string_join(&target, section_target(sectionid));
 
    edit_free_file(file);
 
@@ -3527,6 +3704,7 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp,
    struct file_line * cur_line;
    struct file_line * prev_line;
    unsigned line_number;
+   unsigned section_start_line_number = 0;
    char * target;
    jb_err err;
 
@@ -3554,6 +3732,10 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp,
 
    while ((cur_line != NULL) && (line_number < patternid))
    {
+      if (cur_line->type == FILE_LINE_ACTION)
+      {
+         section_start_line_number = line_number;
+      }
       prev_line = cur_line;
       cur_line = cur_line->next;
       line_number++;
@@ -3583,12 +3765,18 @@ jb_err cgi_edit_actions_remove_url(struct client_state *csp,
    if (err)
    {
       /* Error writing file */
+      if (err == JB_ERR_FILE)
+      {
+         /* Read-only file. */
+         err = cgi_error_file_read_only(csp, rsp, file->identifier);
+      }
       edit_free_file(file);
       return err;
    }
 
    target = strdup(CGI_PREFIX "edit-actions-list?f=");
    string_append(&target, file->identifier);
+   string_join(&target, section_target(section_start_line_number));
 
    edit_free_file(file);
 
@@ -3715,6 +3903,11 @@ jb_err cgi_edit_actions_section_remove(struct client_state *csp,
    if (err)
    {
       /* Error writing file */
+      if (err == JB_ERR_FILE)
+      {
+         /* Read-only file. */
+         err = cgi_error_file_read_only(csp, rsp, file->identifier);
+      }
       edit_free_file(file);
       return err;
    }
@@ -3889,6 +4082,11 @@ jb_err cgi_edit_actions_section_add(struct client_state *csp,
    if (err)
    {
       /* Error writing file */
+      if (err == JB_ERR_FILE)
+      {
+         /* Read-only file. */
+         err = cgi_error_file_read_only(csp, rsp, file->identifier);
+      }
       edit_free_file(file);
       return err;
    }
@@ -4087,6 +4285,11 @@ jb_err cgi_edit_actions_section_swap(struct client_state *csp,
       if (err)
       {
          /* Error writing file */
+         if (err == JB_ERR_FILE)
+         {
+            /* Read-only file. */
+            err = cgi_error_file_read_only(csp, rsp, file->identifier);
+         }
          edit_free_file(file);
          return err;
       }