NetSurf
resource.c
Go to the documentation of this file.
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/**
20 * \file
21 * resource scheme URL handling. Based on the data fetcher by Rob Kendrick
22 */
23
24#include <stdlib.h>
25#include <stdbool.h>
26#include <string.h>
27#include <strings.h>
28#include <stdio.h>
29#include <stdarg.h>
30#include <libwapcaplet/libwapcaplet.h>
31
32#include "netsurf/inttypes.h"
33#include "utils/nsurl.h"
34#include "utils/corestrings.h"
35#include "utils/log.h"
36#include "utils/time.h"
37#include "utils/messages.h"
38#include "utils/utils.h"
39#include "utils/ring.h"
40#include "netsurf/fetch.h"
42
43#include "content/fetch.h"
44#include "content/fetchers.h"
46
47#define DIRECT_ETAG_VALUE 123456
48
49/** Valid resource paths */
50static const char *fetch_resource_paths[] = {
51 "adblock.css",
52 "default.css",
53 "internal.css",
54 "quirks.css",
55 "user.css",
56 "credits.html",
57 "licence.html",
58 "welcome.html",
59 "favicon.ico",
60 "default.ico",
61 "netsurf.png",
62 "icons/arrow-l.png",
63 "icons/content.png",
64 "icons/directory.png",
65 "icons/directory2.png",
66 "icons/hotlist-add.png",
67 "icons/hotlist-rmv.png",
68 "icons/search.png"
69};
70
71/**
72 * map of resource scheme paths to redirect urls
73 */
75 lwc_string *path; /**< resource scheme path */
76 nsurl *redirect_url; /**< url to redirect to */
77 const uint8_t *data; /**< direct pointer to data */
78 size_t data_len; /**< length of direct data */
80
82
84
85/** Context for an resource fetch */
88
89 struct fetch *fetchh; /**< Handle for this fetch */
90
91 bool aborted; /**< Flag indicating fetch has been aborted */
92 bool locked; /**< Flag indicating entry is already entered */
93
94 nsurl *url; /**< requested url */
95
96 struct fetch_resource_map_entry *entry; /**< resource map entry */
97
99
100 time_t etag;
101};
102
103static struct fetch_resource_context *ring = NULL;
104
106
107/** issue fetch callbacks with locking */
108static inline bool fetch_resource_send_callback(const fetch_msg *msg,
109 struct fetch_resource_context *ctx)
110{
111 ctx->locked = true;
112 fetch_send_callback(msg, ctx->fetchh);
113 ctx->locked = false;
114
115 return ctx->aborted;
116}
117
119 const char *fmt, ...)
120{
121 fetch_msg msg;
122 char header[64];
123 va_list ap;
124 int len;
125
126 va_start(ap, fmt);
127 len = vsnprintf(header, sizeof header, fmt, ap);
128 va_end(ap);
129
130 if (len >= (int)sizeof(header) || len < 0) {
131 return false;
132 }
133
134 msg.type = FETCH_HEADER;
135 msg.data.header_or_data.buf = (const uint8_t *) header;
136 msg.data.header_or_data.len = len;
137
138 return fetch_resource_send_callback(&msg, ctx);
139}
140
141
142
143/**
144 * resource handler that results in a redirect to another url.
145 */
147{
148 fetch_msg msg;
149
150 /* content is going to return redirect */
151 fetch_set_http_code(ctx->fetchh, 302);
152
153 msg.type = FETCH_REDIRECT;
156
157 return true;
158}
159
160/* resource handler that returns data directly */
162{
163 fetch_msg msg;
164
165 /* Check if we can just return not modified */
166 if (ctx->etag != 0 && ctx->etag == DIRECT_ETAG_VALUE) {
167 fetch_set_http_code(ctx->fetchh, 304);
170 return true;
171 }
172
173 /* fetch is going to be successful */
174 fetch_set_http_code(ctx->fetchh, 200);
175
176 /* Any callback can result in the fetch being aborted.
177 * Therefore, we _must_ check for this after _every_ call to
178 * fetch_file_send_callback().
179 */
180
181 /* content type */
182 if (fetch_resource_send_header(ctx, "Content-Type: %s",
183 guit->fetch->filetype(lwc_string_data(ctx->entry->path)))) {
184 goto fetch_resource_data_aborted;
185 }
186
187 /* content length */
188 if (fetch_resource_send_header(ctx, "Content-Length: %" PRIsizet,
189 ctx->entry->data_len)) {
190 goto fetch_resource_data_aborted;
191 }
192
193 /* create etag */
194 if (fetch_resource_send_header(ctx, "ETag: \"%10" PRId64 "\"",
195 (int64_t) DIRECT_ETAG_VALUE)) {
196 goto fetch_resource_data_aborted;
197 }
198
199 /* create max-age of 1 year */
201 "Cache-Control: max-age=31536000")) {
202 goto fetch_resource_data_aborted;
203 }
204
205 msg.type = FETCH_DATA;
206 msg.data.header_or_data.buf = (const uint8_t *) ctx->entry->data;
209
210 if (ctx->aborted == false) {
211 msg.type = FETCH_FINISHED;
213 }
214
215fetch_resource_data_aborted:
216
217 return true;
218}
219
221{
222 fetch_msg msg;
223 int code = 404;
224 char buffer[1024];
225 const char *title;
226 char key[8];
227
228 /* content is going to return error code */
229 fetch_set_http_code(ctx->fetchh, code);
230
231 /* content type */
232 if (fetch_resource_send_header(ctx, "Content-Type: text/html; charset=utf-8"))
233 goto fetch_resource_notfound_handler_aborted;
234
235 snprintf(key, sizeof key, "HTTP%03d", code);
236 title = messages_get(key);
237
238 snprintf(buffer, sizeof buffer,
239 "<html><head>"
240 "<title>%s</title>"
241 "<link rel=\"stylesheet\" type=\"text/css\" "
242 "href=\"resource:internal.css\">\n"
243 "</head>"
244 "<body class=\"ns-even-bg ns-even-fg ns-border\" "
245 "id =\"fetcherror\">\n"
246 "<h1 class=\"ns-border ns-odd-fg-bad\">%s</h1>\n"
247 "<p>%s %d %s %s</p>\n"
248 "</body>\n</html>\n",
249 title, title,
250 messages_get("FetchErrorCode"), code,
251 messages_get("FetchFile"), nsurl_access(ctx->url));
252
253 msg.type = FETCH_DATA;
254 msg.data.header_or_data.buf = (const uint8_t *) buffer;
255 msg.data.header_or_data.len = strlen(buffer);
256 if (fetch_resource_send_callback(&msg, ctx))
257 goto fetch_resource_notfound_handler_aborted;
258
259 msg.type = FETCH_FINISHED;
261
262fetch_resource_notfound_handler_aborted:
263 return false;
264}
265
266
267
268/** callback to initialise the resource fetcher. */
269static bool fetch_resource_initialise(lwc_string *scheme)
270{
271 struct fetch_resource_map_entry *e;
272 uint32_t i;
273 nserror res;
274
276
277 for (i = 0; i < NOF_ELEMENTS(fetch_resource_paths); i++) {
279
280 if (lwc_intern_string(fetch_resource_paths[i],
281 strlen(fetch_resource_paths[i]),
282 &e->path) != lwc_error_ok) {
283 while (i > 0) {
284 i--;
285 lwc_string_unref(fetch_resource_map[i].path);
287 }
288 /** \todo should this exit with an error condition? */
289 }
290
291 e->data = NULL;
292 res = guit->fetch->get_resource_data(lwc_string_data(e->path),
293 &e->data,
294 &e->data_len);
295 if (res == NSERROR_OK) {
296 NSLOG(netsurf, INFO, "direct data for %s",
299 } else {
301 if (e->redirect_url == NULL) {
302 lwc_string_unref(e->path);
303 } else {
304 NSLOG(netsurf, INFO, "redirect url for %s",
307 }
308 }
309 }
310
311 return true;
312}
313
314/** callback to finalise the resource fetcher. */
315static void fetch_resource_finalise(lwc_string *scheme)
316{
317 uint32_t i;
318
319 for (i = 0; i < fetch_resource_path_count; i++) {
320 lwc_string_unref(fetch_resource_map[i].path);
321 if (fetch_resource_map[i].data != NULL) {
323 } else {
325 }
326 }
327}
328
329static bool fetch_resource_can_fetch(const nsurl *url)
330{
331 return true;
332}
333
334/**
335 * set up a resource fetch context.
336 */
337static void *
339 nsurl *url,
340 bool only_2xx,
341 bool downgrade_tls,
342 const char *post_urlenc,
343 const struct fetch_multipart_data *post_multipart,
344 const char **headers)
345{
346 struct fetch_resource_context *ctx;
347 lwc_string *path;
348 nserror ret;
349 uint32_t i;
350
351 ctx = calloc(1, sizeof(*ctx));
352 if (ctx == NULL) {
353 return NULL;
354 }
355
357
358 if ((path = nsurl_get_component(url, NSURL_PATH)) != NULL) {
359 bool match;
360
361 /* Ensure requested path is valid */
362 for (i = 0; i < fetch_resource_path_count; i++) {
363 if (lwc_string_isequal(path,
365 &match) == lwc_error_ok && match) {
366 /* found a url match, select handler */
367 ctx->entry = &fetch_resource_map[i];
368 if (ctx->entry->data != NULL) {
370 } else {
372 }
373 break;
374 }
375 }
376
377 lwc_string_unref(path);
378 }
379
380 ctx->url = nsurl_ref(url);
381
382 /* Scan request headers looking for If-None-Match */
383 for (i = 0; headers[i] != NULL; i++) {
384 if (strncasecmp(headers[i], "If-None-Match:",
385 SLEN("If-None-Match:")) != 0) {
386 continue;
387 }
388
389 /* If-None-Match: "12345678" */
390 const char *d = headers[i] + SLEN("If-None-Match:");
391
392 /* Scan to first digit, if any */
393 while (*d != '\0' && (*d < '0' || '9' < *d))
394 d++;
395
396 /* Convert to time_t */
397 if (*d != '\0') {
398 ret = nsc_snptimet(d, strlen(d), &ctx->etag);
399 if (ret != NSERROR_OK) {
400 NSLOG(fetch, WARNING,
401 "Bad If-None-Match value");
402 }
403 }
404 }
405
406 ctx->fetchh = fetchh;
407
408 RING_INSERT(ring, ctx);
409
410 return ctx;
411}
412
413/** callback to free a resource fetch */
414static void fetch_resource_free(void *ctx)
415{
416 struct fetch_resource_context *c = ctx;
417 if (c->url != NULL)
418 nsurl_unref(c->url);
419 free(ctx);
420}
421
422/** callback to start a resource fetch */
423static bool fetch_resource_start(void *ctx)
424{
425 return true;
426}
427
428/** callback to abort a resource fetch */
429static void fetch_resource_abort(void *ctx)
430{
431 struct fetch_resource_context *c = ctx;
432
433 /* To avoid the poll loop having to deal with the fetch context
434 * disappearing from under it, we simply flag the abort here.
435 * The poll loop itself will perform the appropriate cleanup.
436 */
437 c->aborted = true;
438}
439
440
441/** callback to poll for additional resource fetch contents */
442static void fetch_resource_poll(lwc_string *scheme)
443{
444 struct fetch_resource_context *c, *save_ring = NULL;
445
446 while (ring != NULL) {
447 /* Take the first entry from the ring */
448 c = ring;
449 RING_REMOVE(ring, c);
450
451 /* Ignore fetches that have been flagged as locked.
452 * This allows safe re-entrant calls to this function.
453 * Re-entrancy can occur if, as a result of a callback,
454 * the interested party causes fetch_poll() to be called
455 * again.
456 */
457 if (c->locked == true) {
458 RING_INSERT(save_ring, c);
459 continue;
460 }
461
462 /* Only process non-aborted fetches */
463 if (c->aborted == false) {
464 /* resource fetches can be processed in one go */
465 c->handler(c);
466 }
467
468 /* And now finish */
470 fetch_free(c->fetchh);
471 }
472
473 /* Finally, if we saved any fetches which were locked, put them back
474 * into the ring for next time
475 */
476 ring = save_ring;
477}
478
480{
481 lwc_string *scheme = lwc_string_ref(corestring_lwc_resource);
482 const struct fetcher_operation_table fetcher_ops = {
484 .acceptable = fetch_resource_can_fetch,
485 .setup = fetch_resource_setup,
486 .start = fetch_resource_start,
487 .abort = fetch_resource_abort,
488 .free = fetch_resource_free,
489 .poll = fetch_resource_poll,
490 .finalise = fetch_resource_finalise
491 };
492
493 return fetcher_add(scheme, &fetcher_ops);
494}
static osspriteop_area * buffer
The buffer characteristics.
Definition: buffer.c:55
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_REDIRECT
Definition: fetch.h:49
@ FETCH_DATA
Definition: fetch.h:44
@ FETCH_HEADER
Definition: fetch.h:43
@ FETCH_NOTMODIFIED
Definition: fetch.h:50
@ FETCH_FINISHED
Definition: fetch.h:46
Useful interned string pointers (interface).
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_OK
No error.
Definition: errors.h:30
Interface for fetchers factory.
#define NOF_ELEMENTS(array)
Definition: search.c:67
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
Interface to platform-specific fetcher operations.
Netsurf additional integer type formatting macros.
#define PRIsizet
c99 standard printf formatting for size_t type
Definition: inttypes.h:53
#define PRId64
Definition: inttypes.h:34
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
const char * messages_get(const char *key)
Fast lookup of a message by key from the standard Messages hash.
Definition: messages.c:241
Localised message support (interface).
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.
nsurl * nsurl_ref(nsurl *url)
Increment the reference count to a NetSurf URL object.
lwc_string * nsurl_get_component(const nsurl *url, nsurl_component part)
Get part of a URL as a lwc_string, from a NetSurf URL object.
@ NSURL_PATH
Definition: nsurl.h:52
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
static bool fetch_resource_send_header(struct fetch_resource_context *ctx, const char *fmt,...)
Definition: resource.c:118
static void fetch_resource_poll(lwc_string *scheme)
callback to poll for additional resource fetch contents
Definition: resource.c:442
static struct fetch_resource_map_entry fetch_resource_map[NOF_ELEMENTS(fetch_resource_paths)]
static void fetch_resource_finalise(lwc_string *scheme)
callback to finalise the resource fetcher.
Definition: resource.c:315
static bool fetch_resource_start(void *ctx)
callback to start a resource fetch
Definition: resource.c:423
bool(* fetch_resource_handler)(struct fetch_resource_context *)
Definition: resource.c:83
static void fetch_resource_free(void *ctx)
callback to free a resource fetch
Definition: resource.c:414
static bool fetch_resource_notfound_handler(struct fetch_resource_context *ctx)
Definition: resource.c:220
static void fetch_resource_abort(void *ctx)
callback to abort a resource fetch
Definition: resource.c:429
static void * fetch_resource_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers)
set up a resource fetch context.
Definition: resource.c:338
static bool fetch_resource_data_handler(struct fetch_resource_context *ctx)
Definition: resource.c:161
static bool fetch_resource_can_fetch(const nsurl *url)
Definition: resource.c:329
static struct fetch_resource_context * ring
Definition: resource.c:103
nserror fetch_resource_register(void)
Register the resource scheme.
Definition: resource.c:479
static uint32_t fetch_resource_path_count
Definition: resource.c:105
static bool fetch_resource_initialise(lwc_string *scheme)
callback to initialise the resource fetcher.
Definition: resource.c:269
static bool fetch_resource_send_callback(const fetch_msg *msg, struct fetch_resource_context *ctx)
issue fetch callbacks with locking
Definition: resource.c:108
static bool fetch_resource_redirect_handler(struct fetch_resource_context *ctx)
resource handler that results in a redirect to another url.
Definition: resource.c:146
#define DIRECT_ETAG_VALUE
Definition: resource.c:47
static const char * fetch_resource_paths[]
Valid resource paths.
Definition: resource.c:50
resource URL scheme handler interface.
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.
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 * redirect
Definition: fetch.h:86
Fetch POST multipart data.
Definition: fetch.h:109
Context for an resource fetch.
Definition: resource.c:86
struct fetch_resource_map_entry * entry
resource map entry
Definition: resource.c:96
bool aborted
Flag indicating fetch has been aborted.
Definition: resource.c:91
bool locked
Flag indicating entry is already entered.
Definition: resource.c:92
nsurl * url
requested url
Definition: resource.c:94
fetch_resource_handler handler
Definition: resource.c:98
struct fetch_resource_context * r_next
Definition: resource.c:87
struct fetch * fetchh
Handle for this fetch.
Definition: resource.c:89
struct fetch_resource_context * r_prev
Definition: resource.c:87
map of resource scheme paths to redirect urls
Definition: resource.c:74
const uint8_t * data
direct pointer to data
Definition: resource.c:77
size_t data_len
length of direct data
Definition: resource.c:78
nsurl * redirect_url
url to redirect to
Definition: resource.c:76
lwc_string * path
resource scheme path
Definition: resource.c:75
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
const char *(* filetype)(const char *unix_path)
Determine the MIME type of a local file.
Definition: fetch.h:45
nserror(* release_resource_data)(const uint8_t *data)
Releases source data.
Definition: fetch.h:89
nserror(* get_resource_data)(const char *path, const uint8_t **data, size_t *data_len)
Translate resource to source data.
Definition: fetch.h:77
struct nsurl *(* get_resource_url)(const char *path)
Translate resource to full url.
Definition: fetch.h:62
struct gui_fetch_table * fetch
Fetcher table.
Definition: gui_table.h:94
nserror nsc_snptimet(const char *str, size_t size, time_t *timep)
Parse time in seconds since epoc.
Definition: time.c:147
Interface to time operations.
Interface to a number of general purpose functionality.
#define SLEN(x)
Calculate length of constant C string.
Definition: utils.h:88
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821