Add FEATURE_ZLIB to the list of conditional
[privoxy.git] / cgisimple.c
index 9f855a7..9504ef9 100644 (file)
@@ -1,4 +1,4 @@
-const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.31 2002/04/26 12:54:36 oes Exp $";
+const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.45 2006/12/28 18:16:41 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/cgisimple.c,v $
@@ -9,7 +9,7 @@ const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.31 2002/04/26 12:54:36 oes Ex
  *                Functions declared include:
  * 
  *
- * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
+ * Copyright   :  Written by and Copyright (C) 2001-2006 the SourceForge
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -36,6 +36,100 @@ const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.31 2002/04/26 12:54:36 oes Ex
  *
  * Revisions   :
  *    $Log: cgisimple.c,v $
+ *    Revision 1.45  2006/12/28 18:16:41  fabiankeil
+ *    Fixed gcc43 compiler warnings, zero out cgi_send_user_manual's
+ *    body memory before using it, replaced sprintf calls with snprintf.
+ *
+ *    Revision 1.44  2006/12/22 14:19:27  fabiankeil
+ *    Removed checks whether or not AF_FILES have
+ *    data structures associated with them in cgi_show_status.
+ *    It doesn't matter as we're only interested in the file names.
+ *
+ *    For the action files the checks were always true,
+ *    but they prevented empty filter files from being
+ *    listed. Fixes parts of BR 1619208.
+ *
+ *    Revision 1.43  2006/12/17 17:57:56  fabiankeil
+ *    - Added FEATURE_GRACEFUL_TERMINATION to the
+ *      "conditional #defines" section
+ *    - Escaped ampersands in generated HTML.
+ *    - Renamed re-filter-filename to re-filter-filenames
+ *
+ *    Revision 1.42  2006/11/21 15:43:12  fabiankeil
+ *    Add special treatment for WIN32 to make sure
+ *    cgi_send_user_manual opens the files in binary mode.
+ *    Fixes BR 1600411 and unbreaks image delivery.
+ *
+ *    Remove outdated comment.
+ *
+ *    Revision 1.41  2006/10/09 19:18:28  roro
+ *    Redirect http://p.p/user-manual (without trailing slash) to
+ *    http://p.p/user-manual/ (with trailing slash), otherwise links will be broken.
+ *
+ *    Revision 1.40  2006/09/09 13:05:33  fabiankeil
+ *    Modified cgi_send_user_manual to serve binary
+ *    content without destroying it first. Should also be
+ *    faster now. Added ".jpg" check for Content-Type guessing.
+ *
+ *    Revision 1.39  2006/09/08 09:49:23  fabiankeil
+ *    Deliver documents in the user-manual directory
+ *    with "Content-Type text/css" if their filename
+ *    ends with ".css".
+ *
+ *    Revision 1.38  2006/09/06 18:45:03  fabiankeil
+ *    Incorporate modified version of Roland Rosenfeld's patch to
+ *    optionally access the user-manual via Privoxy. Closes patch 679075.
+ *
+ *    Formatting changed to Privoxy style, added call to
+ *    cgi_error_no_template if the requested file doesn't
+ *    exist and modified check whether or not Privoxy itself
+ *    should serve the manual. Should work cross-platform now.
+ *
+ *    Revision 1.37  2006/07/18 14:48:45  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
+ *    Revision 1.35.2.7  2006/01/29 23:10:56  david__schmidt
+ *    Multiple filter file support
+ *
+ *    Revision 1.35.2.6  2005/07/04 03:13:43  david__schmidt
+ *    Undo some damaging memory leak patches
+ *
+ *    Revision 1.35.2.5  2005/05/07 21:50:55  david__schmidt
+ *    A few memory leaks plugged (mostly on error paths)
+ *
+ *    Revision 1.35.2.4  2005/04/04 02:21:24  david__schmidt
+ *    Another instance of:
+ *    Don't show "Edit" buttons #ifndef FEATURE_CGI_EDIT_ACTIONS
+ *    Thanks to Magnus Holmgren for the patch
+ *
+ *    Revision 1.35.2.3  2003/12/17 16:34:15  oes
+ *     - Prevent line wrap beween "View/Edit" link buttons on status page
+ *     - Some (mostly irrelevant) fixes for Out-of-mem-case handling
+ *
+ *    Revision 1.35.2.2  2003/04/03 13:48:28  oes
+ *    Don't show "Edit" buttons #ifndef FEATURE_CGI_EDIT_ACTIONS
+ *
+ *    Revision 1.35.2.1  2002/07/04 15:02:38  oes
+ *    Added ability to send redirects to send-banner CGI, so that it can completely mimic the image blocking action if called with type=auto
+ *
+ *    Revision 1.35.2.1  2002/07/01 17:32:04  morcego
+ *    Applying patch from Andreas as provided by Hal on the list.
+ *    Message-ID: <20020701121218.V1606@feenix.burgiss.net>
+ *
+ *    Revision 1.35  2002/05/12 21:44:44  jongfoster
+ *    Adding amiga.[ch] revision information, if on an amiga.
+ *
+ *    Revision 1.34  2002/04/30 12:06:12  oes
+ *    Deleted unused code from default_cgi
+ *
+ *    Revision 1.33  2002/04/30 11:14:52  oes
+ *    Made csp the first parameter in *action_to_html
+ *
+ *    Revision 1.32  2002/04/26 18:29:13  jongfoster
+ *    Fixing this Visual C++ warning:
+ *    cgisimple.c(775) : warning C4018: '<' : signed/unsigned mismatch
+ *
  *    Revision 1.31  2002/04/26 12:54:36  oes
  *     - Kill obsolete REDIRECT_URL code
  *     - Error handling fixes
