Subversion Repositories Kolibri OS

Rev

Rev 4364 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3584 sourcerer 1
/*
2
 * Copyright 2008 Rob Kendrick 
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 .
17
 */
18
 
19
/* data: URL handling.  See http://tools.ietf.org/html/rfc2397 */
20
 
21
#include 
22
#include 
23
#include 
24
#include 
25
#include 
26
#include 
27
 
28
#include 		/* for URL unescaping functions */
29
 
5043 ashmew2 30
#include "http.h"
31
 
3584 sourcerer 32
#include 
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:[][;base64],
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;
5043 ashmew2 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
 
3584 sourcerer 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);
5043 ashmew2 229
			/* curl_free(unescaped); */
230
			free(unescaped);
3584 sourcerer 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);
5043 ashmew2 240
			/* curl_free(unescaped); */
241
			free(unescaped);
3584 sourcerer 242
			return false;
243
		}
244
		memcpy(c->data, unescaped, c->datalen);
245
	}
246
 
5043 ashmew2 247
	/* curl_free(unescaped); */
248
	free(unescaped);
249
 
3584 sourcerer 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
}