Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
  3.  *
  4.  * This file is part of NetSurf.
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /* about: URL handling.
  20.  *
  21.  * Based on the data fetcher by Rob Kendrick
  22.  * This fetcher provides a simple scheme for the user to access
  23.  * information from the browser from a known, fixed URL.
  24.  */
  25.  
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <fcntl.h>
  29. #include <unistd.h>
  30. #include <assert.h>
  31. #include <errno.h>
  32. #include <stdbool.h>
  33. #include <inttypes.h>
  34. #include <string.h>
  35. #include <strings.h>
  36. #include <time.h>
  37. #include <stdio.h>
  38. #include <dirent.h>
  39. #include <limits.h>
  40. #include <stdarg.h>
  41.  
  42. #include <libwapcaplet/libwapcaplet.h>
  43.  
  44. #include "utils/config.h"
  45. #include "content/dirlist.h"
  46. #include "content/fetch.h"
  47. #include "content/fetchers/about.h"
  48. #include "content/urldb.h"
  49. #include "desktop/netsurf.h"
  50. #include "desktop/options.h"
  51. #include "utils/log.h"
  52. #include "utils/messages.h"
  53. #include "utils/url.h"
  54. #include "utils/utils.h"
  55. #include "utils/ring.h"
  56. #include "utils/testament.h"
  57. #include "image/image_cache.h"
  58.  
  59. struct fetch_about_context;
  60.  
  61. typedef bool (*fetch_about_handler)(struct fetch_about_context *);
  62.  
  63. /** Context for an about fetch */
  64. struct fetch_about_context {
  65.         struct fetch_about_context *r_next, *r_prev;
  66.  
  67.         struct fetch *fetchh; /**< Handle for this fetch */
  68.  
  69.         bool aborted; /**< Flag indicating fetch has been aborted */
  70.         bool locked; /**< Flag indicating entry is already entered */
  71.  
  72.         nsurl *url; /**< The full url the fetch refers to */
  73.  
  74.         fetch_about_handler handler;
  75. };
  76.  
  77. static struct fetch_about_context *ring = NULL;
  78.  
  79. /** issue fetch callbacks with locking */
  80. static inline bool fetch_about_send_callback(const fetch_msg *msg,
  81.                 struct fetch_about_context *ctx)
  82. {
  83.         ctx->locked = true;
  84.         fetch_send_callback(msg, ctx->fetchh);
  85.         ctx->locked = false;
  86.  
  87.         return ctx->aborted;
  88. }
  89.  
  90. static bool fetch_about_send_header(struct fetch_about_context *ctx,
  91.                 const char *fmt, ...)
  92. {
  93.         char header[64];
  94.         fetch_msg msg;
  95.         va_list ap;
  96.  
  97.         va_start(ap, fmt);
  98.  
  99.         vsnprintf(header, sizeof header, fmt, ap);
  100.  
  101.         va_end(ap);
  102.  
  103.         msg.type = FETCH_HEADER;
  104.         msg.data.header_or_data.buf = (const uint8_t *) header;
  105.         msg.data.header_or_data.len = strlen(header);
  106.  
  107.         fetch_about_send_callback(&msg, ctx);
  108.  
  109.         return ctx->aborted;
  110. }
  111.  
  112.  
  113.  
  114.  
  115. static bool fetch_about_blank_handler(struct fetch_about_context *ctx)
  116. {
  117.         fetch_msg msg;
  118.         const char buffer[2] = { ' ', '\0' };
  119.  
  120.         /* content is going to return ok */
  121.         fetch_set_http_code(ctx->fetchh, 200);
  122.  
  123.         /* content type */
  124.         if (fetch_about_send_header(ctx, "Content-Type: text/html"))
  125.                 goto fetch_about_blank_handler_aborted;
  126.  
  127.         msg.type = FETCH_DATA;
  128.         msg.data.header_or_data.buf = (const uint8_t *) buffer;
  129.         msg.data.header_or_data.len = strlen(buffer);
  130.  
  131.         if (fetch_about_send_callback(&msg, ctx))
  132.                 goto fetch_about_blank_handler_aborted;
  133.  
  134.         msg.type = FETCH_FINISHED;
  135.  
  136.         fetch_about_send_callback(&msg, ctx);
  137.  
  138.         return true;
  139.  
  140. fetch_about_blank_handler_aborted:
  141.         return false;
  142. }
  143.  
  144.  
  145. static bool fetch_about_credits_handler(struct fetch_about_context *ctx)
  146. {
  147.         fetch_msg msg;
  148.  
  149.         /* content is going to return redirect */
  150.         fetch_set_http_code(ctx->fetchh, 302);
  151.  
  152.         msg.type = FETCH_REDIRECT;
  153.         msg.data.redirect = "resource:credits.html";
  154.  
  155.         fetch_about_send_callback(&msg, ctx);
  156.  
  157.         return true;
  158. }
  159.  
  160.  
  161. static bool fetch_about_licence_handler(struct fetch_about_context *ctx)
  162. {
  163.         fetch_msg msg;
  164.  
  165.         /* content is going to return redirect */
  166.         fetch_set_http_code(ctx->fetchh, 302);
  167.  
  168.         msg.type = FETCH_REDIRECT;
  169.         msg.data.redirect = "resource:licence.html";
  170.  
  171.         fetch_about_send_callback(&msg, ctx);
  172.  
  173.         return true;
  174. }
  175.  
  176. /** Handler to generate about:cache page.
  177.  *
  178.  * Shows details of current iamge cache
  179.  *
  180.  */
  181. static bool fetch_about_imagecache_handler(struct fetch_about_context *ctx)
  182. {
  183.         fetch_msg msg;
  184.         char buffer[2048]; /* output buffer */
  185.         int code = 200;
  186.         int slen;
  187.         unsigned int cent_loop = 0;
  188.         int res = 0;
  189.  
  190.         /* content is going to return ok */
  191.         fetch_set_http_code(ctx->fetchh, code);
  192.  
  193.         /* content type */
  194.         if (fetch_about_send_header(ctx, "Content-Type: text/html"))
  195.                 goto fetch_about_imagecache_handler_aborted;
  196.  
  197.         msg.type = FETCH_DATA;
  198.         msg.data.header_or_data.buf = (const uint8_t *) buffer;
  199.  
  200.         /* page head */
  201.         slen = snprintf(buffer, sizeof buffer,
  202.                         "<html>\n<head>\n"
  203.                         "<title>NetSurf Browser Image Cache Status</title>\n"
  204.                         "<link rel=\"stylesheet\" type=\"text/css\" "
  205.                         "href=\"resource:internal.css\">\n"
  206.                         "</head>\n"
  207.                         "<body id =\"cachelist\">\n"
  208.                         "<p class=\"banner\">"
  209.                         "<a href=\"http://www.netsurf-browser.org/\">"
  210.                         "<img src=\"resource:netsurf.png\" alt=\"NetSurf\"></a>"
  211.                         "</p>\n"
  212.                         "<h1>NetSurf Browser Image Cache Status</h1>\n" );
  213.         msg.data.header_or_data.len = slen;
  214.         if (fetch_about_send_callback(&msg, ctx))
  215.                 goto fetch_about_imagecache_handler_aborted;
  216.  
  217.         /* image cache summary */
  218.         slen = image_cache_snsummaryf(buffer, sizeof(buffer),
  219.                 "<p>Configured limit of %a hysteresis of %b</p>\n"
  220.                 "<p>Total bitmap size in use %c (in %d)</p>\n"
  221.                 "<p>Age %es</p>\n"
  222.                 "<p>Peak size %f (in %g)</p>\n"
  223.                 "<p>Peak image count %h (size %i)</p>\n"
  224.                 "<p>Cache total/hit/miss/fail (counts) %j/%k/%l/%m "
  225.                                 "(%pj%%/%pk%%/%pl%%/%pm%%)</p>\n"
  226.                 "<p>Cache total/hit/miss/fail (size) %n/%o/%q/%r "
  227.                                 "(%pn%%/%po%%/%pq%%/%pr%%)</p>\n"
  228.                 "<p>Total images never rendered: %s "
  229.                                 "(includes %t that were converted)</p>\n"
  230.                 "<p>Total number of excessive conversions: %u "
  231.                                 "(from %v images converted more than once)"
  232.                                 "</p>\n"
  233.                 "<p>Bitmap of size %w had most (%x) conversions</p>\n"
  234.                 "<h2>Current image cache contents</h2>\n");
  235.         if (slen >= (int) (sizeof(buffer)))
  236.                 goto fetch_about_imagecache_handler_aborted; /* overflow */
  237.  
  238.         msg.data.header_or_data.len = slen;
  239.         if (fetch_about_send_callback(&msg, ctx))
  240.                 goto fetch_about_imagecache_handler_aborted;
  241.  
  242.  
  243.         /* image cache entry table */
  244.         slen = snprintf(buffer, sizeof buffer,
  245.                         "<p class=\"imagecachelist\">\n"
  246.                         "<strong>"
  247.                         "<span>Entry</span>"
  248.                         "<span>Content Key</span>"
  249.                         "<span>Redraw Count</span>"
  250.                         "<span>Conversion Count</span>"
  251.                         "<span>Last Redraw</span>"
  252.                         "<span>Bitmap Age</span>"
  253.                         "<span>Bitmap Size</span>"
  254.                         "<span>Source</span>"
  255.                         "</strong>\n");
  256.         do {
  257.                 res = image_cache_snentryf(buffer + slen, sizeof buffer - slen,
  258.                                 cent_loop,
  259.                                 "<a href=\"%U\">"
  260.                                 "<span>%e</span>"
  261.                                 "<span>%k</span>"
  262.                                 "<span>%r</span>"
  263.                                 "<span>%c</span>"
  264.                                 "<span>%a</span>"
  265.                                 "<span>%g</span>"
  266.                                 "<span>%s</span>"
  267.                                 "<span>%o</span>"
  268.                                 "</a>\n");
  269.                 if (res <= 0)
  270.                         break; /* last option */
  271.  
  272.                 if (res >= (int) (sizeof buffer - slen)) {
  273.                         /* last entry would not fit in buffer, submit buffer */
  274.                         msg.data.header_or_data.len = slen;
  275.                         if (fetch_about_send_callback(&msg, ctx))
  276.                                 goto fetch_about_imagecache_handler_aborted;
  277.                         slen = 0;
  278.                 } else {
  279.                         /* normal addition */
  280.                         slen += res;
  281.                         cent_loop++;
  282.                 }
  283.         } while (res > 0);
  284.  
  285.         slen += snprintf(buffer + slen, sizeof buffer - slen,
  286.                          "</p>\n</body>\n</html>\n");
  287.  
  288.         msg.data.header_or_data.len = slen;
  289.         if (fetch_about_send_callback(&msg, ctx))
  290.                 goto fetch_about_imagecache_handler_aborted;
  291.  
  292.         msg.type = FETCH_FINISHED;
  293.         fetch_about_send_callback(&msg, ctx);
  294.  
  295.         return true;
  296.  
  297. fetch_about_imagecache_handler_aborted:
  298.         return false;
  299. }
  300.  
  301. /** Handler to generate about:config page */
  302. static bool fetch_about_config_handler(struct fetch_about_context *ctx)
  303. {
  304.         fetch_msg msg;
  305.         char buffer[1024];
  306.         int code = 200;
  307.         int slen;
  308.         unsigned int opt_loop = 0;
  309.         int res = 0;
  310.  
  311.         /* content is going to return ok */
  312.         fetch_set_http_code(ctx->fetchh, code);
  313.  
  314.         /* content type */
  315.         if (fetch_about_send_header(ctx, "Content-Type: text/html"))
  316.                 goto fetch_about_config_handler_aborted;
  317.  
  318.         msg.type = FETCH_DATA;
  319.         msg.data.header_or_data.buf = (const uint8_t *) buffer;
  320.  
  321.         slen = snprintf(buffer, sizeof buffer,
  322.                         "<html>\n<head>\n"
  323.                         "<title>NetSurf Browser Config</title>\n"
  324.                         "<link rel=\"stylesheet\" type=\"text/css\" "
  325.                         "href=\"resource:internal.css\">\n"
  326.                         "</head>\n"
  327.                         "<body id =\"configlist\">\n"
  328.                         "<p class=\"banner\">"
  329.                         "<a href=\"http://www.netsurf-browser.org/\">"
  330.                         "<img src=\"resource:netsurf.png\" alt=\"NetSurf\"></a>"
  331.                         "</p>\n"
  332.                         "<h1>NetSurf Browser Config</h1>\n"
  333.                         "<table class=\"config\">\n"
  334.                         "<tr><th></th><th></th><th></th></tr>\n");
  335.  
  336.         do {
  337.                 res = nsoption_snoptionf(buffer + slen, sizeof buffer - slen,
  338.                                 opt_loop,
  339.                                 "<tr><th>%k</th><td>%t</td><td>%V</td></tr>\n");
  340.                 if (res <= 0)
  341.                         break; /* last option */
  342.  
  343.                 if (res >= (int) (sizeof buffer - slen)) {
  344.                         /* last entry would not fit in buffer, submit buffer */
  345.                         msg.data.header_or_data.len = slen;
  346.                         if (fetch_about_send_callback(&msg, ctx))
  347.                                 goto fetch_about_config_handler_aborted;
  348.                         slen = 0;
  349.                 } else {
  350.                         /* normal addition */
  351.                         slen += res;
  352.                         opt_loop++;
  353.                 }
  354.         } while (res > 0);
  355.  
  356.         slen += snprintf(buffer + slen, sizeof buffer - slen,
  357.                          "</table>\n</body>\n</html>\n");
  358.  
  359.         msg.data.header_or_data.len = slen;
  360.         if (fetch_about_send_callback(&msg, ctx))
  361.                 goto fetch_about_config_handler_aborted;
  362.  
  363.         msg.type = FETCH_FINISHED;
  364.         fetch_about_send_callback(&msg, ctx);
  365.  
  366.         return true;
  367.  
  368. fetch_about_config_handler_aborted:
  369.         return false;
  370. }
  371.  
  372.  
  373. /** Generate the text of a Choices file which represents the current
  374.  * in use options.
  375.  */
  376. static bool fetch_about_choices_handler(struct fetch_about_context *ctx)
  377. {
  378.         fetch_msg msg;
  379.         char buffer[1024];
  380.         int code = 200;
  381.         int slen;
  382.         unsigned int opt_loop = 0;
  383.         int res = 0;
  384.  
  385.         /* content is going to return ok */
  386.         fetch_set_http_code(ctx->fetchh, code);
  387.  
  388.         /* content type */
  389.         if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
  390.                 goto fetch_about_choices_handler_aborted;
  391.  
  392.         msg.type = FETCH_DATA;
  393.         msg.data.header_or_data.buf = (const uint8_t *) buffer;
  394.  
  395.         slen = snprintf(buffer, sizeof buffer,
  396.                  "# Automatically generated current NetSurf browser Choices\n");
  397.  
  398.         do {
  399.                 res = nsoption_snoptionf(buffer + slen,
  400.                                 sizeof buffer - slen,
  401.                                 opt_loop,
  402.                                 "%k:%v\n");
  403.                 if (res <= 0)
  404.                         break; /* last option */
  405.  
  406.                 if (res >= (int) (sizeof buffer - slen)) {
  407.                         /* last entry would not fit in buffer, submit buffer */
  408.                         msg.data.header_or_data.len = slen;
  409.                         if (fetch_about_send_callback(&msg, ctx))
  410.                                 goto fetch_about_choices_handler_aborted;
  411.                         slen = 0;
  412.                 } else {
  413.                         /* normal addition */
  414.                         slen += res;
  415.                         opt_loop++;
  416.                 }
  417.         } while (res > 0);
  418.  
  419.         msg.data.header_or_data.len = slen;
  420.         if (fetch_about_send_callback(&msg, ctx))
  421.                 goto fetch_about_choices_handler_aborted;
  422.  
  423.         msg.type = FETCH_FINISHED;
  424.         fetch_about_send_callback(&msg, ctx);
  425.  
  426.         return true;
  427.  
  428. fetch_about_choices_handler_aborted:
  429.         return false;
  430. }
  431.  
  432. /** Generate the text of an svn testament which represents the current
  433.  * build-tree status
  434.  */
  435. typedef struct { const char *leaf; const char *modtype; } modification_t;
  436. static bool fetch_about_testament_handler(struct fetch_about_context *ctx)
  437. {
  438.         static modification_t modifications[] = WT_MODIFICATIONS;
  439.         fetch_msg msg;
  440.         char buffer[1024];
  441.         int code = 200;
  442.         int slen;
  443.         int i;
  444.        
  445.  
  446.         /* content is going to return ok */
  447.         fetch_set_http_code(ctx->fetchh, code);
  448.  
  449.         /* content type */
  450.         if (fetch_about_send_header(ctx, "Content-Type: text/plain"))
  451.                 goto fetch_about_testament_handler_aborted;
  452.  
  453.         msg.type = FETCH_DATA;
  454.         msg.data.header_or_data.buf = (const uint8_t *) buffer;
  455.  
  456.         slen = snprintf(buffer, sizeof buffer,
  457.                  "# Automatically generated by NetSurf build system\n\n");
  458.  
  459.         msg.data.header_or_data.len = slen;
  460.         if (fetch_about_send_callback(&msg, ctx))
  461.                 goto fetch_about_testament_handler_aborted;
  462.        
  463.         slen = snprintf(buffer, sizeof buffer,
  464. #if defined(WT_BRANCHISTRUNK) || defined(WT_BRANCHISMASTER)
  465.                         "# This is a *DEVELOPMENT* build from the main line.\n\n"
  466. #elif defined(WT_BRANCHISTAG) && (WT_MODIFIED == 0)
  467.                         "# This is a tagged build of NetSurf\n"
  468. #ifdef WT_TAGIS
  469.                         "#      The tag used was '" WT_TAGIS "'\n\n"
  470. #else
  471.                         "\n"
  472. #endif
  473. #elif defined(WT_NO_SVN) || defined(WT_NO_GIT)
  474.                         "# This NetSurf was built outside of our revision "
  475.                         "control environment.\n"
  476.                         "# This testament is therefore very useful.\n\n"
  477. #else
  478.                         "# This NetSurf was built from a branch (" WT_BRANCHPATH ").\n\n"
  479. #endif
  480. #if defined(CI_BUILD)
  481.                         "# This build carries the CI build number '" CI_BUILD "'\n\n"
  482. #endif
  483.                         );
  484.  
  485.         msg.data.header_or_data.len = slen;    
  486.         if (fetch_about_send_callback(&msg, ctx))
  487.                 goto fetch_about_testament_handler_aborted;
  488.  
  489.        
  490.         slen = snprintf(buffer, sizeof buffer,
  491.                         "Built by %s (%s) from %s at revision %s\n\n",
  492.                         GECOS, USERNAME, WT_BRANCHPATH, WT_REVID);
  493.  
  494.         msg.data.header_or_data.len = slen;
  495.         if (fetch_about_send_callback(&msg, ctx))
  496.                 goto fetch_about_testament_handler_aborted;
  497.        
  498.         slen = snprintf(buffer, sizeof buffer,
  499.                         "Built on %s in %s\n\n",
  500.                         WT_HOSTNAME, WT_ROOT);
  501.  
  502.         msg.data.header_or_data.len = slen;
  503.         if (fetch_about_send_callback(&msg, ctx))
  504.                 goto fetch_about_testament_handler_aborted;
  505.        
  506.         if (WT_MODIFIED > 0) {
  507.                 slen = snprintf(buffer, sizeof buffer,
  508.                                 "Working tree has %d modification%s\n\n",
  509.                                 WT_MODIFIED, WT_MODIFIED == 1 ? "" : "s");
  510.         } else {
  511.                 slen = snprintf(buffer, sizeof buffer,
  512.                                 "Working tree is not modified.\n");
  513.         }
  514.  
  515.         msg.data.header_or_data.len = slen;
  516.         if (fetch_about_send_callback(&msg, ctx))
  517.                 goto fetch_about_testament_handler_aborted;
  518.        
  519.         for (i = 0; i < WT_MODIFIED; ++i) {
  520.                 slen = snprintf(buffer, sizeof buffer,
  521.                                 "  %s  %s\n",
  522.                                 modifications[i].modtype,
  523.                                 modifications[i].leaf);
  524.                 msg.data.header_or_data.len = slen;
  525.                 if (fetch_about_send_callback(&msg, ctx))
  526.                         goto fetch_about_testament_handler_aborted;
  527.                
  528.         }
  529.  
  530.         msg.type = FETCH_FINISHED;     
  531.         fetch_about_send_callback(&msg, ctx);
  532.  
  533.         return true;
  534.  
  535. fetch_about_testament_handler_aborted:
  536.         return false;
  537. }
  538.  
  539. static bool fetch_about_logo_handler(struct fetch_about_context *ctx)
  540. {
  541.         fetch_msg msg;
  542.  
  543.         /* content is going to return redirect */
  544.         fetch_set_http_code(ctx->fetchh, 302);
  545.  
  546.         msg.type = FETCH_REDIRECT;
  547.         msg.data.redirect = "resource:netsurf.png";
  548.  
  549.         fetch_about_send_callback(&msg, ctx);
  550.  
  551.         return true;
  552. }
  553.  
  554. static bool fetch_about_welcome_handler(struct fetch_about_context *ctx)
  555. {
  556.         fetch_msg msg;
  557.  
  558.         /* content is going to return redirect */
  559.         fetch_set_http_code(ctx->fetchh, 302);
  560.  
  561.         msg.type = FETCH_REDIRECT;
  562.         msg.data.redirect = "resource:welcome.html";
  563.  
  564.         fetch_about_send_callback(&msg, ctx);
  565.  
  566.         return true;
  567. }
  568.  
  569. /* Forward declaration because this handler requires the handler table. */
  570. static bool fetch_about_about_handler(struct fetch_about_context *ctx);
  571.  
  572. struct about_handlers {
  573.         const char *name; /**< name to match in url */
  574.         int name_len;
  575.         lwc_string *lname; /**< Interned name */
  576.         fetch_about_handler handler; /* handler for the url */
  577.         bool hidden; /* Flag indicating if entry should show in listing */
  578. };
  579.  
  580. /** List of about paths and their handlers */
  581. struct about_handlers about_handler_list[] = {
  582.         { "credits", SLEN("credits"), NULL,
  583.                         fetch_about_credits_handler, false },
  584.         { "licence", SLEN("licence"), NULL,
  585.                         fetch_about_licence_handler, false },
  586.         { "license", SLEN("license"), NULL,
  587.                         fetch_about_licence_handler, true },
  588.         { "welcome", SLEN("welcome"), NULL,
  589.                         fetch_about_welcome_handler, false },
  590.         { "config", SLEN("config"), NULL,
  591.                         fetch_about_config_handler, false },
  592.         { "Choices", SLEN("Choices"), NULL,
  593.                         fetch_about_choices_handler, false },
  594.         { "testament", SLEN("testament"), NULL,
  595.                         fetch_about_testament_handler, false },
  596.         { "about", SLEN("about"), NULL,
  597.                         fetch_about_about_handler, true },
  598.         { "logo", SLEN("logo"), NULL,
  599.                         fetch_about_logo_handler, true },
  600.         /* details about the image cache */
  601.         { "imagecache", SLEN("imagecache"), NULL,
  602.                         fetch_about_imagecache_handler, true },
  603.         /* The default blank page */
  604.         { "blank", SLEN("blank"), NULL,
  605.                         fetch_about_blank_handler, true }
  606. };
  607.  
  608. #define about_handler_list_len (sizeof(about_handler_list) /            \
  609.                 sizeof(struct about_handlers))
  610.  
  611. /**
  612.  * List all the valid about: paths available
  613.  *
  614.  * \param ctx The fetch context.
  615.  * \return true for sucess or false to generate an error.
  616.  */
  617. static bool fetch_about_about_handler(struct fetch_about_context *ctx)
  618. {
  619.         fetch_msg msg;
  620.         char buffer[1024];
  621.         int code = 200;
  622.         int slen;
  623.         unsigned int abt_loop = 0;
  624.         int res = 0;
  625.  
  626.         /* content is going to return ok */
  627.         fetch_set_http_code(ctx->fetchh, code);
  628.  
  629.         /* content type */
  630.         if (fetch_about_send_header(ctx, "Content-Type: text/html"))
  631.                 goto fetch_about_config_handler_aborted;
  632.  
  633.         msg.type = FETCH_DATA;
  634.         msg.data.header_or_data.buf = (const uint8_t *) buffer;
  635.  
  636.         slen = snprintf(buffer, sizeof buffer,
  637.                         "<html>\n<head>\n"
  638.                         "<title>NetSurf List of About pages</title>\n"
  639.                         "<link rel=\"stylesheet\" type=\"text/css\" "
  640.                         "href=\"resource:internal.css\">\n"
  641.                         "</head>\n"
  642.                         "<body id =\"aboutlist\">\n"
  643.                         "<p class=\"banner\">"
  644.                         "<a href=\"http://www.netsurf-browser.org/\">"
  645.                         "<img src=\"resource:netsurf.png\" alt=\"NetSurf\"></a>"
  646.                         "</p>\n"
  647.                         "<h1>NetSurf List of About pages</h1>\n"
  648.                         "<ul>\n");
  649.  
  650.         for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
  651.  
  652.                 /* Skip over hidden entries */
  653.                 if (about_handler_list[abt_loop].hidden)
  654.                         continue;
  655.  
  656.                 res = snprintf(buffer + slen, sizeof buffer - slen,
  657.                                "<li><a href=\"about:%s\">about:%s</a></li>\n",
  658.                                about_handler_list[abt_loop].name,
  659.                                about_handler_list[abt_loop].name);
  660.                 if (res <= 0)
  661.                         break; /* last option */
  662.  
  663.                 if (res >= (int)(sizeof buffer - slen)) {
  664.                         /* last entry would not fit in buffer, submit buffer */
  665.                         msg.data.header_or_data.len = slen;
  666.                         if (fetch_about_send_callback(&msg, ctx))
  667.                                 goto fetch_about_config_handler_aborted;
  668.                         slen = 0;
  669.                 } else {
  670.                         /* normal addition */
  671.                         slen += res;
  672.                 }
  673.         }
  674.  
  675.         slen += snprintf(buffer + slen, sizeof buffer - slen,
  676.                          "</ul>\n</body>\n</html>\n");
  677.  
  678.         msg.data.header_or_data.len = slen;
  679.         if (fetch_about_send_callback(&msg, ctx))
  680.                 goto fetch_about_config_handler_aborted;
  681.  
  682.         msg.type = FETCH_FINISHED;
  683.         fetch_about_send_callback(&msg, ctx);
  684.  
  685.         return true;
  686.  
  687. fetch_about_config_handler_aborted:
  688.         return false;
  689. }
  690.  
  691.  
  692. /** callback to initialise the about fetcher. */
  693. static bool fetch_about_initialise(lwc_string *scheme)
  694. {
  695.         unsigned int abt_loop = 0;
  696.         lwc_error error;
  697.  
  698.         for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
  699.                 error = lwc_intern_string(about_handler_list[abt_loop].name,
  700.                                         about_handler_list[abt_loop].name_len,
  701.                                         &about_handler_list[abt_loop].lname);
  702.                 if (error != lwc_error_ok) {
  703.                         while (abt_loop-- != 0) {
  704.                                 lwc_string_unref(about_handler_list[abt_loop].lname);
  705.                         }
  706.                         return false;
  707.                 }
  708.         }
  709.  
  710.         return true;
  711. }
  712.  
  713. /** callback to finalise the about fetcher. */
  714. static void fetch_about_finalise(lwc_string *scheme)
  715. {
  716.         unsigned int abt_loop = 0;
  717.         for (abt_loop = 0; abt_loop < about_handler_list_len; abt_loop++) {
  718.                 lwc_string_unref(about_handler_list[abt_loop].lname);
  719.         }
  720. }
  721.  
  722. static bool fetch_about_can_fetch(const nsurl *url)
  723. {
  724.         return true;
  725. }
  726.  
  727. /** callback to set up a about fetch context. */
  728. static void *
  729. fetch_about_setup(struct fetch *fetchh,
  730.                  nsurl *url,
  731.                  bool only_2xx,
  732.                  bool downgrade_tls,
  733.                  const char *post_urlenc,
  734.                  const struct fetch_multipart_data *post_multipart,
  735.                  const char **headers)
  736. {
  737.         struct fetch_about_context *ctx;
  738.         unsigned int handler_loop;
  739.         lwc_string *path;
  740.         bool match;
  741.  
  742.         ctx = calloc(1, sizeof(*ctx));
  743.         if (ctx == NULL)
  744.                 return NULL;
  745.  
  746.         path = nsurl_get_component(url, NSURL_PATH);
  747.  
  748.         for (handler_loop = 0;
  749.              handler_loop < about_handler_list_len;
  750.              handler_loop++) {
  751.                 ctx->handler = about_handler_list[handler_loop].handler;
  752.                 if (lwc_string_isequal(path,
  753.                                        about_handler_list[handler_loop].lname,
  754.                                        &match) == lwc_error_ok && match) {
  755.                         break;
  756.                 }              
  757.         }
  758.  
  759.         if (path != NULL)
  760.                 lwc_string_unref(path);
  761.  
  762.         ctx->fetchh = fetchh;
  763.         ctx->url = nsurl_ref(url);
  764.  
  765.         RING_INSERT(ring, ctx);
  766.  
  767.         return ctx;
  768. }
  769.  
  770. /** callback to free a about fetch */
  771. static void fetch_about_free(void *ctx)
  772. {
  773.         struct fetch_about_context *c = ctx;
  774.         nsurl_unref(c->url);
  775.         RING_REMOVE(ring, c);
  776.         free(ctx);
  777. }
  778.  
  779. /** callback to start a about fetch */
  780. static bool fetch_about_start(void *ctx)
  781. {
  782.         return true;
  783. }
  784.  
  785. /** callback to abort a about fetch */
  786. static void fetch_about_abort(void *ctx)
  787. {
  788.         struct fetch_about_context *c = ctx;
  789.  
  790.         /* To avoid the poll loop having to deal with the fetch context
  791.          * disappearing from under it, we simply flag the abort here.
  792.          * The poll loop itself will perform the appropriate cleanup.
  793.          */
  794.         c->aborted = true;
  795. }
  796.  
  797.  
  798. /** callback to poll for additional about fetch contents */
  799. static void fetch_about_poll(lwc_string *scheme)
  800. {
  801.         struct fetch_about_context *c, *next;
  802.  
  803.         if (ring == NULL) return;
  804.  
  805.         /* Iterate over ring, processing each pending fetch */
  806.         c = ring;
  807.         do {
  808.                 /* Ignore fetches that have been flagged as locked.
  809.                  * This allows safe re-entrant calls to this function.
  810.                  * Re-entrancy can occur if, as a result of a callback,
  811.                  * the interested party causes fetch_poll() to be called
  812.                  * again.
  813.                  */
  814.                 if (c->locked == true) {
  815.                         next = c->r_next;
  816.                         continue;
  817.                 }
  818.  
  819.                 /* Only process non-aborted fetches */
  820.                 if (c->aborted == false) {
  821.                         /* about fetches can be processed in one go */
  822.                         c->handler(c);
  823.                 }
  824.  
  825.                 /* Compute next fetch item at the last possible moment
  826.                  * as processing this item may have added to the ring
  827.                  */
  828.                 next = c->r_next;
  829.  
  830.                 fetch_remove_from_queues(c->fetchh);
  831.                 fetch_free(c->fetchh);
  832.  
  833.                 /* Advance to next ring entry, exiting if we've reached
  834.                  * the start of the ring or the ring has become empty
  835.                  */
  836.         } while ( (c = next) != ring && ring != NULL);
  837. }
  838.  
  839. void fetch_about_register(void)
  840. {
  841.         lwc_string *scheme;
  842.  
  843.         if (lwc_intern_string("about", SLEN("about"),
  844.                         &scheme) != lwc_error_ok) {
  845.                 die("Failed to initialise the fetch module "
  846.                                 "(couldn't intern \"about\").");
  847.         }
  848.  
  849.         fetch_add_fetcher(scheme,
  850.                 fetch_about_initialise,
  851.                 fetch_about_can_fetch,
  852.                 fetch_about_setup,
  853.                 fetch_about_start,
  854.                 fetch_about_abort,
  855.                 fetch_about_free,
  856.                 fetch_about_poll,
  857.                 fetch_about_finalise);
  858. }
  859.