NetSurf
data.c
Go to the documentation of this file.
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/**
20 * \file
21 * data scheme handling. See http://tools.ietf.org/html/rfc2397
22 */
23
24#include <stdbool.h>
25#include <string.h>
26#include <stdarg.h>
27#include <stdlib.h>
28#include <libwapcaplet/libwapcaplet.h>
29#include <nsutils/base64.h>
30
31#include "netsurf/inttypes.h"
32#include "utils/url.h"
33#include "utils/nsurl.h"
34#include "utils/corestrings.h"
35#include "utils/log.h"
36#include "utils/utils.h"
37#include "utils/ring.h"
38
39#include "content/fetch.h"
40#include "content/fetchers.h"
42
46 char *mimetype;
47 char *data;
48 size_t datalen;
49 bool base64;
50
51 bool aborted;
52 bool locked;
53
55};
56
57static struct fetch_data_context *ring = NULL;
58
59static bool fetch_data_initialise(lwc_string *scheme)
60{
61 NSLOG(netsurf, INFO, "fetch_data_initialise called for %s",
62 lwc_string_data(scheme));
63
64 return true;
65}
66
67static void fetch_data_finalise(lwc_string *scheme)
68{
69 NSLOG(netsurf, INFO, "fetch_data_finalise called for %s",
70 lwc_string_data(scheme));
71}
72
73static bool fetch_data_can_fetch(const nsurl *url)
74{
75 return true;
76}
77
78static void fetch_data_send_callback(const fetch_msg *msg,
79 struct fetch_data_context *c)
80{
81 c->locked = true;
83 c->locked = false;
84}
85
87 const char *fmt, ...)
88{
89 char header[64];
90 fetch_msg msg;
91 va_list ap;
92 int len;
93
94 va_start(ap, fmt);
95 len = vsnprintf(header, sizeof(header), fmt, ap);
96 va_end(ap);
97
98 if (len >= (int)sizeof(header) || len < 0) {
99 return;
100 }
101
102 msg.type = FETCH_HEADER;
103 msg.data.header_or_data.len = len;
104 msg.data.header_or_data.buf = (const uint8_t *)header;
105 fetch_data_send_callback(&msg, ctx);
106}
107
109 bool only_2xx, bool downgrade_tls, const char *post_urlenc,
110 const struct fetch_multipart_data *post_multipart,
111 const char **headers)
112{
113 struct fetch_data_context *ctx = calloc(1, sizeof(*ctx));
114
115 if (ctx == NULL)
116 return NULL;
117
119 ctx->url = nsurl_ref(url);
120
121 RING_INSERT(ring, ctx);
122
123 return ctx;
124}
125
126static bool fetch_data_start(void *ctx)
127{
128 return true;
129}
130
131static void fetch_data_free(void *ctx)
132{
133 struct fetch_data_context *c = ctx;
134
135 nsurl_unref(c->url);
136 free(c->data);
137 free(c->mimetype);
138 free(ctx);
139}
140
141static void fetch_data_abort(void *ctx)
142{
143 struct fetch_data_context *c = ctx;
144
145 /* To avoid the poll loop having to deal with the fetch context
146 * disappearing from under it, we simply flag the abort here.
147 * The poll loop itself will perform the appropriate cleanup.
148 */
149 c->aborted = true;
150}
151
153{
154 nserror res;
155 fetch_msg msg;
156 const char *params;
157 const char *comma;
158 char *unescaped;
159 size_t unescaped_len;
160
161 /* format of a data: URL is:
162 * data:[<mimetype>][;base64],<data>
163 * The mimetype is optional. If it is missing, the , before the
164 * data must still be there.
165 */
166
167 NSLOG(netsurf, DEEPDEBUG, "url: %.140s", nsurl_access(c->url));
168
169 if (nsurl_length(c->url) < 6) {
170 /* 6 is the minimum possible length (data:,) */
171 msg.type = FETCH_ERROR;
172 msg.data.error = "Malformed data: URL";
174 return false;
175 }
176
177 /* skip the data: part */
178 params = nsurl_access(c->url) + SLEN("data:");
179
180 /* find the comma */
181 if ( (comma = strchr(params, ',')) == NULL) {
182 msg.type = FETCH_ERROR;
183 msg.data.error = "Malformed data: URL";
185 return false;
186 }
187
188 if (params[0] == ',') {
189 /* there is no mimetype here, assume text/plain */
190 c->mimetype = strdup("text/plain;charset=US-ASCII");
191 } else {
192 /* make a copy of everything between data: and the comma */
193 c->mimetype = strndup(params, comma - params);
194 }
195
196 if (c->mimetype == NULL) {
197 msg.type = FETCH_ERROR;
198 msg.data.error =
199 "Unable to allocate memory for mimetype in data: URL";
201 return false;
202 }
203
204 if (strcmp(c->mimetype + strlen(c->mimetype) - 7, ";base64") == 0) {
205 c->base64 = true;
206 c->mimetype[strlen(c->mimetype) - 7] = '\0';
207 } else {
208 c->base64 = false;
209 }
210
211 /* URL unescape the data first, just incase some insane page
212 * decides to nest URL and base64 encoding. Like, say, Acid2.
213 */
214 res = url_unescape(comma + 1, 0, &unescaped_len, &unescaped);
215 if (res != NSERROR_OK) {
216 msg.type = FETCH_ERROR;
217 msg.data.error = "Unable to URL decode data: URL";
219 return false;
220 }
221
222 if (c->base64) {
223 if ((nsu_base64_decode_alloc((uint8_t *)unescaped,
224 unescaped_len,
225 (uint8_t **)&c->data,
226 &c->datalen) != NSUERROR_OK) ||
227 (c->data == NULL)) {
228 msg.type = FETCH_ERROR;
229 msg.data.error = "Unable to Base64 decode data: URL";
231 free(unescaped);
232 return false;
233 }
234 free(unescaped);
235 } else {
236 c->datalen = unescaped_len;
237 c->data = unescaped;
238 }
239
240
241 return true;
242}
243
244static void fetch_data_poll(lwc_string *scheme)
245{
246 fetch_msg msg;
247 struct fetch_data_context *c, *save_ring = NULL;
248
249 /* Iterate over ring, processing each pending fetch */
250 while (ring != NULL) {
251 /* Take the first entry from the ring */
252 c = ring;
253 RING_REMOVE(ring, c);
254
255 /* Ignore fetches that have been flagged as locked.
256 * This allows safe re-entrant calls to this function.
257 * Re-entrancy can occur if, as a result of a callback,
258 * the interested party causes fetch_poll() to be called
259 * again.
260 */
261 if (c->locked == true) {
262 RING_INSERT(save_ring, c);
263 continue;
264 }
265
266 /* Only process non-aborted fetches */
267 if (c->aborted == false && fetch_data_process(c) == true) {
269 NSLOG(netsurf, INFO,
270 "setting data: MIME type to %s, length to %"PRIsizet,
271 c->mimetype,
272 c->datalen);
273 /* Any callback can result in the fetch being aborted.
274 * Therefore, we _must_ check for this after _every_
275 * call to fetch_data_send_callback().
276 */
277 fetch_data_send_header(c, "Content-Type: %s",
278 c->mimetype);
279
280 if (c->aborted == false) {
281 fetch_data_send_header(c, "Content-Length: %"
282 PRIsizet, c->datalen);
283 }
284
285 if (c->aborted == false) {
286 /* Set max-age to 1 year. */
287 fetch_data_send_header(c, "Cache-Control: "
288 "max-age=31536000");
289 }
290
291 if (c->aborted == false) {
292 msg.type = FETCH_DATA;
293 msg.data.header_or_data.buf =
294 (const uint8_t *) c->data;
297 }
298
299 if (c->aborted == false) {
300 msg.type = FETCH_FINISHED;
302 }
303 } else {
304 NSLOG(netsurf, INFO, "Processing of %.140s failed!",
305 nsurl_access(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 /* And now finish */
316 }
317
318 /* Finally, if we saved any fetches which were locked, put them back
319 * into the ring for next time
320 */
321 ring = save_ring;
322}
323
325{
326 lwc_string *scheme = lwc_string_ref(corestring_lwc_data);
327 const struct fetcher_operation_table fetcher_ops = {
329 .acceptable = fetch_data_can_fetch,
330 .setup = fetch_data_setup,
331 .start = fetch_data_start,
332 .abort = fetch_data_abort,
333 .free = fetch_data_free,
334 .poll = fetch_data_poll,
335 .finalise = fetch_data_finalise
336 };
337
338 return fetcher_add(scheme, &fetcher_ops);
339}
char * strndup(const char *s, size_t n)
Duplicate up to n characters of a string.
Definition: utils.c:332
void fetch_set_http_code(struct fetch *fetch, long http_code)
set the http code of a fetch
Definition: fetch.c:794
nserror fetcher_add(lwc_string *scheme, const struct fetcher_operation_table *ops)
Register a fetcher for a scheme.
Definition: fetch.c:357
void fetch_send_callback(const fetch_msg *msg, struct fetch *fetch)
send message to fetch
Definition: fetch.c:757
void fetch_free(struct fetch *f)
Free a fetch structure and associated resources.
Definition: fetch.c:548
void fetch_remove_from_queues(struct fetch *fetch)
remove a queued fetch
Definition: fetch.c:767
Fetching of data from a URL (interface).
@ FETCH_DATA
Definition: fetch.h:44
@ FETCH_HEADER
Definition: fetch.h:43
@ FETCH_FINISHED
Definition: fetch.h:46
@ FETCH_ERROR
Definition: fetch.h:48
Useful interned string pointers (interface).
static void fetch_data_send_header(struct fetch_data_context *ctx, const char *fmt,...)
Definition: data.c:86
static bool fetch_data_initialise(lwc_string *scheme)
Definition: data.c:59
static void fetch_data_send_callback(const fetch_msg *msg, struct fetch_data_context *c)
Definition: data.c:78
static void * fetch_data_setup(struct fetch *parent_fetch, nsurl *url, bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers)
Definition: data.c:108
static bool fetch_data_start(void *ctx)
Definition: data.c:126
static void fetch_data_abort(void *ctx)
Definition: data.c:141
static void fetch_data_free(void *ctx)
Definition: data.c:131
static struct fetch_data_context * ring
Definition: data.c:57
static bool fetch_data_can_fetch(const nsurl *url)
Definition: data.c:73
static void fetch_data_poll(lwc_string *scheme)
Definition: data.c:244
nserror fetch_data_register(void)
Register data scheme handler.
Definition: data.c:324
static bool fetch_data_process(struct fetch_data_context *c)
Definition: data.c:152
static void fetch_data_finalise(lwc_string *scheme)
Definition: data.c:67
data scheme fetch handler interface.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_OK
No error.
Definition: errors.h:30
Interface for fetchers factory.
Netsurf additional integer type formatting macros.
#define PRIsizet
c99 standard printf formatting for size_t type
Definition: inttypes.h:53
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
NetSurf URL handling (interface).
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
const char * nsurl_access(const nsurl *url)
Access a NetSurf URL object as a string.
size_t nsurl_length(const nsurl *url)
Find the length of a NetSurf URL object's URL, as returned by nsurl_access.
nsurl * nsurl_ref(nsurl *url)
Increment the reference count to a NetSurf URL object.
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
Ring list structure.
#define RING_REMOVE(ring, element)
Remove the given element from the specified ring.
Definition: ring.h:53
#define RING_INSERT(ring, element)
Insert the given item into the specified ring.
Definition: ring.h:40
Interface to utility string handling.
struct fetch_data_context * r_next
Definition: data.c:54
size_t datalen
Definition: data.c:48
bool aborted
Definition: data.c:51
char * mimetype
Definition: data.c:46
struct fetch * parent_fetch
Definition: data.c:44
char * data
Definition: data.c:47
nsurl * url
Definition: data.c:45
struct fetch_data_context * r_prev
Definition: data.c:54
Fetcher message data.
Definition: fetch.h:72
const uint8_t * buf
Definition: fetch.h:79
fetch_msg_type type
Definition: fetch.h:73
struct fetch_msg::@118::@119 header_or_data
size_t len
Definition: fetch.h:80
union fetch_msg::@118 data
const char * error
Definition: fetch.h:83
Fetch POST multipart data.
Definition: fetch.h:109
Information for a single fetch.
Definition: fetch.c:89
Fetcher operations API.
Definition: fetchers.h:49
bool(* initialise)(lwc_string *scheme)
The initialiser for the fetcher.
Definition: fetchers.h:55
nserror url_unescape(const char *str, size_t length, size_t *length_out, char **result_out)
Convert an escaped string to plain.
Definition: url.c:67
Interface to URL parsing and joining operations.
Interface to a number of general purpose functionality.
#define SLEN(x)
Calculate length of constant C string.
Definition: utils.h:88