Subversion Repositories Kolibri OS

Rev

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