@@ -209,8 +303,9 @@ static jb_err show_defines(struct map *exports);
  *
  * Function    :  cgi_default
  *
- * Description :  CGI function that is called if no action was given.
- *                Lists menu of available unhidden CGIs.
+ * Description :  CGI function that is called for the CGI_SITE_1_HOST
+ *                and CGI_SITE_2_HOST/CGI_SITE_2_PATH base URLs.
+ *                Boring - only exports the default exports.
  *               
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -221,57 +316,26 @@ static jb_err show_defines(struct map *exports);
  *
  * Returns     :  JB_ERR_OK on success
  *                JB_ERR_MEMORY on out-of-memory
- *                (Problems other than out-of-memory should be
- *                handled by this routine - it should set the
- *                rsp appropriately and return "success")
  *
  *********************************************************************/
 jb_err cgi_default(struct client_state *csp,
                    struct http_response *rsp,
                    const struct map *parameters)
 {
-   char *tmp;
    struct map *exports;
 
    assert(csp);
    assert(rsp);
-   assert(parameters);
 
    if (NULL == (exports = default_exports(csp, "")))
    {
       return JB_ERR_MEMORY;
    }
 
-   /* If there were other parameters, export a dump as "cgi-parameters" */
-   if (parameters->first)
-   {
-      tmp = strdup("<p>What made you think this cgi takes parameters?\n"
-                   "Anyway, here they are, in case you're interested:</p>\n");
-      string_join(&tmp, dump_map(parameters));
-      if (tmp == NULL)
-      {
-         free_map(exports);
-         return JB_ERR_MEMORY;
-      }
-      if (map(exports, "cgi-parameters", 1, tmp, 0))
-      {
-         return JB_ERR_MEMORY;
-      }
-   }
-   else
-   {
-      if (map(exports, "cgi-parameters", 1, "", 1))
-      {
-         return JB_ERR_MEMORY;
-      }
-   }
-
    return template_fill_for_cgi(csp, "default", exports, rsp);
 }
 
 
