1 /*
2 * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
3 * Copyright 2013 John-Mark Bell <jmb@netsurf-browser.org>
4 *
5 * This file is part of NetSurf.
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /**
21 * \file
22 * HTML fetcher for CSS objects
23 */
24
25 #include <assert.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <dom/dom.h>
30 #include <libwapcaplet/libwapcaplet.h>
31
32 #include "netsurf/inttypes.h"
33 #include "utils/config.h"
34 #include "utils/corestrings.h"
35 #include "utils/log.h"
36 #include "utils/ring.h"
37 #include "utils/nsurl.h"
38 #include "utils/utils.h"
39 #include "content/fetch.h"
40 #include "content/fetchers.h"
41
42 #include "html/private.h"
43
44 typedef struct html_css_fetcher_item {
45 uint32_t key;
46 dom_string *data;
47 nsurl *base_url;
48
49 struct html_css_fetcher_item *r_next, *r_prev;
50 } html_css_fetcher_item;
51
52 typedef struct html_css_fetcher_context {
53 struct fetch *parent_fetch;
54
55 nsurl *url;
56 html_css_fetcher_item *item;
57
58 bool aborted;
59 bool locked;
60
61 struct html_css_fetcher_context *r_next, *r_prev;
62 } html_css_fetcher_context;
63
64 static uint32_t current_key = 0;
65 static html_css_fetcher_item *items = NULL;
66 static html_css_fetcher_context *ring = NULL;
67
html_css_fetcher_initialise(lwc_string * scheme)68 static bool html_css_fetcher_initialise(lwc_string *scheme)
69 {
70 NSLOG(netsurf, INFO, "html_css_fetcher_initialise called for %s",
71 lwc_string_data(scheme));
72 return true;
73 }
74
html_css_fetcher_finalise(lwc_string * scheme)75 static void html_css_fetcher_finalise(lwc_string *scheme)
76 {
77 NSLOG(netsurf, INFO, "html_css_fetcher_finalise called for %s",
78 lwc_string_data(scheme));
79 }
80
html_css_fetcher_can_fetch(const nsurl * url)81 static bool html_css_fetcher_can_fetch(const nsurl *url)
82 {
83 return true;
84 }
85
html_css_fetcher_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)86 static void *html_css_fetcher_setup(struct fetch *parent_fetch, nsurl *url,
87 bool only_2xx, bool downgrade_tls, const char *post_urlenc,
88 const struct fetch_multipart_data *post_multipart,
89 const char **headers)
90 {
91 html_css_fetcher_context *ctx;
92 lwc_string *path;
93 uint32_t key;
94 html_css_fetcher_item *item, *found = NULL;
95
96 /* format of a x-ns-css URL is:
97 * x-ns-url:<key>
98 * Where key is an unsigned 32bit integer
99 */
100
101 path = nsurl_get_component(url, NSURL_PATH);
102 /* The path must exist */
103 if (path == NULL) {
104 return NULL;
105 }
106
107 key = strtoul(lwc_string_data(path), NULL, 10);
108
109 lwc_string_unref(path);
110
111 /* There must be at least one item */
112 if (items == NULL) {
113 return NULL;
114 }
115
116 item = items;
117 do {
118 if (item->key == key) {
119 found = item;
120 break;
121 }
122
123 item = item->r_next;
124 } while (item != items);
125
126 /* We must have found the item */
127 if (found == NULL) {
128 return NULL;
129 }
130
131 ctx = calloc(1, sizeof(*ctx));
132 if (ctx == NULL)
133 return NULL;
134
135 ctx->parent_fetch = parent_fetch;
136 ctx->url = nsurl_ref(url);
137 ctx->item = found;
138
139 RING_INSERT(ring, ctx);
140
141 return ctx;
142 }
143
html_css_fetcher_start(void * ctx)144 static bool html_css_fetcher_start(void *ctx)
145 {
146 return true;
147 }
148
html_css_fetcher_free(void * ctx)149 static void html_css_fetcher_free(void *ctx)
150 {
151 html_css_fetcher_context *c = ctx;
152
153 nsurl_unref(c->url);
154 if (c->item != NULL) {
155 nsurl_unref(c->item->base_url);
156 dom_string_unref(c->item->data);
157 RING_REMOVE(items, c->item);
158 free(c->item);
159 }
160 RING_REMOVE(ring, c);
161 free(ctx);
162 }
163
html_css_fetcher_abort(void * ctx)164 static void html_css_fetcher_abort(void *ctx)
165 {
166 html_css_fetcher_context *c = ctx;
167
168 /* To avoid the poll loop having to deal with the fetch context
169 * disappearing from under it, we simply flag the abort here.
170 * The poll loop itself will perform the appropriate cleanup.
171 */
172 c->aborted = true;
173 }
174
html_css_fetcher_send_callback(const fetch_msg * msg,html_css_fetcher_context * c)175 static void html_css_fetcher_send_callback(const fetch_msg *msg,
176 html_css_fetcher_context *c)
177 {
178 c->locked = true;
179 fetch_send_callback(msg, c->parent_fetch);
180 c->locked = false;
181 }
182
html_css_fetcher_poll(lwc_string * scheme)183 static void html_css_fetcher_poll(lwc_string *scheme)
184 {
185 fetch_msg msg;
186 html_css_fetcher_context *c, *next;
187
188 if (ring == NULL) return;
189
190 /* Iterate over ring, processing each pending fetch */
191 c = ring;
192 do {
193 /* Ignore fetches that have been flagged as locked.
194 * This allows safe re-entrant calls to this function.
195 * Re-entrancy can occur if, as a result of a callback,
196 * the interested party causes fetch_poll() to be called
197 * again.
198 */
199 if (c->locked == true) {
200 next = c->r_next;
201 continue;
202 }
203
204 /* Only process non-aborted fetches */
205 if (c->aborted) {
206 /* Nothing to do */
207 assert(c->locked == false);
208 } else if (c->item != NULL) {
209 char header[4096];
210
211 fetch_set_http_code(c->parent_fetch, 200);
212
213 /* Any callback can result in the fetch being aborted.
214 * Therefore, we _must_ check for this after _every_
215 * call to html_css_fetcher_send_callback().
216 */
217 snprintf(header, sizeof header,
218 "Content-Type: text/css; charset=utf-8");
219 msg.type = FETCH_HEADER;
220 msg.data.header_or_data.buf = (const uint8_t *) header;
221 msg.data.header_or_data.len = strlen(header);
222 html_css_fetcher_send_callback(&msg, c);
223
224 if (c->aborted == false) {
225 snprintf(header, sizeof header,
226 "Content-Length: %"PRIsizet,
227 dom_string_byte_length(c->item->data));
228 msg.type = FETCH_HEADER;
229 msg.data.header_or_data.buf =
230 (const uint8_t *) header;
231 msg.data.header_or_data.len = strlen(header);
232 html_css_fetcher_send_callback(&msg, c);
233 }
234
235 if (c->aborted == false) {
236 snprintf(header, sizeof header,
237 "X-NS-Base: %.*s",
238 (int) nsurl_length(c->item->base_url),
239 nsurl_access(c->item->base_url));
240 msg.type = FETCH_HEADER;
241 msg.data.header_or_data.buf =
242 (const uint8_t *) header;
243 msg.data.header_or_data.len = strlen(header);
244 html_css_fetcher_send_callback(&msg, c);
245 }
246
247 if (c->aborted == false) {
248 msg.type = FETCH_DATA;
249 msg.data.header_or_data.buf =
250 (const uint8_t *)
251 dom_string_data(c->item->data);
252 msg.data.header_or_data.len =
253 dom_string_byte_length(c->item->data);
254 html_css_fetcher_send_callback(&msg, c);
255 }
256
257 if (c->aborted == false) {
258 msg.type = FETCH_FINISHED;
259 html_css_fetcher_send_callback(&msg, c);
260 }
261 } else {
262 NSLOG(netsurf, INFO, "Processing of %s failed!",
263 nsurl_access(c->url));
264
265 /* Ensure that we're unlocked here. If we aren't,
266 * then html_css_fetcher_process() is broken.
267 */
268 assert(c->locked == false);
269 }
270
271 /* Compute next fetch item at the last possible moment as
272 * processing this item may have added to the ring.
273 */
274 next = c->r_next;
275
276 fetch_remove_from_queues(c->parent_fetch);
277 fetch_free(c->parent_fetch);
278
279 /* Advance to next ring entry, exiting if we've reached
280 * the start of the ring or the ring has become empty
281 */
282 } while ( (c = next) != ring && ring != NULL);
283 }
284
285 /* exported interface documented in html_internal.h */
html_css_fetcher_register(void)286 nserror html_css_fetcher_register(void)
287 {
288 const struct fetcher_operation_table html_css_fetcher_ops = {
289 .initialise = html_css_fetcher_initialise,
290 .acceptable = html_css_fetcher_can_fetch,
291 .setup = html_css_fetcher_setup,
292 .start = html_css_fetcher_start,
293 .abort = html_css_fetcher_abort,
294 .free = html_css_fetcher_free,
295 .poll = html_css_fetcher_poll,
296 .finalise = html_css_fetcher_finalise
297 };
298
299 return fetcher_add(lwc_string_ref(corestring_lwc_x_ns_css),
300 &html_css_fetcher_ops);
301 }
302
303 /* exported interface documented in html_internal.h */
304 nserror
html_css_fetcher_add_item(dom_string * data,nsurl * base_url,uint32_t * key)305 html_css_fetcher_add_item(dom_string *data, nsurl *base_url, uint32_t *key)
306 {
307 html_css_fetcher_item *item = malloc(sizeof(*item));
308
309 if (item == NULL) {
310 return NSERROR_NOMEM;
311 }
312
313 *key = item->key = current_key++;
314 item->data = dom_string_ref(data);
315 item->base_url = nsurl_ref(base_url);
316
317 RING_INSERT(items, item);
318
319 return NSERROR_OK;
320 }
321