Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2008 Rob Kendrick <rjek@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. /* data: URL handling.  See http://tools.ietf.org/html/rfc2397 */
  20.  
  21. #include <assert.h>
  22. #include <errno.h>
  23. #include <stdbool.h>
  24. #include <string.h>
  25. #include <strings.h>
  26. #include <time.h>
  27.  
  28. #include <curl/curl.h>          /* for URL unescaping functions */
  29.  
  30. #include <libwapcaplet/libwapcaplet.h>
  31.  
  32. #include "utils/config.h"
  33. #include "content/fetch.h"
  34. #include "content/fetchers/data.h"
  35. #include "content/urldb.h"
  36. #include "desktop/netsurf.h"
  37. #include "desktop/options.h"
  38. #include "utils/log.h"
  39. #include "utils/messages.h"
  40. #include "utils/url.h"
  41. #include "utils/utils.h"
  42. #include "utils/ring.h"
  43. #include "utils/base64.h"
  44.  
  45. struct fetch_data_context {
  46.         struct fetch *parent_fetch;
  47.         char *url;
  48.         char *mimetype;
  49.         char *data;
  50.         size_t datalen;
  51.         bool base64;
  52.  
  53.         bool aborted;
  54.         bool locked;
  55.        
  56.         struct fetch_data_context *r_next, *r_prev;
  57. };
  58.  
  59. static struct fetch_data_context *ring = NULL;
  60.  
  61. static CURL *curl;
  62.  
  63. static bool fetch_data_initialise(lwc_string *scheme)
  64. {
  65.         LOG(("fetch_data_initialise called for %s", lwc_string_data(scheme)));
  66.         if ( (curl = curl_easy_init()) == NULL)
  67.                 return false;
  68.         else
  69.                 return true;
  70. }
  71.  
  72. static void fetch_data_finalise(lwc_string *scheme)
  73. {
  74.         LOG(("fetch_data_finalise called for %s", lwc_string_data(scheme)));
  75.         curl_easy_cleanup(curl);
  76. }
  77.  
  78. static bool fetch_data_can_fetch(const nsurl *url)
  79. {
  80.         return true;
  81. }
  82.  
  83. static void *fetch_data_setup(struct fetch *parent_fetch, nsurl *url,
  84.                  bool only_2xx, bool downgrade_tls, const char *post_urlenc,
  85.                  const struct fetch_multipart_data *post_multipart,
  86.                  const char **headers)
  87. {
  88.         struct fetch_data_context *ctx = calloc(1, sizeof(*ctx));
  89.        
  90.         if (ctx == NULL)
  91.                 return NULL;
  92.                
  93.         ctx->parent_fetch = parent_fetch;
  94.  
  95.         /* TODO: keep as nsurl to avoid copy */
  96.         ctx->url = malloc(nsurl_length(url) + 1);
  97.         if (ctx->url == NULL) {
  98.                 free(ctx);
  99.                 return NULL;
  100.         }
  101.         memcpy(ctx->url, nsurl_access(url), nsurl_length(url) + 1);
  102.  
  103.         RING_INSERT(ring, ctx);
  104.        
  105.         return ctx;
  106. }
  107.  
  108. static bool fetch_data_start(void *ctx)
  109. {
  110.         return true;
  111. }
  112.  
  113. static void fetch_data_free(void *ctx)
  114. {
  115.         struct fetch_data_context *c = ctx;
  116.  
  117.         free(c->url);
  118.         free(c->data);
  119.         free(c->mimetype);
  120.         RING_REMOVE(ring, c);
  121.         free(ctx);
  122. }
  123.  
  124. static void fetch_data_abort(void *ctx)
  125. {
  126.         struct fetch_data_context *c = ctx;
  127.  
  128.         /* To avoid the poll loop having to deal with the fetch context
  129.          * disappearing from under it, we simply flag the abort here.
  130.          * The poll loop itself will perform the appropriate cleanup.
  131.          */
  132.         c->aborted = true;
  133. }
  134.  
  135. static void fetch_data_send_callback(const fetch_msg *msg,
  136.                 struct fetch_data_context *c)
  137. {
  138.         c->locked = true;
  139.         fetch_send_callback(msg, c->parent_fetch);
  140.         c->locked = false;
  141. }
  142.  
  143. static bool fetch_data_process(struct fetch_data_context *c)
  144. {
  145.         fetch_msg msg;
  146.         char *params;
  147.         char *comma;
  148.         char *unescaped;
  149.         int templen;
  150.        
  151.         /* format of a data: URL is:
  152.          *   data:[<mimetype>][;base64],<data>
  153.          * The mimetype is optional.  If it is missing, the , before the
  154.          * data must still be there.
  155.          */
  156.        
  157.         LOG(("url: %.140s", c->url));
  158.        
  159.         if (strlen(c->url) < 6) {
  160.                 /* 6 is the minimum possible length (data:,) */
  161.                 msg.type = FETCH_ERROR;
  162.                 msg.data.error = "Malformed data: URL";
  163.                 fetch_data_send_callback(&msg, c);
  164.                 return false;
  165.         }
  166.        
  167.         /* skip the data: part */
  168.         params = c->url + SLEN("data:");
  169.        
  170.         /* find the comma */
  171.         if ( (comma = strchr(params, ',')) == NULL) {
  172.                 msg.type = FETCH_ERROR;
  173.                 msg.data.error = "Malformed data: URL";
  174.                 fetch_data_send_callback(&msg, c);
  175.                 return false;
  176.         }
  177.        
  178.         if (params[0] == ',') {
  179.                 /* there is no mimetype here, assume text/plain */
  180.                 c->mimetype = strdup("text/plain;charset=US-ASCII");
  181.         } else {       
  182.                 /* make a copy of everything between data: and the comma */
  183.                 c->mimetype = strndup(params, comma - params);
  184.         }
  185.        
  186.         if (c->mimetype == NULL) {
  187.                 msg.type = FETCH_ERROR;
  188.                 msg.data.error =
  189.                         "Unable to allocate memory for mimetype in data: URL";
  190.                 fetch_data_send_callback(&msg, c);
  191.                 return false;
  192.         }
  193.        
  194.         if (strcmp(c->mimetype + strlen(c->mimetype) - 7, ";base64") == 0) {
  195.                 c->base64 = true;
  196.                 c->mimetype[strlen(c->mimetype) - 7] = '\0';
  197.         } else {
  198.                 c->base64 = false;
  199.         }
  200.        
  201.         /* we URL unescape the data first, just incase some insane page
  202.          * decides to nest URL and base64 encoding.  Like, say, Acid2.
  203.          */
  204.         templen = c->datalen;
  205.         unescaped = curl_easy_unescape(curl, comma + 1, 0, &templen);
  206.         c->datalen = templen;
  207.         if (unescaped == NULL) {
  208.                 msg.type = FETCH_ERROR;
  209.                 msg.data.error = "Unable to URL decode data: URL";
  210.                 fetch_data_send_callback(&msg, c);
  211.                 return false;
  212.         }
  213.        
  214.         if (c->base64) {
  215.                 c->data = malloc(c->datalen); /* safe: always gets smaller */
  216.                 if (base64_decode(unescaped, c->datalen, c->data,
  217.                                 &(c->datalen)) == false) {
  218.                         msg.type = FETCH_ERROR;
  219.                         msg.data.error = "Unable to Base64 decode data: URL";
  220.                         fetch_data_send_callback(&msg, c);
  221.                         curl_free(unescaped);
  222.                         return false;
  223.                 }
  224.         } else {
  225.                 c->data = malloc(c->datalen);
  226.                 if (c->data == NULL) {
  227.                         msg.type = FETCH_ERROR;
  228.                         msg.data.error =
  229.                                 "Unable to allocate memory for data: URL";
  230.                         fetch_data_send_callback(&msg, c);
  231.                         curl_free(unescaped);
  232.                         return false;
  233.                 }
  234.                 memcpy(c->data, unescaped, c->datalen);
  235.         }
  236.        
  237.         curl_free(unescaped);
  238.        
  239.         return true;
  240. }
  241.  
  242. static void fetch_data_poll(lwc_string *scheme)
  243. {
  244.         fetch_msg msg;
  245.         struct fetch_data_context *c, *next;
  246.        
  247.         if (ring == NULL) return;
  248.        
  249.         /* Iterate over ring, processing each pending fetch */
  250.         c = ring;
  251.         do {
  252.                 /* Ignore fetches that have been flagged as locked.
  253.                  * This allows safe re-entrant calls to this function.
  254.                  * Re-entrancy can occur if, as a result of a callback,
  255.                  * the interested party causes fetch_poll() to be called
  256.                  * again.
  257.                  */
  258.                 if (c->locked == true) {
  259.                         next = c->r_next;
  260.                         continue;
  261.                 }
  262.  
  263.                 /* Only process non-aborted fetches */
  264.                 if (c->aborted == false && fetch_data_process(c) == true) {
  265.                         char header[64];
  266.  
  267.                         fetch_set_http_code(c->parent_fetch, 200);
  268.                         LOG(("setting data: MIME type to %s, length to %zd",
  269.                                         c->mimetype, c->datalen));
  270.                         /* Any callback can result in the fetch being aborted.
  271.                          * Therefore, we _must_ check for this after _every_
  272.                          * call to fetch_data_send_callback().
  273.                          */
  274.                         snprintf(header, sizeof header, "Content-Type: %s",
  275.                                         c->mimetype);
  276.                         msg.type = FETCH_HEADER;
  277.                         msg.data.header_or_data.buf = (const uint8_t *) header;
  278.                         msg.data.header_or_data.len = strlen(header);
  279.                         fetch_data_send_callback(&msg, c);
  280.  
  281.                         if (c->aborted == false) {
  282.                                 snprintf(header, sizeof header,
  283.                                         "Content-Length: %"SSIZET_FMT,
  284.                                         c->datalen);
  285.                                 msg.type = FETCH_HEADER;
  286.                                 msg.data.header_or_data.buf =
  287.                                                 (const uint8_t *) header;
  288.                                 msg.data.header_or_data.len = strlen(header);
  289.                                 fetch_data_send_callback(&msg, c);
  290.                         }
  291.  
  292.                         if (c->aborted == false) {
  293.                                 msg.type = FETCH_DATA;
  294.                                 msg.data.header_or_data.buf =
  295.                                                 (const uint8_t *) c->data;
  296.                                 msg.data.header_or_data.len = c->datalen;
  297.                                 fetch_data_send_callback(&msg, c);
  298.                         }
  299.  
  300.                         if (c->aborted == false) {
  301.                                 msg.type = FETCH_FINISHED;
  302.                                 fetch_data_send_callback(&msg, c);
  303.                         }
  304.                 } else {
  305.                         LOG(("Processing of %s failed!", c->url));
  306.  
  307.                         /* Ensure that we're unlocked here. If we aren't,
  308.                          * then fetch_data_process() is broken.
  309.                          */
  310.                         assert(c->locked == false);
  311.                 }
  312.  
  313.                 /* Compute next fetch item at the last possible moment as
  314.                  * processing this item may have added to the ring.
  315.                  */
  316.                 next = c->r_next;
  317.  
  318.                 fetch_remove_from_queues(c->parent_fetch);
  319.                 fetch_free(c->parent_fetch);
  320.  
  321.                 /* Advance to next ring entry, exiting if we've reached
  322.                  * the start of the ring or the ring has become empty
  323.                  */
  324.         } while ( (c = next) != ring && ring != NULL);
  325. }
  326.  
  327. void fetch_data_register(void)
  328. {
  329.         lwc_string *scheme;
  330.  
  331.         if (lwc_intern_string("data", SLEN("data"), &scheme) != lwc_error_ok) {
  332.                 die("Failed to initialise the fetch module "
  333.                                 "(couldn't intern \"data\").");
  334.         }
  335.  
  336.         fetch_add_fetcher(scheme,
  337.                 fetch_data_initialise,
  338.                 fetch_data_can_fetch,
  339.                 fetch_data_setup,
  340.                 fetch_data_start,
  341.                 fetch_data_abort,
  342.                 fetch_data_free,
  343.                 fetch_data_poll,
  344.                 fetch_data_finalise);
  345. }
  346.