-
-
 /*********************************************************************
  *
  * Function    :  cgi_error_404
@@ -437,7 +501,8 @@ jb_err cgi_show_request(struct client_state *csp,
  *           type : Selects the type of banner between "trans", "logo",
  *                  and "auto". Defaults to "logo" if absent or invalid.
  *                  "auto" means to select as if we were image-blocking.
- *                  (Only the first character really counts).
+ *                  (Only the first character really counts; b and t are
+ *                  equivalent).
  *
  * Returns     :  JB_ERR_OK on success
  *                JB_ERR_MEMORY on out-of-memory error.  
@@ -449,20 +514,24 @@ jb_err cgi_send_banner(struct client_state *csp,
 {
    char imagetype = lookup(parameters, "type")[0];
 
-   if (imagetype == 'a') /* auto */
+   /*
+    * If type is auto, then determine the right thing
+    * to do from the set-image-blocker action
+    */
+   if (imagetype == 'a') 
    {
-      /* Default to pattern */
+      /*
+       * Default to pattern
+       */
       imagetype = 'p';
+
 #ifdef FEATURE_IMAGE_BLOCKING
       if ((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
       {
          static const char prefix1[] = CGI_PREFIX "send-banner?type=";
          static const char prefix2[] = "http://" CGI_SITE_1_HOST "/send-banner?type=";
+         const char *p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
 
-         /* determine HOW images should be blocked */
-         const char * p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
-
-         /* and handle accordingly: */
          if (p == NULL)
          {
             /* Use default - nothing to do here. */
@@ -475,6 +544,11 @@ jb_err cgi_send_banner(struct client_state *csp,
          {
             imagetype = 'p';
          }
+
+         /*
+          * If the action is to call this CGI, determine
+          * the argument:
+          */
          else if (0 == strncmpic(p, prefix1, sizeof(prefix1) - 1))
          {
             imagetype = p[sizeof(prefix1) - 1];
@@ -483,34 +557,63 @@ jb_err cgi_send_banner(struct client_state *csp,
          {
             imagetype = p[sizeof(prefix2) - 1];
          }
+
+         /*
+          * Everything else must (should) be a URL to
+          * redirect to.
+          */
+         else
+         {
+            imagetype = 'r';
+         }
       }
 #endif /* def FEATURE_IMAGE_BLOCKING */
    }
       
-   if ((imagetype == 'b') || (imagetype == 't')) /* blank / transparent */
+   /*
+    * Now imagetype is either the non-auto type we were called with,
+    * or it was auto and has since been determined. In any case, we
+    * can proceed to actually answering the request by sending a redirect
+    * or an image as appropriate:
+    */
+   if (imagetype == 'r') 
    {
-      rsp->body = bindup(image_blank_data, image_blank_length);
-      rsp->content_length = image_blank_length;
-
+      rsp->status = strdup("302 Local Redirect from Privoxy");
+      if (rsp->status == NULL)
+      {
+         return JB_ERR_MEMORY;
+      }
+      if (enlist_unique_header(rsp->headers, "Location",
+                               csp->action->string[ACTION_STRING_IMAGE_BLOCKER]))
+      {
+         return JB_ERR_MEMORY;
+      }
    }
-   else /* pattern */
+   else
    {
-      rsp->body = bindup(image_pattern_data, image_pattern_length);
-      rsp->content_length = image_pattern_length;
-   }   
+      if ((imagetype == 'b') || (imagetype == 't')) 
+      {
+         rsp->body = bindup(image_blank_data, image_blank_length);
+         rsp->content_length = image_blank_length;
+      }
+      else
+      {
+         rsp->body = bindup(image_pattern_data, image_pattern_length);
+         rsp->content_length = image_pattern_length;
+      }
 
-   if (rsp->body == NULL)
-   {
-      return JB_ERR_MEMORY;
-   }
+      if (rsp->body == NULL)
+      {
+         return JB_ERR_MEMORY;
+      }
+      if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
+      {
+         return JB_ERR_MEMORY;
+      }
 
-   if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
-   {
-      return JB_ERR_MEMORY;
+      rsp->is_static = 1;
    }
 
-   rsp->is_static = 1;
-
    return JB_ERR_OK;
 
 }
@@ -606,6 +709,130 @@ jb_err cgi_send_stylesheet(struct client_state *csp,
    return JB_ERR_OK;
 
 }
+/*********************************************************************
+ *
+ * Function    :  cgi_send_user_manual
+ *
+ * Description :  CGI function that sends a file in the user
+ *                manual directory.
+ *
+ * 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 : file=name.html, the name of the HTML file
+ *                  (relative to user-manual from config)
+ *
+ * Returns     :  JB_ERR_OK on success
+ *                JB_ERR_MEMORY on out-of-memory error.  
+ *
+ *********************************************************************/
+jb_err cgi_send_user_manual(struct client_state *csp,
+                            struct http_response *rsp,
+                            const struct map *parameters)
+{
+   const char * filename;
+   char *full_path;
+   FILE *fp;
+   jb_err err = JB_ERR_OK;
+   size_t length;
+
+   assert(csp);
+   assert(rsp);
+   assert(parameters);
+
+   if (!parameters->first)
+   {
+      /* requested http://p.p/user-manual (without trailing slash) */
+      return cgi_redirect(rsp, CGI_PREFIX "user-manual/");
+   }
+
+   get_string_param(parameters, "file", &filename);
+   /* Check paramter for hack attempts */
+   if (filename && strchr(filename, '/'))
+   {
+      return JB_ERR_CGI_PARAMS;
+   }
+   if (filename && strstr(filename, ".."))
+   {
+      return JB_ERR_CGI_PARAMS;
+   }
+
+   full_path = make_path(csp->config->usermanual, filename ? filename : "index.html");
+   if (full_path == NULL)
+   {
+      return JB_ERR_MEMORY;
+   }
+
+   /* Open user-manual file */
+#ifdef WIN32
+   /*
+    * XXX: Do we support other operating systems that
+    * require special treatment to fopen in binary mode?
+    */
+   if (NULL == (fp = fopen(full_path, "rb")))
+#else
+   if (NULL == (fp = fopen(full_path, "r")))
+#endif /* def WIN32 */
+   {
+      log_error(LOG_LEVEL_ERROR, "Cannot open user-manual file %s: %E", full_path);
+      err = cgi_error_no_template(csp, rsp, full_path);
+      free(full_path);
+      return err;
+   }
+
+   /* Get file length */
+   fseek(fp, 0, SEEK_END);
+   length = (size_t)ftell(fp);
+   fseek(fp, 0, SEEK_SET);
+
+   /* Allocate memory and load the file directly into the body */
+   rsp->body = (char *)malloc(length+1);
+   if (!rsp->body)
+   {
+      fclose(fp);
+      free(full_path);
+      return JB_ERR_MEMORY;
+   }
+   memset(rsp->body, '\0', length+1);
+   if (!fread(rsp->body, length, 1, fp))
+   {
+      /*
+       * This happens if we didn't fopen in binary mode.
+       * If it does, we just log it and serve what we got.
+       */
+      log_error(LOG_LEVEL_ERROR, "Couldn't completely read user-manual file %s.", full_path);
+   }
+   fclose(fp);
+   free(full_path);
+
+   rsp->content_length = length;
+
+   /* Guess correct Content-Type based on the filename's ending */
+   if (filename)
+   {
+      length = strlen(filename);
+   }
+   else
+   {
+      length = 0;
+   } 
+   if((length>=4) && !strcmp(&filename[length-4], ".css"))
+   {
+      err = enlist(rsp->headers, "Content-Type: text/css");
+   }
+   else if((length>=4) && !strcmp(&filename[length-4], ".jpg"))
+   {
+      err = enlist(rsp->headers, "Content-Type: image/jpeg");
+   }
+   else
+   {
+      err = enlist(rsp->headers, "Content-Type: text/html");
+   }
+
+   return err;
+}
 
 
 /*********************************************************************
@@ -655,7 +882,7 @@ jb_err cgi_show_version(struct client_state *csp,
  *
  * Function    :  cgi_show_status
  *
- * Description :  CGI function that returns a web page describing the
+ * Description :  CGI function that returns a web page describing the
  *                current status of Privoxy.
  *
  * Parameters  :
@@ -692,8 +919,6 @@ jb_err cgi_show_status(struct client_state *csp,
    int local_urls_read;
    int local_urls_rejected;
 #endif /* ndef FEATURE_STATISTICS */
-   struct file_list * fl;
-   struct url_actions * b;
    jb_err err = JB_ERR_OK;
 
    struct map *exports;
@@ -710,7 +935,7 @@ jb_err cgi_show_status(struct client_state *csp,
    switch (*(lookup(parameters, "file")))
    {
    case 'a':
-      if (!get_number_param(csp, parameters, "index", &i) && i < MAX_ACTION_FILES && csp->actions_list[i])
+      if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->actions_list[i])
       {
          filename = csp->actions_list[i]->filename;
          file_description = "Actions File";
@@ -718,9 +943,9 @@ jb_err cgi_show_status(struct client_state *csp,
       break;
 
    case 'f':
-      if (csp->rlist)
+      if (!get_number_param(csp, parameters, "index", &i) && i < MAX_AF_FILES && csp->rlist[i])
       {
-         filename = csp->rlist->filename;
+         filename = csp->rlist[i]->filename;
          file_description = "Filter File";
       }
       break;
@@ -814,13 +1039,13 @@ jb_err cgi_show_status(struct client_state *csp,
       perc_rej = (float)local_urls_rejected * 100.0F /
             (float)local_urls_read;
 
-      sprintf(buf, "%d", local_urls_read);
+      snprintf(buf, sizeof(buf), "%d", local_urls_read);
       if (!err) err = map(exports, "requests-received", 1, buf, 1);
 
-      sprintf(buf, "%d", local_urls_rejected);
+      snprintf(buf, sizeof(buf), "%d", local_urls_rejected);
       if (!err) err = map(exports, "requests-blocked", 1, buf, 1);
 
-      sprintf(buf, "%6.2f", perc_rej);
+      snprintf(buf, sizeof(buf), "%6.2f", perc_rej);
       if (!err) err = map(exports, "percent-blocked", 1, buf, 1);
    }
 
@@ -835,20 +1060,22 @@ jb_err cgi_show_status(struct client_state *csp,
     * FIXME: Shouldn't include hardwired HTML here, use line template instead!
     */
    s = strdup("");
-   for (i = 0; i < MAX_ACTION_FILES; i++)
+   for (i = 0; i < MAX_AF_FILES; i++)
    {
-      if (((fl = csp->actions_list[i]) != NULL) && ((b = fl->f) != NULL))
+      if (csp->actions_list[i] != NULL)
       {
          if (!err) err = string_append(&s, "<tr><td>");
          if (!err) err = string_join(&s, html_encode(csp->actions_list[i]->filename));
-         snprintf(buf, 100, "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&index=%d\">View</a> ", i);
+         snprintf(buf, 100, "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&amp;index=%d\">View</a>", i);
          if (!err) err = string_append(&s, buf);
 
+#ifdef FEATURE_CGI_EDIT_ACTIONS
          if (NULL == strstr(csp->actions_list[i]->filename, "standard.action") && NULL != csp->config->actions_file_short[i])
          {
-            snprintf(buf, 100, "<a href=\"/edit-actions-list?f=%s\">Edit</a>", csp->config->actions_file_short[i]);
+            snprintf(buf, 100, "&nbsp;&nbsp;<a href=\"/edit-actions-list?f=%s\">Edit</a>", csp->config->actions_file_short[i]);
             if (!err) err = string_append(&s, buf);
          }
+#endif
 
          if (!err) err = string_append(&s, "</td></tr>\n");
       }
@@ -862,13 +1089,30 @@ jb_err cgi_show_status(struct client_state *csp,
       if (!err) err = map(exports, "actions-filenames", 1, "<tr><td>None specified</td></tr>", 1);
    }
 
-   if (csp->rlist)
+   /* 
+    * List all re_filterfiles in use, together with view options.
+    * FIXME: Shouldn't include hardwired HTML here, use line template instead!
+    */
+   s = strdup("");
+   for (i = 0; i < MAX_AF_FILES; i++)
    {
-      if (!err) err = map(exports, "re-filter-filename", 1, html_encode(csp->rlist->filename), 0);
+      if (csp->rlist[i] != NULL)
+      {
+         if (!err) err = string_append(&s, "<tr><td>");
+         if (!err) err = string_join(&s, html_encode(csp->rlist[i]->filename));
+         snprintf(buf, 100,
+            "</td><td class=\"buttons\"><a href=\"/show-status?file=filter&amp;index=%d\">View</a>", i);
+         if (!err) err = string_append(&s, buf);
+         if (!err) err = string_append(&s, "</td></tr>\n");
+      }
+   }
+   if (*s != '\0')   
+   {
+      if (!err) err = map(exports, "re-filter-filenames", 1, s, 0);
    }
    else
    {
-      if (!err) err = map(exports, "re-filter-filename", 1, "None specified", 1);
+      if (!err) err = map(exports, "re-filter-filenames", 1, "<tr><td>None specified</td></tr>", 1);
       if (!err) err = map_block_killer(exports, "have-filterfile");
    }
 
@@ -1031,7 +1275,7 @@ jb_err cgi_show_url_info(struct client_state *csp,
 
       init_current_action(action);
 
-      if (map(exports, "default", 1, current_action_to_html(action, csp), 0))
+      if (map(exports, "default", 1, current_action_to_html(csp, action), 0))
       {
          free_current_action(action);
          free(url_param);
@@ -1045,6 +1289,7 @@ jb_err cgi_show_url_info(struct client_state *csp,
 
       if (err == JB_ERR_MEMORY)
       {
+         free_http_request(url_to_query);
          free_current_action(action);
          free_map(exports);
          return JB_ERR_MEMORY;
@@ -1057,6 +1302,7 @@ jb_err cgi_show_url_info(struct client_state *csp,
          if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1);
 
          free_current_action(action);
+         free_http_request(url_to_query);
 
          if (err)
          {
@@ -1076,13 +1322,14 @@ jb_err cgi_show_url_info(struct client_state *csp,
          {
             free_current_action(action);
             free_map(exports);
+            free_http_request(url_to_query);
             return JB_ERR_MEMORY;
          }
       }
 
       matches = strdup("<table class=\"transparent\">");
 
-      for (i = 0; i < MAX_ACTION_FILES; i++)
+      for (i = 0; i < MAX_AF_FILES; i++)
       {
          if (NULL == csp->config->actions_file_short[i]
              || !strcmp(csp->config->actions_file_short[i], "standard")) continue;
@@ -1096,11 +1343,15 @@ jb_err cgi_show_url_info(struct client_state *csp,
                /* FIXME: Hardcoded HTML! */
                string_append(&matches, "<tr><th>In file: ");
                string_join  (&matches, html_encode(csp->config->actions_file_short[i]));
-               snprintf(buf, 150, ".action <a class=\"cmd\" href=\"/show-status?file=actions&index=%d\">", i);
+               snprintf(buf, 150, ".action <a class=\"cmd\" href=\"/show-status?file=actions&amp;index=%d\">", i);
                string_append(&matches, buf);
-               string_append(&matches, "View</a> <a class=\"cmd\" href=\"/edit-actions-list?f=");
+               string_append(&matches, "View</a>");
+#ifdef FEATURE_CGI_EDIT_ACTIONS
+               string_append(&matches, " <a class=\"cmd\" href=\"/edit-actions-list?f=");
                string_join  (&matches, html_encode(csp->config->actions_file_short[i]));
-               string_append(&matches, "\">Edit</a></th></tr>\n");
+               string_append(&matches, "\">Edit</a>");
+#endif
+               string_append(&matches, "</th></tr>\n");
 
                hits = 0;
                b = b->next;
@@ -1112,7 +1363,7 @@ jb_err cgi_show_url_info(struct client_state *csp,
             if (url_match(b->url, url_to_query))
             {
                string_append(&matches, "<tr><td>{");
-               string_join  (&matches, actions_to_html(b->action, csp));
+               string_join  (&matches, actions_to_html(csp, b->action));
                string_append(&matches, " }</b><br>\n<code>");
                string_join  (&matches, html_encode(b->url->spec));
                string_append(&matches, "</code></td></tr>\n");
@@ -1152,7 +1403,7 @@ jb_err cgi_show_url_info(struct client_state *csp,
          return JB_ERR_MEMORY;
       }
 
-      s = current_action_to_html(action, csp);
+      s = current_action_to_html(csp, action);
 
       free_current_action(action);
 
@@ -1265,6 +1516,12 @@ static jb_err show_defines(struct map *exports)
    if (!err) err = map(exports, "FORCE_PREFIX", 1, "(none - disabled)", 1);
 #endif /* ndef FEATURE_FORCE_LOAD */
 
+#ifdef FEATURE_GRACEFUL_TERMINATION
+   if (!err) err = map_conditional(exports, "FEATURE_GRACEFUL_TERMINATION", 1);
+#else /* ifndef FEATURE_GRACEFUL_TERMINATION */
+   if (!err) err = map_conditional(exports, "FEATURE_GRACEFUL_TERMINATION", 0);
+#endif /* ndef FEATURE_GRACEFUL_TERMINATION */
+
 #ifdef FEATURE_IMAGE_BLOCKING
    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 1);
 #else /* ifndef FEATURE_IMAGE_BLOCKING */
@@ -1313,6 +1570,12 @@ static jb_err show_defines(struct map *exports)
    if (!err) err = map_conditional(exports, "FEATURE_TRUST", 0);
 #endif /* ndef FEATURE_TRUST */
 
+#ifdef FEATURE_ZLIB
+   if (!err) err = map_conditional(exports, "FEATURE_ZLIB", 1);
+#else /* ifndef FEATURE_ZLIB */
+   if (!err) err = map_conditional(exports, "FEATURE_ZLIB", 0);
+#endif /* ndef FEATURE_ZLIB */
+
 #ifdef STATIC_PCRE
    if (!err) err = map_conditional(exports, "STATIC_PCRE", 1);
 #else /* ifndef STATIC_PCRE */
@@ -1353,13 +1616,17 @@ static char *show_rcs(void)
 #define SHOW_RCS(__x)              \
    {                               \
       extern const char __x[];     \
-      sprintf(buf, "%s\n", __x);   \
+      snprintf(buf, sizeof(buf), " %s\n", __x);   \
       string_append(&result, buf); \
    }
 
    /* In alphabetical order */
    SHOW_RCS(actions_h_rcs)
    SHOW_RCS(actions_rcs)
+#ifdef AMIGA
+   SHOW_RCS(amiga_h_rcs)
+   SHOW_RCS(amiga_rcs)
+#endif /* def AMIGA */
    SHOW_RCS(cgi_h_rcs)
    SHOW_RCS(cgi_rcs)
 #ifdef FEATURE_CGI_EDIT_ACTIONS