Bug Summary

File:content/urldb.c
Warning:line 2919, column 9
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name urldb.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -resource-dir /usr/lib/llvm-19/lib/clang/19 -isystem /usr/include/mit-krb5 -I . -I include -I build/Linux-gtk2 -I frontends -I content/handlers -D WITH_JPEG -U WITH_PDF_EXPORT -D LIBICONV_PLUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /usr/include/x86_64-linux-gnu -I /usr/include/p11-kit-1 -D WITH_CURL -D WITH_OPENSSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D UTF8PROC_EXPORTS -D WITH_UTF8PROC -I /usr/include/webp -D WITH_WEBP -I /usr/include/libpng16 -D WITH_PNG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include/ -D WITH_BMP -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_GIF -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NS_SVG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSSPRITE -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSPSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSLOG -D NETSURF_UA_FORMAT_STRING="Mozilla/5.0 (%s) NetSurf/%d.%d" -D NETSURF_HOMEPAGE="about:welcome" -D NETSURF_LOG_LEVEL=VERBOSE -D NETSURF_BUILTIN_LOG_FILTER="(level:WARNING || cat:jserrors)" -D NETSURF_BUILTIN_VERBOSE_FILTER="(level:VERBOSE || cat:jserrors)" -D STMTEXPR=1 -I /usr/include/librsvg-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/libpng16 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/cairo -I /usr/include/freetype2 -I /usr/include/pixman-1 -I /usr/include/harfbuzz -I /usr/include/libxml2 -I /usr/include/pango-1.0 -I /usr/include/fribidi -D WITH_RSVG -I /usr/include/gtk-2.0 -I /usr/lib/x86_64-linux-gnu/gtk-2.0/include -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/atk-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D gtk -D nsgtk -D G_DISABLE_SINGLE_INCLUDES -D G_DISABLE_DEPRECATED -D GTK_DISABLE_SINGLE_INCLUDES -D GTK_MULTIHEAD_SAFE -D PANGO_DISABLE_DEPRECATED -D GTK_DISABLE_DEPRECATED -D _XOPEN_SOURCE=700 -D _POSIX_C_SOURCE=200809L -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _NETBSD_SOURCE -D GTK_RESPATH="/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf/:./frontends/gtk/res/" -D WITH_GRESOURCE -D DUK_OPT_HAVE_CUSTOM_H -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wwrite-strings -Wno-unused-parameter -Wno-unused-but-set-variable -std=c99 -fconst-strings -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-netsurf/clangScanBuildReports/2025-11-28-113330-2505254-1 -x c content/urldb.c
1/*
2 * Copyright 2006 John M Bell <jmb202@ecs.soton.ac.uk>
3 * Copyright 2009 John Tytgat <joty@netsurf-browser.org>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
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 * Unified URL information database implementation
23 *
24 * URLs are stored in a tree-based structure as follows:
25 *
26 * The host component is extracted from each URL and, if a FQDN, split on
27 * every '.'.The tree is constructed by inserting each FQDN segment in
28 * reverse order. Duplicate nodes are merged.
29 *
30 * If the host part of an URL is an IP address, then this is added to the
31 * tree verbatim (as if it were a TLD).
32 *
33 * This provides something looking like:
34 *
35 * root (a sentinel)
36 * |
37 * -------------------------------------------------
38 * | | | | | | |
39 * com edu gov 127.0.0.1 net org uk TLDs
40 * | | | | | |
41 * google ... ... ... ... co 2LDs
42 * | |
43 * www bbc Hosts/Subdomains
44 * |
45 * www ...
46 *
47 * Each of the nodes in this tree is a struct host_part. This stores the
48 * FQDN segment (or IP address) with which the node is concerned. Each node
49 * may contain further information about paths on a host (struct path_data)
50 * or SSL certificate processing on a host-wide basis
51 * (host_part::permit_invalid_certs).
52 *
53 * Path data is concerned with storing various metadata about the path in
54 * question. This includes global history data, HTTP authentication details
55 * and any associated HTTP cookies. This is stored as a tree of path segments
56 * hanging off the relevant host_part node.
57 *
58 * Therefore, to find the last visited time of the URL
59 * http://www.example.com/path/to/resource.html, the FQDN tree would be
60 * traversed in the order root -> "com" -> "example" -> "www". The "www"
61 * node would have attached to it a tree of struct path_data:
62 *
63 * (sentinel)
64 * |
65 * path
66 * |
67 * to
68 * |
69 * resource.html
70 *
71 * This represents the absolute path "/path/to/resource.html". The leaf node
72 * "resource.html" contains the last visited time of the resource.
73 *
74 * The mechanism described above is, however, not particularly conducive to
75 * fast searching of the database for a given URL (or URLs beginning with a
76 * given prefix). Therefore, an anciliary data structure is used to enable
77 * fast searching. This structure simply reflects the contents of the
78 * database, with entries being added/removed at the same time as for the
79 * core database. In order to ensure that degenerate cases are kept to a
80 * minimum, we use an AAtree. This is an approximation of a Red-Black tree
81 * with similar performance characteristics, but with a significantly
82 * simpler implementation. Entries in this tree comprise pointers to the
83 * leaf nodes of the host tree described above.
84 *
85 * REALLY IMPORTANT NOTE: urldb expects all URLs to be normalised. Use of
86 * non-normalised URLs with urldb will result in undefined behaviour and
87 * potential crashes.
88 */
89
90#include <assert.h>
91#include <stdbool.h>
92#include <stdio.h>
93#include <stdlib.h>
94#include <string.h>
95#include <strings.h>
96#include <time.h>
97#ifdef WITH_NSPSL1
98#include <nspsl.h>
99#endif
100
101#include "utils/inet.h"
102#include "utils/nsoption.h"
103#include "utils/log.h"
104#include "utils/corestrings.h"
105#include "utils/url.h"
106#include "utils/utils.h"
107#include "utils/bloom.h"
108#include "utils/time.h"
109#include "utils/nsurl.h"
110#include "utils/ascii.h"
111#include "utils/http.h"
112#include "netsurf/bitmap.h"
113#include "desktop/cookie_manager.h"
114
115#include "content/content.h"
116#include "content/urldb.h"
117
118/**
119 * cookie entry.
120 *
121 * \warning This *must* be kept in sync with the public interface in
122 * netsurf/cookie_db.h
123 */
124struct cookie_internal_data {
125 struct cookie_internal_data *prev; /**< Previous in list */
126 struct cookie_internal_data *next; /**< Next in list */
127
128 char *name; /**< Cookie name */
129 char *value; /**< Cookie value */
130 bool_Bool value_was_quoted; /**< Value was quoted in Set-Cookie: */
131 char *comment; /**< Cookie comment */
132 bool_Bool domain_from_set; /**< Domain came from Set-Cookie: header */
133 char *domain; /**< Domain */
134 bool_Bool path_from_set; /**< Path came from Set-Cookie: header */
135 char *path; /**< Path */
136 time_t expires; /**< Expiry timestamp, or -1 for session */
137 time_t last_used; /**< Last used time */
138 bool_Bool secure; /**< Only send for HTTPS requests */
139 bool_Bool http_only; /**< Only expose to HTTP(S) requests */
140 enum cookie_version version; /**< Specification compliance */
141 bool_Bool no_destroy; /**< Never destroy this cookie,
142 * unless it's expired */
143
144};
145
146
147/**
148 * A protection space
149 *
150 * This is defined as a tuple canonical_root_url and realm. This
151 * structure lives as linked list element in a leaf host_part struct
152 * so we need additional scheme and port to have a canonical_root_url.
153 */
154struct prot_space_data {
155 /**
156 * URL scheme of canonical hostname of this protection space.
157 */
158 lwc_string *scheme;
159 /**
160 * Port number of canonical hostname of this protection
161 * space. When 0, it means the default port for given scheme,
162 * i.e. 80 (http), 443 (https).
163 */
164 unsigned int port;
165 /** Protection realm */
166 char *realm;
167
168 /**
169 * Authentication details for this protection space in form
170 * username:password
171 */
172 char *auth;
173 /** Next sibling */
174 struct prot_space_data *next;
175};
176
177
178/**
179 * meta data about a url
180 *
181 * \warning must be kept in sync with url_data structure in netsurf/url_db.h
182 */
183struct url_internal_data {
184 char *title; /**< Resource title */
185 unsigned int visits; /**< Visit count */
186 time_t last_visit; /**< Last visit time */
187 content_type type; /**< Type of resource */
188};
189
190
191/**
192 * data entry for url
193 */
194struct path_data {
195 nsurl *url; /**< Full URL */
196 lwc_string *scheme; /**< URL scheme for data */
197 unsigned int port; /**< Port number for data. When 0, it means
198 * the default port for given scheme, i.e.
199 * 80 (http), 443 (https). */
200 char *segment; /**< Path segment for this node */
201 unsigned int frag_cnt; /**< Number of entries in path_data::fragment */
202 char **fragment; /**< Array of fragments */
203 bool_Bool persistent; /**< This entry should persist */
204
205 struct url_internal_data urld; /**< URL data for resource */
206
207 /**
208 * Protection space to which this resource belongs too. Can be
209 * NULL when it does not belong to a protection space or when
210 * it is not known. No ownership (is with struct host_part::prot_space).
211 */
212 const struct prot_space_data *prot_space;
213 /** Cookies associated with resource */
214 struct cookie_internal_data *cookies;
215 /** Last cookie in list */
216 struct cookie_internal_data *cookies_end;
217
218 struct path_data *next; /**< Next sibling */
219 struct path_data *prev; /**< Previous sibling */
220 struct path_data *parent; /**< Parent path segment */
221 struct path_data *children; /**< Child path segments */
222 struct path_data *last; /**< Last child */
223};
224
225struct hsts_data {
226 time_t expires; /**< Expiry time */
227 bool_Bool include_sub_domains; /**< Whether to include subdomains */
228};
229
230struct host_part {
231 /**
232 * Known paths on this host. This _must_ be first so that
233 * struct host_part *h = (struct host_part *)mypath; works
234 */
235 struct path_data paths;
236 /**
237 * Allow access to SSL protected resources on this host
238 * without verifying certificate authenticity
239 */
240 bool_Bool permit_invalid_certs;
241 /* HSTS data */
242 struct hsts_data hsts;
243
244 /**
245 * Part of host string
246 */
247 char *part;
248
249 /**
250 * Linked list of all known proctection spaces known for this
251 * host and all its schems and ports.
252 */
253 struct prot_space_data *prot_space;
254
255 struct host_part *next; /**< Next sibling */
256 struct host_part *prev; /**< Previous sibling */
257 struct host_part *parent; /**< Parent host part */
258 struct host_part *children; /**< Child host parts */
259};
260
261
262/**
263 * search index node
264 */
265struct search_node {
266 const struct host_part *data; /**< Host tree entry */
267
268 unsigned int level; /**< Node level */
269
270 struct search_node *left; /**< Left subtree */
271 struct search_node *right; /**< Right subtree */
272};
273
274/** Root database handle */
275static struct host_part db_root;
276
277/** Search trees - one per letter + 1 for IPs + 1 for Everything Else */
278#define NUM_SEARCH_TREES28 28
279#define ST_IP0 0
280#define ST_EE1 1
281#define ST_DN2 2
282static struct search_node empty = { 0, 0, &empty, &empty };
283static struct search_node *search_trees[NUM_SEARCH_TREES28] = {
284 &empty, &empty, &empty, &empty, &empty, &empty, &empty, &empty,
285 &empty, &empty, &empty, &empty, &empty, &empty, &empty, &empty,
286 &empty, &empty, &empty, &empty, &empty, &empty, &empty, &empty,
287 &empty, &empty, &empty, &empty
288};
289
290/** Minimum cookie database file version */
291#define MIN_COOKIE_FILE_VERSION100 100
292/** Current cookie database file version */
293#define COOKIE_FILE_VERSION102 102
294/** loaded cookie file version */
295static int loaded_cookie_file_version;
296
297/** Minimum URL database file version */
298#define MIN_URL_FILE_VERSION106 106
299/** Current URL database file version */
300#define URL_FILE_VERSION107 107
301
302/**
303 * filter for url presence in database
304 *
305 * Bloom filter used for short-circuting the false case of "is this
306 * URL in the database?". BLOOM_SIZE controls how large the filter is
307 * in bytes. Primitive experimentation shows that for a filter of X
308 * bytes filled with X items, searching for X items not in the filter
309 * has a 5% false-positive rate. We set it to 32kB, which should be
310 * enough for all but the largest databases, while not being
311 * shockingly wasteful on memory.
312 */
313static struct bloom_filter *url_bloom;
314/**
315 * Size of url filter
316 */
317#define BLOOM_SIZE(1024 * 32) (1024 * 32)
318
319
320/**
321 * write a time_t to a file portably
322 *
323 * \param fp File to write to
324 * \param val the unix time value to output
325 * \return NSERROR_OK on success
326 */
327static nserror urldb_write_timet(FILE *fp, time_t val)
328{
329 int use;
330 char op[32];
331
332 use = nsc_sntimet(op, 32, &val);
333 if (use == 0) {
334 fprintf(fp, "%i\n", (int)val);
335 } else {
336 fprintf(fp, "%.*s\n", use, op);
337 }
338 return NSERROR_OK;
339}
340
341/**
342 * Write paths associated with a host
343 *
344 * \param parent Root of (sub)tree to write
345 * \param host Current host name
346 * \param fp File to write to
347 * \param path Current path string
348 * \param path_alloc Allocated size of path
349 * \param path_used Used size of path
350 * \param expiry Expiry time of URLs
351 */
352static void
353urldb_write_paths(const struct path_data *parent,
354 const char *host,
355 FILE *fp,
356 char **path,
357 int *path_alloc,
358 int *path_used,
359 time_t expiry)
360{
361 const struct path_data *p = parent;
362 int i;
363
364 do {
365 int seglen = p->segment != NULL((void*)0) ? strlen(p->segment) : 0;
366 int len = *path_used + seglen + 1;
367
368 if (*path_alloc < len) {
369 char *temp;
370 temp = realloc(*path,
371 (len > 64) ? len : *path_alloc + 64);
372 if (!temp) {
373 return;
374 }
375 *path = temp;
376 *path_alloc = (len > 64) ? len : *path_alloc + 64;
377 }
378
379 if (p->segment != NULL((void*)0)) {
380 memcpy(*path + *path_used - 1, p->segment, seglen);
381 }
382
383 if (p->children != NULL((void*)0)) {
384 (*path)[*path_used + seglen - 1] = '/';
385 (*path)[*path_used + seglen] = '\0';
386 } else {
387 (*path)[*path_used + seglen - 1] = '\0';
388 len -= 1;
389 }
390
391 *path_used = len;
392
393 if (p->children != NULL((void*)0)) {
394 /* Drill down into children */
395 p = p->children;
396 } else {
397 /* leaf node */
398 if (p->persistent ||
399 ((p->urld.last_visit > expiry) &&
400 (p->urld.visits > 0))) {
401 fprintf(fp, "%s\n", lwc_string_data(p->scheme)({((p->scheme != ((void*)0)) ? (void) (0) : __assert_fail (
"p->scheme != NULL", "content/urldb.c", 401, __extension__
__PRETTY_FUNCTION__)); (const char *)((p->scheme)+1);})
);
402
403 if (p->port) {
404 fprintf(fp,"%d\n", p->port);
405 } else {
406 fprintf(fp, "\n");
407 }
408
409 fprintf(fp, "%s\n", *path);
410
411 /** \todo handle fragments? */
412
413 /* number of visits */
414 fprintf(fp, "%i\n", p->urld.visits);
415
416 /* time entry was last used */
417 urldb_write_timet(fp, p->urld.last_visit);
418
419 /* entry type */
420 fprintf(fp, "%i\n", (int)p->urld.type);
421
422 fprintf(fp, "\n");
423
424 if (p->urld.title) {
425 uint8_t *s = (uint8_t *) p->urld.title;
426
427 for (i = 0; s[i] != '\0'; i++)
428 if (s[i] < 32)
429 s[i] = ' ';
430 for (--i; ((i > 0) && (s[i] == ' '));
431 i--)
432 s[i] = '\0';
433 fprintf(fp, "%s\n", p->urld.title);
434 } else {
435 fprintf(fp, "\n");
436 }
437 }
438
439 /* Now, find next node to process. */
440 while (p != parent) {
441 int seglen = p->segment != NULL((void*)0)
442 ? strlen(p->segment) : 0;
443
444 /* Remove our segment from the path */
445 *path_used -= seglen;
446 (*path)[*path_used - 1] = '\0';
447
448 if (p->next != NULL((void*)0)) {
449 /* Have a sibling, process that */
450 p = p->next;
451 break;
452 }
453
454 /* Going up, so remove '/' */
455 *path_used -= 1;
456 (*path)[*path_used - 1] = '\0';
457
458 /* Ascend tree */
459 p = p->parent;
460 }
461 }
462 } while (p != parent);
463}
464
465
466/**
467 * Count number of URLs associated with a host
468 *
469 * \param root Root of path data tree
470 * \param expiry Expiry time for URLs
471 * \param count Pointer to count
472 */
473static void
474urldb_count_urls(const struct path_data *root,
475 time_t expiry,
476 unsigned int *count)
477{
478 const struct path_data *p = root;
479
480 do {
481 if (p->children != NULL((void*)0)) {
482 /* Drill down into children */
483 p = p->children;
484 } else {
485 /* No more children, increment count if required */
486 if (p->persistent ||
487 ((p->urld.last_visit > expiry) &&
488 (p->urld.visits > 0))) {
489 (*count)++;
490 }
491
492 /* Now, find next node to process. */
493 while (p != root) {
494 if (p->next != NULL((void*)0)) {
495 /* Have a sibling, process that */
496 p = p->next;
497 break;
498 }
499
500 /* Ascend tree */
501 p = p->parent;
502 }
503 }
504 } while (p != root);
505}
506
507
508/**
509 * Save a search (sub)tree
510 *
511 * \param parent root node of search tree to save.
512 * \param fp File to write to
513 */
514static void urldb_save_search_tree(struct search_node *parent, FILE *fp)
515{
516 char host[256];
517 const struct host_part *h;
518 unsigned int path_count = 0;
519 char *path, *p, *end;
520 int path_alloc = 64, path_used = 1;
521 time_t expiry, hsts_expiry = 0;
522 int hsts_include_subdomains = 0;
523
524 expiry = time(NULL((void*)0)) - ((60 * 60 * 24) * nsoption_int(expire_url)(nsoptions[NSOPTION_expire_url].value.i));
525
526 if (parent == &empty)
527 return;
528
529 urldb_save_search_tree(parent->left, fp);
530
531 path = malloc(path_alloc);
532 if (!path)
533 return;
534
535 path[0] = '\0';
536
537 for (h = parent->data, p = host, end = host + sizeof host;
538 h && h != &db_root && p < end; h = h->parent) {
539 int written = snprintf(p, end - p, "%s%s", h->part,
540 (h->parent && h->parent->parent) ? "." : "");
541 if (written < 0) {
542 free(path);
543 return;
544 }
545 p += written;
546 }
547
548 h = parent->data;
549 if (h && h->hsts.expires > expiry) {
550 hsts_expiry = h->hsts.expires;
551 hsts_include_subdomains = h->hsts.include_sub_domains;
552 }
553
554 urldb_count_urls(&parent->data->paths, expiry, &path_count);
555
556 if (path_count > 0) {
557 fprintf(fp, "%s %i ", host, hsts_include_subdomains);
558 urldb_write_timet(fp, hsts_expiry);
559 fprintf(fp, "%i\n", path_count);
560
561 urldb_write_paths(&parent->data->paths, host, fp,
562 &path, &path_alloc, &path_used, expiry);
563 } else if (hsts_expiry) {
564 fprintf(fp, "%s %i ", host, hsts_include_subdomains);
565 urldb_write_timet(fp, hsts_expiry);
566 fprintf(fp, "0\n");
567 }
568
569 free(path);
570
571 urldb_save_search_tree(parent->right, fp);
572}
573
574
575/**
576 * Path data iterator (internal)
577 *
578 * \param parent Root of subtree to iterate over
579 * \param url_callback Callback function
580 * \param cookie_callback Callback function
581 * \return true to continue, false otherwise
582 */
583static bool_Bool
584urldb_iterate_entries_path(const struct path_data *parent,
585 bool_Bool (*url_callback)(nsurl *url, const struct url_data *data),
586 bool_Bool (*cookie_callback)(const struct cookie_data *data))
587{
588 const struct path_data *p = parent;
589 const struct cookie_data *c;
590
591 do {
592 if (p->children != NULL((void*)0)) {
593 /* Drill down into children */
594 p = p->children;
595 } else {
596 /* All leaf nodes in the path tree should have an URL or
597 * cookies attached to them. If this is not the case, it
598 * indicates that there's a bug in the file loader/URL
599 * insertion code. Therefore, assert this here. */
600 assert(url_callback || cookie_callback)((url_callback || cookie_callback) ? (void) (0) : __assert_fail
("url_callback || cookie_callback", "content/urldb.c", 600, __extension__
__PRETTY_FUNCTION__))
;
601
602 /** \todo handle fragments? */
603 if (url_callback) {
604 const struct url_internal_data *u = &p->urld;
605
606 assert(p->url)((p->url) ? (void) (0) : __assert_fail ("p->url", "content/urldb.c"
, 606, __extension__ __PRETTY_FUNCTION__))
;
607
608 if (!url_callback(p->url,
609 (const struct url_data *) u))
610 return false0;
611 } else {
612 c = (const struct cookie_data *)p->cookies;
613 for (; c != NULL((void*)0); c = c->next) {
614 if (!cookie_callback(c))
615 return false0;
616 }
617 }
618
619 /* Now, find next node to process. */
620 while (p != parent) {
621 if (p->next != NULL((void*)0)) {
622 /* Have a sibling, process that */
623 p = p->next;
624 break;
625 }
626
627 /* Ascend tree */
628 p = p->parent;
629 }
630 }
631 } while (p != parent);
632
633 return true1;
634}
635
636
637/**
638 * Check whether a host string is an IP address.
639 *
640 * This call detects IPv4 addresses (all of dotted-quad or subsets,
641 * decimal or hexadecimal notations) and IPv6 addresses (including
642 * those containing embedded IPv4 addresses.)
643 *
644 * \param host a hostname terminated by '\0'
645 * \return true if the hostname is an IP address, false otherwise
646 */
647static bool_Bool urldb__host_is_ip_address(const char *host)
648{
649 struct in_addr ipv4;
650 size_t host_len = strlen(host);
651 const char *sane_host;
652 const char *slash;
653#ifndef NO_IPV6
654 struct in6_addr ipv6;
655 char ipv6_addr[64];
656 unsigned int ipv6_addr_len;
657#endif
658 /**
659 * @todo FIXME Some parts of urldb.c make confusions between hosts
660 * and "prefixes", we can sometimes be erroneously passed more than
661 * just a host. Sometimes we may be passed trailing slashes, or even
662 * whole path segments. A specific criminal in this class is
663 * urldb_iterate_partial, which takes a prefix to search for, but
664 * passes that prefix to functions that expect only hosts.
665 *
666 * For the time being, we will accept such calls; we check if there
667 * is a / in the host parameter, and if there is, we take a copy and
668 * replace the / with a \0. This is not a permanent solution; we
669 * should search through NetSurf and find all the callers that are
670 * in error and fix them. When doing this task, it might be wise
671 * to replace the hideousness below with code that doesn't have to do
672 * this, and add assert(strchr(host, '/') == NULL); somewhere.
673 * -- rjek - 2010-11-04
674 */
675
676 slash = strchr(host, '/');
677 if (slash == NULL((void*)0)) {
678 sane_host = host;
679 } else {
680 char *c = strdup(host);
681 c[slash - host] = '\0';
682 sane_host = c;
683 host_len = slash - host;
684 NSLOG(netsurf, INFO, "WARNING: called with non-host '%s'",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 685
, }; nslog__log(&_nslog_ctx, "WARNING: called with non-host '%s'"
, host); } } while(0)
685 host)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 685
, }; nslog__log(&_nslog_ctx, "WARNING: called with non-host '%s'"
, host); } } while(0)
;
686 }
687
688 if (strspn(sane_host, "0123456789abcdefABCDEF[].:") < host_len)
689 goto out_false;
690
691 if (inet_aton(sane_host, &ipv4) != 0) {
692 /* This can only be a sane IPv4 address if it contains 3 dots.
693 * Helpfully, inet_aton is happy to treat "a", "a.b", "a.b.c",
694 * and "a.b.c.d" as valid IPv4 address strings where we only
695 * support the full, dotted-quad, form.
696 */
697 int num_dots = 0;
698 size_t index;
699
700 for (index = 0; index < host_len; index++) {
701 if (sane_host[index] == '.')
702 num_dots++;
703 }
704
705 if (num_dots == 3)
706 goto out_true;
707 else
708 goto out_false;
709 }
710
711#ifndef NO_IPV6
712 if ((host_len < 6) ||
713 (sane_host[0] != '[') ||
714 (sane_host[host_len - 1] != ']')) {
715 goto out_false;
716 }
717
718 ipv6_addr_len = host_len - 2;
719 if (ipv6_addr_len >= sizeof(ipv6_addr)) {
720 ipv6_addr_len = sizeof(ipv6_addr) - 1;
721 }
722 strncpy(ipv6_addr, sane_host + 1, ipv6_addr_len);
723 ipv6_addr[ipv6_addr_len] = '\0';
724
725 if (inet_pton(AF_INET610, ipv6_addr, &ipv6) == 1)
726 goto out_true;
727#endif
728
729out_false:
730 if (slash != NULL((void*)0)) free((void *)sane_host);
731 return false0;
732
733out_true:
734 if (slash != NULL((void*)0)) free((void *)sane_host);
735 return true1;
736}
737
738
739/**
740 * Compare host_part with prefix
741 *
742 * \param a host part
743 * \param b prefix
744 * \return 0 if match, non-zero, otherwise
745 */
746static int urldb_search_match_prefix(const struct host_part *a, const char *b)
747{
748 const char *end, *dot;
749 int plen, ret;
750
751 assert(a && a != &db_root && b)((a && a != &db_root && b) ? (void) (0) :
__assert_fail ("a && a != &db_root && b"
, "content/urldb.c", 751, __extension__ __PRETTY_FUNCTION__))
;
752
753 if (urldb__host_is_ip_address(b)) {
754 /* IP address */
755 return strncasecmp(a->part, b, strlen(b));
756 }
757
758 end = b + strlen(b) + 1;
759
760 while (b < end && a && a != &db_root) {
761 dot = strchr(b, '.');
762 if (!dot) {
763 /* last segment */
764 dot = end - 1;
765 }
766
767 /* Compare strings (length limited) */
768 if ((ret = strncasecmp(a->part, b, dot - b)) != 0)
769 /* didn't match => return difference */
770 return ret;
771
772 /* The strings matched */
773 if (dot < end - 1) {
774 /* Consider segment lengths only in the case
775 * where the prefix contains segments */
776 plen = strlen(a->part);
777 if (plen > dot - b) {
778 /* len(a) > len(b) */
779 return 1;
780 } else if (plen < dot - b) {
781 /* len(a) < len(b) */
782 return -1;
783 }
784 }
785
786 b = dot + 1;
787 a = a->parent;
788 }
789
790 /* If we get here then either:
791 * a) The path lengths differ
792 * or b) The hosts are identical
793 */
794 if (a && a != &db_root && b >= end) {
795 /* len(a) > len(b) => prefix matches */
796 return 0;
797 } else if ((!a || a == &db_root) && b < end) {
798 /* len(a) < len(b) => prefix does not match */
799 return -1;
800 }
801
802 /* Identical */
803 return 0;
804}
805
806
807/**
808 * Partial host iterator (internal)
809 *
810 * \param root Root of (sub)tree to traverse
811 * \param prefix Prefix to match
812 * \param callback Callback function
813 * \return true to continue, false otherwise
814 */
815static bool_Bool
816urldb_iterate_partial_host(struct search_node *root,
817 const char *prefix,
818 bool_Bool (*callback)(nsurl *url, const struct url_data *data))
819{
820 int c;
821
822 assert(root && prefix && callback)((root && prefix && callback) ? (void) (0) : __assert_fail
("root && prefix && callback", "content/urldb.c"
, 822, __extension__ __PRETTY_FUNCTION__))
;
823
824 if (root == &empty)
825 return true1;
826
827 c = urldb_search_match_prefix(root->data, prefix);
828
829 if (c > 0) {
830 /* No match => look in left subtree */
831 return urldb_iterate_partial_host(root->left,
832 prefix,
833 callback);
834 } else if (c < 0) {
835 /* No match => look in right subtree */
836 return urldb_iterate_partial_host(root->right,
837 prefix,
838 callback);
839 } else {
840 /* Match => iterate over l/r subtrees & process this node */
841 if (!urldb_iterate_partial_host(root->left,
842 prefix,
843 callback)) {
844 return false0;
845 }
846
847 if (root->data->paths.children) {
848 /* and extract all paths attached to this host */
849 if (!urldb_iterate_entries_path(&root->data->paths,
850 callback,
851 NULL((void*)0))) {
852 return false0;
853 }
854 }
855
856 if (!urldb_iterate_partial_host(root->right,
857 prefix,
858 callback)) {
859 return false0;
860 }
861 }
862
863 return true1;
864}
865
866
867/**
868 * Partial path iterator (internal)
869 *
870 * Given: http://www.example.org/a/b/c/d//e
871 * and assuming a path tree:
872 * ^
873 * / \
874 * a1 b1
875 * / \
876 * a2 b2
877 * /|\
878 * a b c
879 * 3 3 |
880 * d
881 * |
882 * e
883 * / \
884 * f g
885 *
886 * Prefix will be: p will be:
887 *
888 * a/b/c/d//e a1
889 * b/c/d//e a2
890 * b/c/d//e b3
891 * c/d//e a3
892 * c/d//e b3
893 * c/d//e c
894 * d//e d
895 * /e e (skip /)
896 * e e
897 *
898 * I.E. perform a breadth-first search of the tree.
899 *
900 * \param parent Root of (sub)tree to traverse
901 * \param prefix Prefix to match
902 * \param callback Callback function
903 * \return true to continue, false otherwise
904 */
905static bool_Bool
906urldb_iterate_partial_path(const struct path_data *parent,
907 const char *prefix,
908 bool_Bool (*callback)(nsurl *url, const struct url_data *data))
909{
910 const struct path_data *p = parent->children;
911 const char *slash, *end = prefix + strlen(prefix);
912
913 do {
914 slash = strchr(prefix, '/');
915 if (!slash) {
916 slash = end;
917 }
918
919 if (slash == prefix && *prefix == '/') {
920 /* Ignore "//" */
921 prefix++;
922 continue;
923 }
924
925 if (strncasecmp(p->segment, prefix, slash - prefix) == 0) {
926 /* prefix matches so far */
927 if (slash == end) {
928 /* we've run out of prefix, so all
929 * paths below this one match */
930 if (!urldb_iterate_entries_path(p,
931 callback,
932 NULL((void*)0))) {
933 return false0;
934 }
935
936 /* Progress to next sibling */
937 p = p->next;
938 } else {
939 /* Skip over this segment */
940 prefix = slash + 1;
941
942 p = p->children;
943 }
944 } else {
945 /* Doesn't match this segment, try next sibling */
946 p = p->next;
947 }
948 } while (p != NULL((void*)0));
949
950 return true1;
951}
952
953
954/**
955 * Host data iterator (internal)
956 *
957 * \param parent Root of subtree to iterate over
958 * \param url_callback Callback function
959 * \param cookie_callback Callback function
960 * \return true to continue, false otherwise
961 */
962static bool_Bool
963urldb_iterate_entries_host(struct search_node *parent,
964 bool_Bool (*url_callback)(nsurl *url, const struct url_data *data),
965 bool_Bool (*cookie_callback)(const struct cookie_data *data))
966{
967 if (parent == &empty) {
968 return true1;
969 }
970
971 if (!urldb_iterate_entries_host(parent->left,
972 url_callback,
973 cookie_callback)) {
974 return false0;
975 }
976
977 if ((parent->data->paths.children) ||
978 ((cookie_callback) &&
979 (parent->data->paths.cookies))) {
980 /* We have paths (or domain cookies), so iterate them */
981 if (!urldb_iterate_entries_path(&parent->data->paths,
982 url_callback,
983 cookie_callback)) {
984 return false0;
985 }
986 }
987
988 if (!urldb_iterate_entries_host(parent->right,
989 url_callback,
990 cookie_callback)) {
991 return false0;
992 }
993
994 return true1;
995}
996
997
998/**
999 * Add a host node to the tree
1000 *
1001 * \param part Host segment to add (or whole IP address) (copied)
1002 * \param parent Parent node to add to
1003 * \return Pointer to added node, or NULL on memory exhaustion
1004 */
1005static struct host_part *
1006urldb_add_host_node(const char *part, struct host_part *parent)
1007{
1008 struct host_part *d;
1009
1010 assert(part && parent)((part && parent) ? (void) (0) : __assert_fail ("part && parent"
, "content/urldb.c", 1010, __extension__ __PRETTY_FUNCTION__)
)
;
1011
1012 d = calloc(1, sizeof(struct host_part));
1013 if (!d) {
1014 return NULL((void*)0);
1015 }
1016
1017 d->part = strdup(part);
1018 if (!d->part) {
1019 free(d);
1020 return NULL((void*)0);
1021 }
1022
1023 d->next = parent->children;
1024 if (parent->children) {
1025 parent->children->prev = d;
1026 }
1027 d->parent = parent;
1028 parent->children = d;
1029
1030 return d;
1031}
1032
1033
1034/**
1035 * Fragment comparator callback for qsort
1036 *
1037 * \param a first value
1038 * \param b second value
1039 * \return 0 for equal else positive or negative value on comparison
1040 */
1041static int urldb_add_path_fragment_cmp(const void *a, const void *b)
1042{
1043 return strcasecmp(*((const char **) a), *((const char **) b));
1044}
1045
1046
1047/**
1048 * Add a fragment to a path segment
1049 *
1050 * \param segment Path segment to add to
1051 * \param fragment Fragment to add (copied), or NULL
1052 * \return segment or NULL on memory exhaustion
1053 */
1054static struct path_data *
1055urldb_add_path_fragment(struct path_data *segment, lwc_string *fragment)
1056{
1057 char **temp;
1058
1059 assert(segment)((segment) ? (void) (0) : __assert_fail ("segment", "content/urldb.c"
, 1059, __extension__ __PRETTY_FUNCTION__))
;
1060
1061 /* If no fragment, this function is a NOP
1062 * This may seem strange, but it makes the rest
1063 * of the code cleaner */
1064 if (!fragment)
1065 return segment;
1066
1067 temp = realloc(segment->fragment,
1068 (segment->frag_cnt + 1) * sizeof(char *));
1069 if (!temp)
1070 return NULL((void*)0);
1071
1072 segment->fragment = temp;
1073 segment->fragment[segment->frag_cnt] =
1074 strdup(lwc_string_data(fragment)({((fragment != ((void*)0)) ? (void) (0) : __assert_fail ("fragment != NULL"
, "content/urldb.c", 1074, __extension__ __PRETTY_FUNCTION__)
); (const char *)((fragment)+1);})
);
1075 if (!segment->fragment[segment->frag_cnt]) {
1076 /* Don't free temp - it's now our buffer */
1077 return NULL((void*)0);
1078 }
1079
1080 segment->frag_cnt++;
1081
1082 /* We want fragments in alphabetical order, so sort them
1083 * It may prove better to insert in alphabetical order instead */
1084 qsort(segment->fragment,
1085 segment->frag_cnt,
1086 sizeof (char *),
1087 urldb_add_path_fragment_cmp);
1088
1089 return segment;
1090}
1091
1092
1093/**
1094 * Add a path node to the tree
1095 *
1096 * \param scheme URL scheme associated with path (copied)
1097 * \param port Port number on host associated with path
1098 * \param segment Path segment to add (copied)
1099 * \param fragment URL fragment (copied), or NULL
1100 * \param parent Parent node to add to
1101 * \return Pointer to added node, or NULL on memory exhaustion
1102 */
1103static struct path_data *
1104urldb_add_path_node(lwc_string *scheme,
1105 unsigned int port,
1106 const char *segment,
1107 lwc_string *fragment,
1108 struct path_data *parent)
1109{
1110 struct path_data *d, *e;
1111
1112 assert(scheme && segment && parent)((scheme && segment && parent) ? (void) (0) :
__assert_fail ("scheme && segment && parent"
, "content/urldb.c", 1112, __extension__ __PRETTY_FUNCTION__)
)
;
1113
1114 d = calloc(1, sizeof(struct path_data));
1115 if (!d)
1116 return NULL((void*)0);
1117
1118 d->scheme = lwc_string_ref(scheme)({lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1118, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
++; __lwc_s;})
;
1119
1120 d->port = port;
1121
1122 d->segment = strdup(segment);
1123 if (!d->segment) {
1124 lwc_string_unref(d->scheme){ lwc_string *__lwc_s = (d->scheme); ((__lwc_s != ((void*)
0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1124, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1125 free(d);
1126 return NULL((void*)0);
1127 }
1128
1129 if (fragment) {
1130 if (!urldb_add_path_fragment(d, fragment)) {
1131 free(d->segment);
1132 lwc_string_unref(d->scheme){ lwc_string *__lwc_s = (d->scheme); ((__lwc_s != ((void*)
0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1132, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1133 free(d);
1134 return NULL((void*)0);
1135 }
1136 }
1137
1138 for (e = parent->children; e; e = e->next) {
1139 if (strcmp(e->segment, d->segment) > 0)
1140 break;
1141 }
1142
1143 if (e) {
1144 d->prev = e->prev;
1145 d->next = e;
1146 if (e->prev)
1147 e->prev->next = d;
1148 else
1149 parent->children = d;
1150 e->prev = d;
1151 } else if (!parent->children) {
1152 d->prev = d->next = NULL((void*)0);
1153 parent->children = parent->last = d;
1154 } else {
1155 d->next = NULL((void*)0);
1156 d->prev = parent->last;
1157 parent->last->next = d;
1158 parent->last = d;
1159 }
1160 d->parent = parent;
1161
1162 return d;
1163}
1164
1165
1166/**
1167 * Get the search tree for a particular host
1168 *
1169 * \param host the host to lookup
1170 * \return the corresponding search tree
1171 */
1172static struct search_node **urldb_get_search_tree_direct(const char *host)
1173{
1174 assert(host)((host) ? (void) (0) : __assert_fail ("host", "content/urldb.c"
, 1174, __extension__ __PRETTY_FUNCTION__))
;
1175
1176 if (urldb__host_is_ip_address(host)) {
1177 return &search_trees[ST_IP0];
1178 } else if (ascii_is_alpha(*host)) {
1179 return &search_trees[ST_DN2 + ascii_to_lower(*host) - 'a'];
1180 }
1181 return &search_trees[ST_EE1];
1182}
1183
1184
1185/**
1186 * Get the search tree for a particular host
1187 *
1188 * \param host the host to lookup
1189 * \return the corresponding search tree
1190 */
1191static struct search_node *urldb_get_search_tree(const char *host)
1192{
1193 return *urldb_get_search_tree_direct(host);
1194}
1195
1196
1197/**
1198 * Compare host part with a string
1199 *
1200 * \param a host part
1201 * \param b string to compare
1202 * \return 0 if match, non-zero, otherwise
1203 */
1204static int urldb_search_match_string(const struct host_part *a, const char *b)
1205{
1206 const char *end, *dot;
1207 int plen, ret;
1208
1209 assert(a && a != &db_root && b)((a && a != &db_root && b) ? (void) (0) :
__assert_fail ("a && a != &db_root && b"
, "content/urldb.c", 1209, __extension__ __PRETTY_FUNCTION__)
)
;
1210
1211 if (urldb__host_is_ip_address(b)) {
1212 /* IP address */
1213 return strcasecmp(a->part, b);
1214 }
1215
1216 end = b + strlen(b) + 1;
1217
1218 while (b < end && a && a != &db_root) {
1219 dot = strchr(b, '.');
1220 if (!dot) {
1221 /* last segment */
1222 dot = end - 1;
1223 }
1224
1225 /* Compare strings (length limited) */
1226 if ((ret = strncasecmp(a->part, b, dot - b)) != 0)
1227 /* didn't match => return difference */
1228 return ret;
1229
1230 /* The strings matched, now check that the lengths do, too */
1231 plen = strlen(a->part);
1232
1233 if (plen > dot - b) {
1234 /* len(a) > len(b) */
1235 return 1;
1236 } else if (plen < dot - b) {
1237 /* len(a) < len(b) */
1238 return -1;
1239 }
1240
1241 b = dot + 1;
1242 a = a->parent;
1243 }
1244
1245 /* If we get here then either:
1246 * a) The path lengths differ
1247 * or b) The hosts are identical
1248 */
1249 if (a && a != &db_root && b >= end) {
1250 /* len(a) > len(b) */
1251 return 1;
1252 } else if ((!a || a == &db_root) && b < end) {
1253 /* len(a) < len(b) */
1254 return -1;
1255 }
1256
1257 /* Identical */
1258 return 0;
1259}
1260
1261
1262/**
1263 * Find a node in a search tree
1264 *
1265 * \param root Tree to look in
1266 * \param host Host to find
1267 * \return Pointer to host tree node, or NULL if not found
1268 */
1269static const struct host_part *
1270urldb_search_find(struct search_node *root, const char *host)
1271{
1272 int c;
1273
1274 assert(root && host)((root && host) ? (void) (0) : __assert_fail ("root && host"
, "content/urldb.c", 1274, __extension__ __PRETTY_FUNCTION__)
)
;
1275
1276 if (root == &empty) {
1277 return NULL((void*)0);
1278 }
1279
1280 c = urldb_search_match_string(root->data, host);
1281
1282 if (c > 0) {
1283 return urldb_search_find(root->left, host);
1284 } else if (c < 0) {
1285 return urldb_search_find(root->right, host);
1286 }
1287
1288 return root->data;
1289}
1290
1291
1292/**
1293 * Match a path string
1294 *
1295 * \param parent Path (sub)tree to look in
1296 * \param path The path to search for
1297 * \param scheme The URL scheme associated with the path
1298 * \param port The port associated with the path
1299 * \return Pointer to path data or NULL if not found.
1300 */
1301static struct path_data *
1302urldb_match_path(const struct path_data *parent,
1303 const char *path,
1304 lwc_string *scheme,
1305 unsigned short port)
1306{
1307 const struct path_data *p;
1308 const char *slash;
1309 bool_Bool match;
1310
1311 assert(parent != NULL)((parent != ((void*)0)) ? (void) (0) : __assert_fail ("parent != NULL"
, "content/urldb.c", 1311, __extension__ __PRETTY_FUNCTION__)
)
;
1312 assert(parent->segment == NULL)((parent->segment == ((void*)0)) ? (void) (0) : __assert_fail
("parent->segment == NULL", "content/urldb.c", 1312, __extension__
__PRETTY_FUNCTION__))
;
1313
1314 if (path[0] != '/') {
1315 NSLOG(netsurf, INFO, "path is %s", path)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1315
, }; nslog__log(&_nslog_ctx, "path is %s", path); } } while
(0)
;
1316 }
1317
1318 assert(path[0] == '/')((path[0] == '/') ? (void) (0) : __assert_fail ("path[0] == '/'"
, "content/urldb.c", 1318, __extension__ __PRETTY_FUNCTION__)
)
;
1319
1320 /* Start with children, as parent has no segment */
1321 p = parent->children;
1322
1323 while (p != NULL((void*)0)) {
1324 slash = strchr(path + 1, '/');
1325 if (!slash) {
1326 slash = path + strlen(path);
1327 }
1328
1329 if (strncmp(p->segment, path + 1, slash - path - 1) == 0 &&
1330 lwc_string_isequal(p->scheme, scheme, &match)((*(&match) = ((p->scheme) == (scheme))), lwc_error_ok
)
== lwc_error_ok &&
1331 match == true1 &&
1332 p->port == port) {
1333 if (*slash == '\0') {
1334 /* Complete match */
1335 return (struct path_data *) p;
1336 }
1337
1338 /* Match so far, go down tree */
1339 p = p->children;
1340
1341 path = slash;
1342 } else {
1343 /* No match, try next sibling */
1344 p = p->next;
1345 }
1346 }
1347
1348 return NULL((void*)0);
1349}
1350
1351
1352/**
1353 * Find an URL in the database
1354 *
1355 * \param url Absolute URL to find
1356 * \return Pointer to path data, or NULL if not found
1357 */
1358static struct path_data *urldb_find_url(nsurl *url)
1359{
1360 const struct host_part *h;
1361 struct path_data *p;
1362 struct search_node *tree;
1363 char *plq;
1364 const char *host_str;
1365 lwc_string *scheme, *host, *port;
1366 size_t len = 0;
1367 unsigned int port_int;
1368 bool_Bool match;
1369
1370 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 1370, __extension__ __PRETTY_FUNCTION__))
;
1371
1372 if (url_bloom != NULL((void*)0)) {
1373 if (bloom_search_hash(url_bloom, nsurl_hash(url)) == false0) {
1374 return NULL((void*)0);
1375 }
1376 }
1377
1378 scheme = nsurl_get_component(url, NSURL_SCHEME);
1379 if (scheme == NULL((void*)0))
1380 return NULL((void*)0);
1381
1382 if (lwc_string_isequal(scheme, corestring_lwc_mailto, &match)((*(&match) = ((scheme) == (corestring_lwc_mailto))), lwc_error_ok
)
==
1383 lwc_error_ok && match == true1) {
1384 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1384, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1385 return NULL((void*)0);
1386 }
1387
1388 host = nsurl_get_component(url, NSURL_HOST);
1389 if (host != NULL((void*)0)) {
1390 host_str = lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 1390, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
;
1391 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 1391
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
1392
1393 } else if (lwc_string_isequal(scheme, corestring_lwc_file, &match)((*(&match) = ((scheme) == (corestring_lwc_file))), lwc_error_ok
)
==
1394 lwc_error_ok && match == true1) {
1395 host_str = "localhost";
1396
1397 } else {
1398 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1398, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1399 return NULL((void*)0);
1400 }
1401
1402 tree = urldb_get_search_tree(host_str);
1403 h = urldb_search_find(tree, host_str);
1404 if (!h) {
1405 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1405, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1406 return NULL((void*)0);
1407 }
1408
1409 /* generate plq (path, leaf, query) */
1410 if (nsurl_get(url, NSURL_PATH | NSURL_QUERY, &plq, &len) != NSERROR_OK) {
1411 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1411, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1412 return NULL((void*)0);
1413 }
1414
1415 /* Get port */
1416 port = nsurl_get_component(url, NSURL_PORT);
1417 if (port != NULL((void*)0)) {
1418 port_int = atoi(lwc_string_data(port)({((port != ((void*)0)) ? (void) (0) : __assert_fail ("port != NULL"
, "content/urldb.c", 1418, __extension__ __PRETTY_FUNCTION__)
); (const char *)((port)+1);})
);
1419 lwc_string_unref(port){ lwc_string *__lwc_s = (port); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 1419
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
1420 } else {
1421 port_int = 0;
1422 }
1423
1424 p = urldb_match_path(&h->paths, plq, scheme, port_int);
1425
1426 free(plq);
1427 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 1427, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
1428
1429 return p;
1430}
1431
1432
1433/**
1434 * Dump URL database paths to stderr
1435 *
1436 * \param parent Parent node of tree to dump
1437 */
1438static void urldb_dump_paths(struct path_data *parent)
1439{
1440 const struct path_data *p = parent;
1441 unsigned int i;
1442
1443 do {
1444 if (p->segment != NULL((void*)0)) {
1445 NSLOG(netsurf, INFO, "\t%s : %u",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1446
, }; nslog__log(&_nslog_ctx, "\t%s : %u", ({((p->scheme
!= ((void*)0)) ? (void) (0) : __assert_fail ("p->scheme != NULL"
, "content/urldb.c", 1446, __extension__ __PRETTY_FUNCTION__)
); (const char *)((p->scheme)+1);}), p->port); } } while
(0)
1446 lwc_string_data(p->scheme), p->port)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1446
, }; nslog__log(&_nslog_ctx, "\t%s : %u", ({((p->scheme
!= ((void*)0)) ? (void) (0) : __assert_fail ("p->scheme != NULL"
, "content/urldb.c", 1446, __extension__ __PRETTY_FUNCTION__)
); (const char *)((p->scheme)+1);}), p->port); } } while
(0)
;
1447
1448 NSLOG(netsurf, INFO, "\t\t'%s'", p->segment)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1448
, }; nslog__log(&_nslog_ctx, "\t\t'%s'", p->segment); }
} while(0)
;
1449
1450 for (i = 0; i != p->frag_cnt; i++) {
1451 NSLOG(netsurf, INFO, "\t\t\t#%s",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1452
, }; nslog__log(&_nslog_ctx, "\t\t\t#%s", p->fragment[
i]); } } while(0)
1452 p->fragment[i])do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1452
, }; nslog__log(&_nslog_ctx, "\t\t\t#%s", p->fragment[
i]); } } while(0)
;
1453 }
1454 }
1455
1456 if (p->children != NULL((void*)0)) {
1457 p = p->children;
1458 } else {
1459 while (p != parent) {
1460 if (p->next != NULL((void*)0)) {
1461 p = p->next;
1462 break;
1463 }
1464
1465 p = p->parent;
1466 }
1467 }
1468 } while (p != parent);
1469}
1470
1471
1472/**
1473 * Dump URL database hosts to stderr
1474 *
1475 * \param parent Parent node of tree to dump
1476 */
1477static void urldb_dump_hosts(struct host_part *parent)
1478{
1479 struct host_part *h;
1480
1481 if (parent->part) {
1482 NSLOG(netsurf, INFO, "%s", parent->part)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1482
, }; nslog__log(&_nslog_ctx, "%s", parent->part); } } while
(0)
;
1483
1484 NSLOG(netsurf, INFO, "\t%s invalid SSL certs",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1485
, }; nslog__log(&_nslog_ctx, "\t%s invalid SSL certs", parent
->permit_invalid_certs ? "Permits" : "Denies"); } } while(
0)
1485 parent->permit_invalid_certs ? "Permits" : "Denies")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1485
, }; nslog__log(&_nslog_ctx, "\t%s invalid SSL certs", parent
->permit_invalid_certs ? "Permits" : "Denies"); } } while(
0)
;
1486 }
1487
1488 /* Dump path data */
1489 urldb_dump_paths(&parent->paths);
1490
1491 /* and recurse */
1492 for (h = parent->children; h; h = h->next) {
1493 urldb_dump_hosts(h);
1494 }
1495}
1496
1497
1498/**
1499 * Dump search tree
1500 *
1501 * \param parent Parent node of tree to dump
1502 * \param depth Tree depth
1503 */
1504static void urldb_dump_search(struct search_node *parent, int depth)
1505{
1506 const struct host_part *h;
1507 int i; /* index into string */
1508 char s[1024];
1509 int r;
1510 int sl = sizeof(s) - 2;
1511
1512 if (parent == &empty)
1513 return;
1514
1515 urldb_dump_search(parent->left, depth + 1);
1516
1517 for (i = 0; i != depth; i++) {
1518 s[i] = ' ';
1519 }
1520
1521 for (h = parent->data; h; h = h->parent) {
1522 if (h->part) {
1523 r = snprintf(&s[i], sl - i, "%s", h->part);
1524 if (r < 0) {
1525 break;
1526 }
1527 if ((i + r) >= sl) {
1528 break;
1529 }
1530 i += r;
1531 }
1532
1533 if (h->parent && h->parent->parent) {
1534 s[i]='.';
1535 i++;
1536 }
1537 }
1538 s[i]= 0;
1539
1540 NSLOG(netsurf, INFO, "%s", s)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1540
, }; nslog__log(&_nslog_ctx, "%s", s); } } while(0)
;
1541
1542 urldb_dump_search(parent->right, depth + 1);
1543}
1544
1545
1546/**
1547 * Compare a pair of host parts
1548 *
1549 * \param a first host part
1550 * \param b second host part
1551 * \return 0 if match, non-zero, otherwise
1552 */
1553static int
1554urldb_search_match_host(const struct host_part *a, const struct host_part *b)
1555{
1556 int ret;
1557
1558 assert(a && b)((a && b) ? (void) (0) : __assert_fail ("a && b"
, "content/urldb.c", 1558, __extension__ __PRETTY_FUNCTION__)
)
;
1559
1560 /* traverse up tree to root, comparing parts as we go. */
1561 for (; a && a != &db_root && b && b != &db_root;
1562 a = a->parent, b = b->parent) {
1563 if ((ret = strcasecmp(a->part, b->part)) != 0) {
1564 /* They differ => return the difference here */
1565 return ret;
1566 }
1567 }
1568
1569 /* If we get here then either:
1570 * a) The path lengths differ
1571 * or b) The hosts are identical
1572 */
1573 if (a && a != &db_root && (!b || b == &db_root)) {
1574 /* len(a) > len(b) */
1575 return 1;
1576 } else if ((!a || a == &db_root) && b && b != &db_root) {
1577 /* len(a) < len(b) */
1578 return -1;
1579 }
1580
1581 /* identical */
1582 return 0;
1583}
1584
1585
1586/**
1587 * Rotate a subtree right
1588 *
1589 * \param root Root of subtree to rotate
1590 * \return new root of subtree
1591 */
1592static struct search_node *urldb_search_skew(struct search_node *root)
1593{
1594 assert(root)((root) ? (void) (0) : __assert_fail ("root", "content/urldb.c"
, 1594, __extension__ __PRETTY_FUNCTION__))
;
1595
1596 if (root->left->level == root->level) {
1597 struct search_node *temp;
1598
1599 temp = root->left;
1600 root->left = temp->right;
1601 temp->right = root;
1602 root = temp;
1603 }
1604
1605 return root;
1606}
1607
1608
1609/**
1610 * Rotate a node left, increasing the parent's level
1611 *
1612 * \param root Root of subtree to rotate
1613 * \return New root of subtree
1614 */
1615static struct search_node *urldb_search_split(struct search_node *root)
1616{
1617 assert(root)((root) ? (void) (0) : __assert_fail ("root", "content/urldb.c"
, 1617, __extension__ __PRETTY_FUNCTION__))
;
1618
1619 if (root->right->right->level == root->level) {
1620 struct search_node *temp;
1621
1622 temp = root->right;
1623 root->right = temp->left;
1624 temp->left = root;
1625 root = temp;
1626
1627 root->level++;
1628 }
1629
1630 return root;
1631}
1632
1633
1634/**
1635 * Insert node into search tree
1636 *
1637 * \param root Root of (sub)tree to insert into
1638 * \param n Node to insert
1639 * \return Pointer to updated root
1640 */
1641static struct search_node *
1642urldb_search_insert_internal(struct search_node *root, struct search_node *n)
1643{
1644 assert(root && n)((root && n) ? (void) (0) : __assert_fail ("root && n"
, "content/urldb.c", 1644, __extension__ __PRETTY_FUNCTION__)
)
;
1645
1646 if (root == &empty) {
1647 root = n;
1648 } else {
1649 int c = urldb_search_match_host(root->data, n->data);
1650
1651 if (c > 0) {
1652 root->left = urldb_search_insert_internal(
1653 root->left, n);
1654 } else if (c < 0) {
1655 root->right = urldb_search_insert_internal(
1656 root->right, n);
1657 } else {
1658 /* exact match */
1659 free(n);
1660 return root;
1661 }
1662
1663 root = urldb_search_skew(root);
1664 root = urldb_search_split(root);
1665 }
1666
1667 return root;
1668}
1669
1670
1671/**
1672 * Insert a node into the search tree
1673 *
1674 * \param root Root of tree to insert into
1675 * \param data User data to insert
1676 * \return Pointer to updated root, or NULL if failed
1677 */
1678static struct search_node *
1679urldb_search_insert(struct search_node *root, const struct host_part *data)
1680{
1681 struct search_node *n;
1682
1683 assert(root && data)((root && data) ? (void) (0) : __assert_fail ("root && data"
, "content/urldb.c", 1683, __extension__ __PRETTY_FUNCTION__)
)
;
1684
1685 n = malloc(sizeof(struct search_node));
1686 if (!n)
1687 return NULL((void*)0);
1688
1689 n->level = 1;
1690 n->data = data;
1691 n->left = n->right = &empty;
1692
1693 root = urldb_search_insert_internal(root, n);
1694
1695 return root;
1696}
1697
1698
1699/**
1700 * Parse a cookie avpair
1701 *
1702 * \param c Cookie struct to populate
1703 * \param n Name component
1704 * \param v Value component
1705 * \param was_quoted Whether \a v was quoted in the input
1706 * \return true on success, false on memory exhaustion
1707 */
1708static bool_Bool
1709urldb_parse_avpair(struct cookie_internal_data *c,
1710 char *n,
1711 char *v,
1712 bool_Bool was_quoted)
1713{
1714 int vlen;
1715
1716 assert(c && n && v)((c && n && v) ? (void) (0) : __assert_fail (
"c && n && v", "content/urldb.c", 1716, __extension__
__PRETTY_FUNCTION__))
;
1717
1718 /* Strip whitespace from start of name */
1719 for (; *n; n++) {
1720 if (*n != ' ' && *n != '\t')
1721 break;
1722 }
1723
1724 /* Strip whitespace from end of name */
1725 for (vlen = strlen(n); vlen; vlen--) {
1726 if (n[vlen] == ' ' || n[vlen] == '\t')
1727 n[vlen] = '\0';
1728 else
1729 break;
1730 }
1731
1732 /* Strip whitespace from start of value */
1733 for (; *v; v++) {
1734 if (*v != ' ' && *v != '\t')
1735 break;
1736 }
1737
1738 /* Strip whitespace from end of value */
1739 for (vlen = strlen(v); vlen; vlen--) {
1740 if (v[vlen] == ' ' || v[vlen] == '\t')
1741 v[vlen] = '\0';
1742 else
1743 break;
1744 }
1745
1746 if (!c->comment && strcasecmp(n, "Comment") == 0) {
1747 c->comment = strdup(v);
1748 if (!c->comment)
1749 return false0;
1750 } else if (!c->domain && strcasecmp(n, "Domain") == 0) {
1751 if (v[0] == '.') {
1752 /* Domain must start with a dot */
1753 c->domain_from_set = true1;
1754 c->domain = strdup(v);
1755 if (!c->domain)
1756 return false0;
1757 }
1758 } else if (strcasecmp(n, "Max-Age") == 0) {
1759 int temp = atoi(v);
1760 if (temp == 0)
1761 /* Special case - 0 means delete */
1762 c->expires = 0;
1763 else
1764 c->expires = time(NULL((void*)0)) + temp;
1765 } else if (!c->path && strcasecmp(n, "Path") == 0) {
1766 c->path_from_set = true1;
1767 c->path = strdup(v);
1768 if (!c->path)
1769 return false0;
1770 } else if (strcasecmp(n, "Version") == 0) {
1771 c->version = atoi(v);
1772 } else if (strcasecmp(n, "Expires") == 0) {
1773 char *datenoday;
1774 time_t expires;
1775 nserror res;
1776
1777 /* Strip dayname from date (these are hugely variable
1778 * and liable to break the parser. They also serve no
1779 * useful purpose) */
1780 for (datenoday = v;
1781 *datenoday && !ascii_is_digit(*datenoday);
1782 datenoday++) {
1783 /* do nothing */
1784 }
1785
1786 res = nsc_strntimet(datenoday, strlen(datenoday), &expires);
1787 if (res != NSERROR_OK) {
1788 /* assume we have an unrepresentable date =>
1789 * force it to the maximum possible value of a
1790 * 32bit time_t (this may break in 2038. We'll
1791 * deal with that once we come to it) */
1792 expires = (time_t)0x7fffffff;
1793 }
1794 c->expires = expires;
1795 } else if (strcasecmp(n, "Secure") == 0) {
1796 c->secure = true1;
1797 } else if (strcasecmp(n, "HttpOnly") == 0) {
1798 c->http_only = true1;
1799 } else if (!c->name) {
1800 c->name = strdup(n);
1801 c->value = strdup(v);
1802 c->value_was_quoted = was_quoted;
1803 if (!c->name || !c->value) {
1804 return false0;
1805 }
1806 }
1807
1808 return true1;
1809}
1810
1811
1812/**
1813 * Free a cookie
1814 *
1815 * \param c The cookie to free
1816 */
1817static void urldb_free_cookie(struct cookie_internal_data *c)
1818{
1819 assert(c)((c) ? (void) (0) : __assert_fail ("c", "content/urldb.c", 1819
, __extension__ __PRETTY_FUNCTION__))
;
1820
1821 free(c->comment);
1822 free(c->domain);
1823 free(c->path);
1824 free(c->name);
1825 free(c->value);
1826 free(c);
1827}
1828
1829
1830/**
1831 * Parse a cookie
1832 *
1833 * \param url URL being fetched
1834 * \param cookie Pointer to cookie string (updated on exit)
1835 * \return Pointer to cookie structure (on heap, caller frees) or NULL
1836 */
1837static struct cookie_internal_data *
1838urldb_parse_cookie(nsurl *url, const char **cookie)
1839{
1840 struct cookie_internal_data *c;
1841 const char *cur;
1842 char name[1024], value[4096];
1843 char *n = name, *v = value;
1844 bool_Bool in_value = false0;
1845 bool_Bool had_value_data = false0;
1846 bool_Bool value_verbatim = false0;
1847 bool_Bool quoted = false0;
1848 bool_Bool was_quoted = false0;
1849
1850 assert(url && cookie && *cookie)((url && cookie && *cookie) ? (void) (0) : __assert_fail
("url && cookie && *cookie", "content/urldb.c"
, 1850, __extension__ __PRETTY_FUNCTION__))
;
1851
1852 c = calloc(1, sizeof(struct cookie_internal_data));
1853 if (c == NULL((void*)0))
1854 return NULL((void*)0);
1855
1856 c->expires = -1;
1857
1858 name[0] = '\0';
1859 value[0] = '\0';
1860
1861 for (cur = *cookie; *cur; cur++) {
1862 if (*cur == '\r' && *(cur + 1) == '\n') {
1863 /* End of header */
1864 if (quoted) {
1865 /* Unmatched quote encountered */
1866
1867 /* Match Firefox 2.0.0.11 */
1868 value[0] = '\0';
1869
1870 }
1871
1872 break;
1873 } else if (*cur == '\r') {
1874 /* Spurious linefeed */
1875 continue;
1876 } else if (*cur == '\n') {
1877 /* Spurious newline */
1878 continue;
1879 }
1880
1881 if (in_value && !had_value_data) {
1882 if (*cur == ' ' || *cur == '\t') {
1883 /* Strip leading whitespace from value */
1884 continue;
1885 } else {
1886 had_value_data = true1;
1887
1888 /* Value is taken verbatim if first non-space
1889 * character is not a " */
1890 if (*cur != '"') {
1891 value_verbatim = true1;
1892 }
1893 }
1894 }
1895
1896 if (in_value && !value_verbatim && (*cur == '"')) {
1897 /* Only non-verbatim values may be quoted */
1898 if (cur == *cookie || *(cur - 1) != '\\') {
1899 /* Only unescaped quotes count */
1900 was_quoted = quoted;
1901 quoted = !quoted;
1902
1903 continue;
1904 }
1905 }
1906
1907 if (!quoted && !in_value && *cur == '=') {
1908 /* First equals => attr-value separator */
1909 in_value = true1;
1910 continue;
1911 }
1912
1913 if (!quoted && (was_quoted || *cur == ';')) {
1914 /* Semicolon or after quoted value
1915 * => end of current avpair */
1916
1917 /* NUL-terminate tokens */
1918 *n = '\0';
1919 *v = '\0';
1920
1921 if (!urldb_parse_avpair(c, name, value, was_quoted)) {
1922 /* Memory exhausted */
1923 urldb_free_cookie(c);
1924 return NULL((void*)0);
1925 }
1926
1927 /* And reset to start */
1928 n = name;
1929 v = value;
1930 in_value = false0;
1931 had_value_data = false0;
1932 value_verbatim = false0;
1933 was_quoted = false0;
1934
1935 /* Now, if the current input is anything other than a
1936 * semicolon, we must be sure to reprocess it */
1937 if (*cur != ';') {
1938 cur--;
1939 }
1940
1941 continue;
1942 }
1943
1944 /* And now handle commas. These are a pain as they may mean
1945 * any of the following:
1946 *
1947 * + End of cookie
1948 * + Day separator in Expires avpair
1949 * + (Invalid) comma in unquoted value
1950 *
1951 * Therefore, in order to handle all 3 cases (2 and 3 are
1952 * identical, the difference being that 2 is in the spec and
1953 * 3 isn't), we need to determine where the comma actually
1954 * lies. We use the following heuristic:
1955 *
1956 * Given a comma at the current input position, find the
1957 * immediately following semicolon (or end of input if none
1958 * found). Then, consider the input characters between
1959 * these two positions. If any of these characters is an
1960 * '=', we must assume that the comma signified the end of
1961 * the current cookie.
1962 *
1963 * This holds as the first avpair of any cookie must be
1964 * NAME=VALUE, so the '=' is guaranteed to appear in the
1965 * case where the comma marks the end of a cookie.
1966 *
1967 * This will fail, however, in the case where '=' appears in
1968 * the value of the current avpair after the comma or the
1969 * subsequent cookie does not start with NAME=VALUE. Neither
1970 * of these is particularly likely and if they do occur, the
1971 * website is more broken than we can be bothered to handle.
1972 */
1973 if (!quoted && *cur == ',') {
1974 /* Find semi-colon, if any */
1975 const char *p;
1976 const char *semi = strchr(cur + 1, ';');
1977 if (!semi)
1978 semi = cur + strlen(cur) - 2 /* CRLF */;
1979
1980 /* Look for equals sign between comma and semi */
1981 for (p = cur + 1; p < semi; p++)
1982 if (*p == '=')
1983 break;
1984
1985 if (p == semi) {
1986 /* none found => comma internal to value */
1987 /* do nothing */
1988 } else {
1989 /* found one => comma marks end of cookie */
1990 cur++;
1991 break;
1992 }
1993 }
1994
1995 /* Accumulate into buffers, always leaving space for a NUL */
1996 /** \todo is silently truncating overlong names/values wise? */
1997 if (!in_value) {
1998 if (n < name + (sizeof(name) - 1))
1999 *n++ = *cur;
2000 } else {
2001 if (v < value + (sizeof(value) - 1))
2002 *v++ = *cur;
2003 }
2004 }
2005
2006 /* Parse final avpair */
2007 *n = '\0';
2008 *v = '\0';
2009
2010 if (!urldb_parse_avpair(c, name, value, was_quoted)) {
2011 /* Memory exhausted */
2012 urldb_free_cookie(c);
2013 return NULL((void*)0);
2014 }
2015
2016 /* Now fix-up default values */
2017 if (c->domain == NULL((void*)0)) {
2018 lwc_string *host = nsurl_get_component(url, NSURL_HOST);
2019 if (host == NULL((void*)0)) {
2020 urldb_free_cookie(c);
2021 return NULL((void*)0);
2022 }
2023 c->domain = strdup(lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 2023, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
);
2024 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 2024
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
2025 }
2026
2027 if (c->path == NULL((void*)0)) {
2028 const char *path_data;
2029 char *path, *slash;
2030 lwc_string *path_lwc;
2031
2032 path_lwc = nsurl_get_component(url, NSURL_PATH);
2033 if (path_lwc == NULL((void*)0)) {
2034 urldb_free_cookie(c);
2035 return NULL((void*)0);
2036 }
2037 path_data = lwc_string_data(path_lwc)({((path_lwc != ((void*)0)) ? (void) (0) : __assert_fail ("path_lwc != NULL"
, "content/urldb.c", 2037, __extension__ __PRETTY_FUNCTION__)
); (const char *)((path_lwc)+1);})
;
2038
2039 /* Strip leafname and trailing slash (4.3.1) */
2040 slash = strrchr(path_data, '/');
2041 if (slash != NULL((void*)0)) {
2042 /* Special case: retain first slash in path */
2043 if (slash == path_data)
2044 slash++;
2045
2046 slash = strndup(path_data, slash - path_data);
2047 if (slash == NULL((void*)0)) {
2048 lwc_string_unref(path_lwc){ lwc_string *__lwc_s = (path_lwc); ((__lwc_s != ((void*)0)) ?
(void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 2048, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
2049 urldb_free_cookie(c);
2050 return NULL((void*)0);
2051 }
2052
2053 path = slash;
2054 lwc_string_unref(path_lwc){ lwc_string *__lwc_s = (path_lwc); ((__lwc_s != ((void*)0)) ?
(void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 2054, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
2055 } else {
2056 path = strdup(lwc_string_data(path_lwc)({((path_lwc != ((void*)0)) ? (void) (0) : __assert_fail ("path_lwc != NULL"
, "content/urldb.c", 2056, __extension__ __PRETTY_FUNCTION__)
); (const char *)((path_lwc)+1);})
);
2057 lwc_string_unref(path_lwc){ lwc_string *__lwc_s = (path_lwc); ((__lwc_s != ((void*)0)) ?
(void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 2057, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
2058 if (path == NULL((void*)0)) {
2059 urldb_free_cookie(c);
2060 return NULL((void*)0);
2061 }
2062 }
2063
2064 c->path = path;
2065 }
2066
2067 /* Write back current position */
2068 *cookie = cur;
2069
2070 return c;
2071}
2072
2073
2074/**
2075 * Add a path to the database, creating any intermediate entries
2076 *
2077 * \param scheme URL scheme associated with path
2078 * \param port Port number on host associated with path
2079 * \param host Host tree node to attach to
2080 * \param path_query Absolute path plus query to add (freed)
2081 * \param fragment URL fragment, or NULL
2082 * \param url URL (fragment ignored)
2083 * \return Pointer to leaf node, or NULL on memory exhaustion
2084 */
2085static struct path_data *
2086urldb_add_path(lwc_string *scheme,
2087 unsigned int port,
2088 const struct host_part *host,
2089 char *path_query,
2090 lwc_string *fragment,
2091 nsurl *url)
2092{
2093 struct path_data *d, *e;
2094 char *buf = path_query;
2095 char *segment, *slash;
2096 bool_Bool match;
2097
2098 assert(scheme && host && url)((scheme && host && url) ? (void) (0) : __assert_fail
("scheme && host && url", "content/urldb.c",
2098, __extension__ __PRETTY_FUNCTION__))
;
2099
2100 d = (struct path_data *) &host->paths;
2101
2102 /* skip leading '/' */
2103 segment = buf;
2104 if (*segment == '/')
2105 segment++;
2106
2107 /* Process path segments */
2108 do {
2109 slash = strchr(segment, '/');
2110 if (!slash) {
2111 /* last segment */
2112 /* look for existing entry */
2113 for (e = d->children; e; e = e->next)
2114 if (strcmp(segment, e->segment) == 0 &&
2115 lwc_string_isequal(scheme,((*(&match) = ((scheme) == (e->scheme))), lwc_error_ok
)
2116 e->scheme, &match)((*(&match) = ((scheme) == (e->scheme))), lwc_error_ok
)
==
2117 lwc_error_ok &&
2118 match == true1 &&
2119 e->port == port)
2120 break;
2121
2122 d = e ? urldb_add_path_fragment(e, fragment) :
2123 urldb_add_path_node(scheme, port,
2124 segment, fragment, d);
2125 break;
2126 }
2127
2128 *slash = '\0';
2129
2130 /* look for existing entry */
2131 for (e = d->children; e; e = e->next)
2132 if (strcmp(segment, e->segment) == 0 &&
2133 lwc_string_isequal(scheme, e->scheme,((*(&match) = ((scheme) == (e->scheme))), lwc_error_ok
)
2134 &match)((*(&match) = ((scheme) == (e->scheme))), lwc_error_ok
)
== lwc_error_ok &&
2135 match == true1 &&
2136 e->port == port)
2137 break;
2138
2139 d = e ? e : urldb_add_path_node(scheme, port, segment, NULL((void*)0), d);
2140 if (!d)
2141 break;
2142
2143 segment = slash + 1;
2144 } while (1);
2145
2146 free(path_query);
2147
2148 if (d && !d->url) {
2149 /* Insert defragmented URL */
2150 if (nsurl_defragment(url, &d->url) != NSERROR_OK)
2151 return NULL((void*)0);
2152 }
2153
2154 return d;
2155}
2156
2157
2158/**
2159 * Add a host to the database, creating any intermediate entries
2160 *
2161 * \param host Hostname to add
2162 * \return Pointer to leaf node, or NULL on memory exhaustion
2163 */
2164static struct host_part *urldb_add_host(const char *host)
2165{
2166 struct host_part *d = (struct host_part *) &db_root, *e;
2167 struct search_node *s;
2168 char buf[256]; /* 256 bytes is sufficient - domain names are
2169 * limited to 255 chars. */
2170 char *part;
2171
2172 assert(host)((host) ? (void) (0) : __assert_fail ("host", "content/urldb.c"
, 2172, __extension__ __PRETTY_FUNCTION__))
;
2173
2174 if (urldb__host_is_ip_address(host)) {
2175 /* Host is an IP, so simply add as TLD */
2176
2177 /* Check for existing entry */
2178 for (e = d->children; e; e = e->next)
2179 if (strcasecmp(host, e->part) == 0)
2180 /* found => return it */
2181 return e;
2182
2183 d = urldb_add_host_node(host, d);
2184
2185 s = urldb_search_insert(search_trees[ST_IP0], d);
2186 if (!s) {
2187 /* failed */
2188 d = NULL((void*)0);
2189 } else {
2190 search_trees[ST_IP0] = s;
2191 }
2192
2193 return d;
2194 }
2195
2196 /* Copy host string, so we can corrupt it */
2197 strncpy(buf, host, sizeof buf);
2198 buf[sizeof buf - 1] = '\0';
2199
2200 /* Process FQDN segments backwards */
2201 do {
2202 part = strrchr(buf, '.');
2203 if (!part) {
2204 /* last segment */
2205 /* Check for existing entry */
2206 for (e = d->children; e; e = e->next)
2207 if (strcasecmp(buf, e->part) == 0)
2208 break;
2209
2210 if (e) {
2211 d = e;
2212 } else {
2213 d = urldb_add_host_node(buf, d);
2214 }
2215
2216 /* And insert into search tree */
2217 if (d) {
2218 struct search_node **r;
2219
2220 r = urldb_get_search_tree_direct(buf);
2221 s = urldb_search_insert(*r, d);
2222 if (!s) {
2223 /* failed */
2224 d = NULL((void*)0);
2225 } else {
2226 *r = s;
2227 }
2228 }
2229 break;
2230 }
2231
2232 /* Check for existing entry */
2233 for (e = d->children; e; e = e->next)
2234 if (strcasecmp(part + 1, e->part) == 0)
2235 break;
2236
2237 d = e ? e : urldb_add_host_node(part + 1, d);
2238 if (!d)
2239 break;
2240
2241 *part = '\0';
2242 } while (1);
2243
2244 return d;
2245}
2246
2247
2248/**
2249 * Insert a cookie into the database
2250 *
2251 * \param c The cookie to insert
2252 * \param scheme URL scheme associated with cookie path
2253 * \param url URL (sans fragment) associated with cookie
2254 * \return true on success, false on memory exhaustion (c will be freed)
2255 */
2256static bool_Bool
2257urldb_insert_cookie(struct cookie_internal_data *c,
2258 lwc_string *scheme,
2259 nsurl *url)
2260{
2261 struct cookie_internal_data *d;
2262 const struct host_part *h;
2263 struct path_data *p;
2264 time_t now = time(NULL((void*)0));
2265
2266 assert(c)((c) ? (void) (0) : __assert_fail ("c", "content/urldb.c", 2266
, __extension__ __PRETTY_FUNCTION__))
;
2267
2268 if (c->domain[0] == '.') {
2269 h = urldb_search_find(
2270 urldb_get_search_tree(&(c->domain[1])),
2271 c->domain + 1);
2272 if (!h) {
2273 h = urldb_add_host(c->domain + 1);
2274 if (!h) {
2275 urldb_free_cookie(c);
2276 return false0;
2277 }
2278 }
2279
2280 p = (struct path_data *) &h->paths;
2281 } else {
2282 /* Need to have a URL and scheme, if it's not a domain cookie */
2283 assert(url != NULL)((url != ((void*)0)) ? (void) (0) : __assert_fail ("url != NULL"
, "content/urldb.c", 2283, __extension__ __PRETTY_FUNCTION__)
)
;
2284 assert(scheme != NULL)((scheme != ((void*)0)) ? (void) (0) : __assert_fail ("scheme != NULL"
, "content/urldb.c", 2284, __extension__ __PRETTY_FUNCTION__)
)
;
2285
2286 h = urldb_search_find(
2287 urldb_get_search_tree(c->domain),
2288 c->domain);
2289
2290 if (!h) {
2291 h = urldb_add_host(c->domain);
2292 if (!h) {
2293 urldb_free_cookie(c);
2294 return false0;
2295 }
2296 }
2297
2298 /* find path */
2299 p = urldb_add_path(scheme, 0, h,
2300 strdup(c->path), NULL((void*)0), url);
2301 if (!p) {
2302 urldb_free_cookie(c);
2303 return false0;
2304 }
2305 }
2306
2307 /* add cookie */
2308 for (d = p->cookies; d; d = d->next) {
2309 if (!strcmp(d->domain, c->domain) &&
2310 !strcmp(d->path, c->path) &&
2311 !strcmp(d->name, c->name))
2312 break;
2313 }
2314
2315 if (d) {
2316 if (c->expires != -1 && c->expires < now) {
2317 /* remove cookie */
2318 if (d->next)
2319 d->next->prev = d->prev;
2320 else
2321 p->cookies_end = d->prev;
2322 if (d->prev)
2323 d->prev->next = d->next;
2324 else
2325 p->cookies = d->next;
2326
2327 cookie_manager_remove((struct cookie_data *)d);
2328
2329 urldb_free_cookie(d);
2330 urldb_free_cookie(c);
2331 } else {
2332 /* replace d with c */
2333 c->prev = d->prev;
2334 c->next = d->next;
2335 if (c->next)
2336 c->next->prev = c;
2337 else
2338 p->cookies_end = c;
2339 if (c->prev)
2340 c->prev->next = c;
2341 else
2342 p->cookies = c;
2343
2344 cookie_manager_remove((struct cookie_data *)d);
2345 urldb_free_cookie(d);
2346
2347 cookie_manager_add((struct cookie_data *)c);
2348 }
2349 } else {
2350 c->prev = p->cookies_end;
2351 c->next = NULL((void*)0);
2352 if (p->cookies_end)
2353 p->cookies_end->next = c;
2354 else
2355 p->cookies = c;
2356 p->cookies_end = c;
2357
2358 cookie_manager_add((struct cookie_data *)c);
2359 }
2360
2361 return true1;
2362}
2363
2364
2365/**
2366 * Concatenate a cookie into the provided buffer
2367 *
2368 * \param c Cookie to concatenate
2369 * \param version The version of the cookie string to output
2370 * \param used Pointer to amount of buffer used (updated)
2371 * \param alloc Pointer to allocated size of buffer (updated)
2372 * \param buf Pointer to Pointer to buffer (updated)
2373 * \return true on success, false on memory exhaustion
2374 */
2375static bool_Bool
2376urldb_concat_cookie(struct cookie_internal_data *c,
2377 int version,
2378 int *used,
2379 int *alloc,
2380 char **buf)
2381{
2382 /* Combined (A)BNF for the Cookie: request header:
2383 *
2384 * CHAR = <any US-ASCII character (octets 0 - 127)>
2385 * CTL = <any US-ASCII control character
2386 * (octets 0 - 31) and DEL (127)>
2387 * CR = <US-ASCII CR, carriage return (13)>
2388 * LF = <US-ASCII LF, linefeed (10)>
2389 * SP = <US-ASCII SP, space (32)>
2390 * HT = <US-ASCII HT, horizontal-tab (9)>
2391 * <"> = <US-ASCII double-quote mark (34)>
2392 *
2393 * CRLF = CR LF
2394 *
2395 * LWS = [CRLF] 1*( SP | HT )
2396 *
2397 * TEXT = <any OCTET except CTLs,
2398 * but including LWS>
2399 *
2400 * token = 1*<any CHAR except CTLs or separators>
2401 * separators = "(" | ")" | "<" | ">" | "@"
2402 * | "," | ";" | ":" | "\" | <">
2403 * | "/" | "[" | "]" | "?" | "="
2404 * | "{" | "}" | SP | HT
2405 *
2406 * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
2407 * qdtext = <any TEXT except <">>
2408 * quoted-pair = "\" CHAR
2409 *
2410 * attr = token
2411 * value = word
2412 * word = token | quoted-string
2413 *
2414 * cookie = "Cookie:" cookie-version
2415 * 1*((";" | ",") cookie-value)
2416 * cookie-value = NAME "=" VALUE [";" path] [";" domain]
2417 * cookie-version = "$Version" "=" value
2418 * NAME = attr
2419 * VALUE = value
2420 * path = "$Path" "=" value
2421 * domain = "$Domain" "=" value
2422 *
2423 * A note on quoted-string handling:
2424 * The cookie data stored in the db is verbatim (i.e. sans enclosing
2425 * <">, if any, and with all quoted-pairs intact) thus all that we
2426 * need to do here is ensure that value strings which were quoted
2427 * in Set-Cookie or which include any of the separators are quoted
2428 * before use.
2429 *
2430 * A note on cookie-value separation:
2431 * We use semicolons for all separators, including between
2432 * cookie-values. This simplifies things and is backwards compatible.
2433 */
2434 const char * const separators = "()<>@,;:\\\"/[]?={} \t";
2435
2436 int max_len;
2437
2438 assert(c && used && alloc && buf && *buf)((c && used && alloc && buf &&
*buf) ? (void) (0) : __assert_fail ("c && used && alloc && buf && *buf"
, "content/urldb.c", 2438, __extension__ __PRETTY_FUNCTION__)
)
;
2439
2440 /* "; " cookie-value
2441 * We allow for the possibility that values are quoted
2442 */
2443 max_len = 2 + strlen(c->name) + 1 + strlen(c->value) + 2 +
2444 (c->path_from_set ?
2445 8 + strlen(c->path) + 2 : 0) +
2446 (c->domain_from_set ?
2447 10 + strlen(c->domain) + 2 : 0);
2448
2449 if (*used + max_len >= *alloc) {
2450 char *temp = realloc(*buf, *alloc + 4096);
2451 if (!temp) {
2452 return false0;
2453 }
2454 *buf = temp;
2455 *alloc += 4096;
2456 }
2457
2458 if (version == COOKIE_NETSCAPE) {
2459 /* Original Netscape cookie */
2460 sprintf(*buf + *used - 1, "; %s=", c->name);
2461 *used += 2 + strlen(c->name) + 1;
2462
2463 /* The Netscape spec doesn't mention quoting of cookie values.
2464 * RFC 2109 $10.1.3 indicates that values must not be quoted.
2465 *
2466 * However, other browsers preserve quoting, so we should, too
2467 */
2468 if (c->value_was_quoted) {
2469 sprintf(*buf + *used - 1, "\"%s\"", c->value);
2470 *used += 1 + strlen(c->value) + 1;
2471 } else {
2472 /** \todo should we %XX-encode [;HT,SP] ? */
2473 /** \todo Should we strip escaping backslashes? */
2474 sprintf(*buf + *used - 1, "%s", c->value);
2475 *used += strlen(c->value);
2476 }
2477
2478 /* We don't send path/domain information -- that's what the
2479 * Netscape spec suggests we should do, anyway. */
2480 } else {
2481 /* RFC2109 or RFC2965 cookie */
2482 sprintf(*buf + *used - 1, "; %s=", c->name);
2483 *used += 2 + strlen(c->name) + 1;
2484
2485 /* Value needs quoting if it contains any separator or if
2486 * it needs preserving from the Set-Cookie header */
2487 if (c->value_was_quoted ||
2488 strpbrk(c->value, separators) != NULL((void*)0)) {
2489 sprintf(*buf + *used - 1, "\"%s\"", c->value);
2490 *used += 1 + strlen(c->value) + 1;
2491 } else {
2492 sprintf(*buf + *used - 1, "%s", c->value);
2493 *used += strlen(c->value);
2494 }
2495
2496 if (c->path_from_set) {
2497 /* Path, quoted if necessary */
2498 sprintf(*buf + *used - 1, "; $Path=");
2499 *used += 8;
2500
2501 if (strpbrk(c->path, separators) != NULL((void*)0)) {
2502 sprintf(*buf + *used - 1, "\"%s\"", c->path);
2503 *used += 1 + strlen(c->path) + 1;
2504 } else {
2505 sprintf(*buf + *used - 1, "%s", c->path);
2506 *used += strlen(c->path);
2507 }
2508 }
2509
2510 if (c->domain_from_set) {
2511 /* Domain, quoted if necessary */
2512 sprintf(*buf + *used - 1, "; $Domain=");
2513 *used += 10;
2514
2515 if (strpbrk(c->domain, separators) != NULL((void*)0)) {
2516 sprintf(*buf + *used - 1, "\"%s\"", c->domain);
2517 *used += 1 + strlen(c->domain) + 1;
2518 } else {
2519 sprintf(*buf + *used - 1, "%s", c->domain);
2520 *used += strlen(c->domain);
2521 }
2522 }
2523 }
2524
2525 return true1;
2526}
2527
2528
2529/**
2530 * deletes paths from a cookie.
2531 *
2532 * \param domain the cookie domain
2533 * \param path the cookie path
2534 * \param name The cookie name
2535 * \param parent The url data of the cookie
2536 */
2537static void
2538urldb_delete_cookie_paths(const char *domain,
2539 const char *path,
2540 const char *name,
2541 struct path_data *parent)
2542{
2543 struct cookie_internal_data *c;
2544 struct path_data *p = parent;
2545
2546 assert(parent)((parent) ? (void) (0) : __assert_fail ("parent", "content/urldb.c"
, 2546, __extension__ __PRETTY_FUNCTION__))
;
2547
2548 do {
2549 for (c = p->cookies; c; c = c->next) {
2550 if (strcmp(c->domain, domain) == 0 &&
2551 strcmp(c->path, path) == 0 &&
2552 strcmp(c->name, name) == 0) {
2553 if (c->prev) {
2554 c->prev->next = c->next;
2555 } else {
2556 p->cookies = c->next;
2557 }
2558
2559 if (c->next) {
2560 c->next->prev = c->prev;
2561 } else {
2562 p->cookies_end = c->prev;
2563 }
2564
2565 urldb_free_cookie(c);
2566
2567 return;
2568 }
2569 }
2570
2571 if (p->children) {
2572 p = p->children;
2573 } else {
2574 while (p != parent) {
2575 if (p->next != NULL((void*)0)) {
2576 p = p->next;
2577 break;
2578 }
2579
2580 p = p->parent;
2581 }
2582 }
2583 } while (p != parent);
2584}
2585
2586
2587/**
2588 * Deletes cookie hosts and their assoicated paths
2589 *
2590 * \param domain the cookie domain
2591 * \param path the cookie path
2592 * \param name The cookie name
2593 * \param parent The url data of the cookie
2594 */
2595static void
2596urldb_delete_cookie_hosts(const char *domain,
2597 const char *path,
2598 const char *name,
2599 struct host_part *parent)
2600{
2601 struct host_part *h;
2602 assert(parent)((parent) ? (void) (0) : __assert_fail ("parent", "content/urldb.c"
, 2602, __extension__ __PRETTY_FUNCTION__))
;
2603
2604 urldb_delete_cookie_paths(domain, path, name, &parent->paths);
2605
2606 for (h = parent->children; h; h = h->next) {
2607 urldb_delete_cookie_hosts(domain, path, name, h);
2608 }
2609}
2610
2611
2612/**
2613 * Save a path subtree's cookies
2614 *
2615 * \param fp File pointer to write to
2616 * \param parent Parent path
2617 */
2618static void urldb_save_cookie_paths(FILE *fp, struct path_data *parent)
2619{
2620 struct path_data *p = parent;
2621 time_t now = time(NULL((void*)0));
2622
2623 assert(fp && parent)((fp && parent) ? (void) (0) : __assert_fail ("fp && parent"
, "content/urldb.c", 2623, __extension__ __PRETTY_FUNCTION__)
)
;
2624
2625 do {
2626 if (p->cookies != NULL((void*)0)) {
2627 struct cookie_internal_data *c;
2628
2629 for (c = p->cookies; c != NULL((void*)0); c = c->next) {
2630 if (c->expires == -1 || c->expires < now) {
2631 /* Skip expired & session cookies */
2632 continue;
2633 }
2634
2635 fprintf(fp,
2636 "%d\t%s\t%d\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t"
2637 "%s\t%s\t%d\t%s\t%s\t%s\n",
2638 c->version, c->domain,
2639 c->domain_from_set, c->path,
2640 c->path_from_set, c->secure,
2641 c->http_only,
2642 (int)c->expires, (int)c->last_used,
2643 c->no_destroy, c->name, c->value,
2644 c->value_was_quoted,
2645 p->scheme ? lwc_string_data(p->scheme)({((p->scheme != ((void*)0)) ? (void) (0) : __assert_fail (
"p->scheme != NULL", "content/urldb.c", 2645, __extension__
__PRETTY_FUNCTION__)); (const char *)((p->scheme)+1);})
:
2646 "unused",
2647 p->url ? nsurl_access(p->url) :
2648 "unused",
2649 c->comment ? c->comment : "");
2650 }
2651 }
2652
2653 if (p->children != NULL((void*)0)) {
2654 p = p->children;
2655 } else {
2656 while (p != parent) {
2657 if (p->next != NULL((void*)0)) {
2658 p = p->next;
2659 break;
2660 }
2661
2662 p = p->parent;
2663 }
2664 }
2665 } while (p != parent);
2666}
2667
2668
2669/**
2670 * Save a host subtree's cookies
2671 *
2672 * \param fp File pointer to write to
2673 * \param parent Parent host
2674 */
2675static void urldb_save_cookie_hosts(FILE *fp, struct host_part *parent)
2676{
2677 struct host_part *h;
2678 assert(fp && parent)((fp && parent) ? (void) (0) : __assert_fail ("fp && parent"
, "content/urldb.c", 2678, __extension__ __PRETTY_FUNCTION__)
)
;
2679
2680 urldb_save_cookie_paths(fp, &parent->paths);
2681
2682 for (h = parent->children; h; h = h->next)
2683 urldb_save_cookie_hosts(fp, h);
2684}
2685
2686
2687/**
2688 * Destroy a cookie node
2689 *
2690 * \param c Cookie to destroy
2691 */
2692static void urldb_destroy_cookie(struct cookie_internal_data *c)
2693{
2694 free(c->name);
2695 free(c->value);
2696 free(c->comment);
2697 free(c->domain);
2698 free(c->path);
2699
2700 free(c);
2701}
2702
2703
2704/**
2705 * Destroy the contents of a path node
2706 *
2707 * \param node Node to destroy contents of (does not destroy node)
2708 */
2709static void urldb_destroy_path_node_content(struct path_data *node)
2710{
2711 struct cookie_internal_data *a, *b;
2712 unsigned int i;
2713
2714 if (node->url != NULL((void*)0)) {
2715 nsurl_unref(node->url);
2716 }
2717
2718 if (node->scheme != NULL((void*)0)) {
2719 lwc_string_unref(node->scheme){ lwc_string *__lwc_s = (node->scheme); ((__lwc_s != ((void
*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 2719, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
2720 }
2721
2722 free(node->segment);
2723 for (i = 0; i < node->frag_cnt; i++)
2724 free(node->fragment[i]);
2725 free(node->fragment);
2726
2727 free(node->urld.title);
2728
2729 for (a = node->cookies; a; a = b) {
2730 b = a->next;
2731 urldb_destroy_cookie(a);
2732 }
2733}
2734
2735
2736/**
2737 * Destroy protection space data
2738 *
2739 * \param space Protection space to destroy
2740 */
2741static void urldb_destroy_prot_space(struct prot_space_data *space)
2742{
2743 lwc_string_unref(space->scheme){ lwc_string *__lwc_s = (space->scheme); ((__lwc_s != ((void
*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 2743, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
2744 free(space->realm);
2745 free(space->auth);
2746
2747 free(space);
2748}
2749
2750
2751/**
2752 * Destroy a path tree
2753 *
2754 * \param root Root node of tree to destroy
2755 */
2756static void urldb_destroy_path_tree(struct path_data *root)
2757{
2758 struct path_data *p = root;
2759
2760 do {
2761 if (p->children != NULL((void*)0)) {
2762 p = p->children;
2763 } else {
2764 struct path_data *q = p;
2765
2766 while (p != root) {
2767 if (p->next != NULL((void*)0)) {
2768 p = p->next;
2769 break;
2770 }
2771
2772 p = p->parent;
2773
2774 urldb_destroy_path_node_content(q);
2775 free(q);
2776
2777 q = p;
2778 }
2779
2780 urldb_destroy_path_node_content(q);
2781 free(q);
2782 }
2783 } while (p != root);
2784}
2785
2786
2787/**
2788 * Destroy a host tree
2789 *
2790 * \param root Root node of tree to destroy
2791 */
2792static void urldb_destroy_host_tree(struct host_part *root)
2793{
2794 struct host_part *a, *b;
2795 struct path_data *p, *q;
2796 struct prot_space_data *s, *t;
2797
2798 /* Destroy children */
2799 for (a = root->children; a; a = b) {
2800 b = a->next;
2801 urldb_destroy_host_tree(a);
2802 }
2803
2804 /* Now clean up paths */
2805 for (p = root->paths.children; p; p = q) {
2806 q = p->next;
2807 urldb_destroy_path_tree(p);
2808 }
2809
2810 /* Root path */
2811 urldb_destroy_path_node_content(&root->paths);
2812
2813 /* Proctection space data */
2814 for (s = root->prot_space; s; s = t) {
2815 t = s->next;
2816 urldb_destroy_prot_space(s);
2817 }
2818
2819 /* And ourselves */
2820 free(root->part);
2821 free(root);
2822}
2823
2824
2825/**
2826 * Destroy a search tree
2827 *
2828 * \param root Root node of tree to destroy
2829 */
2830static void urldb_destroy_search_tree(struct search_node *root)
2831{
2832 /* Destroy children */
2833 if (root->left != &empty)
2834 urldb_destroy_search_tree(root->left);
2835 if (root->right != &empty)
2836 urldb_destroy_search_tree(root->right);
2837
2838 /* And destroy ourselves */
2839 free(root);
2840}
2841
2842
2843/*************** External interface ***************/
2844
2845
2846/* exported interface documented in content/urldb.h */
2847void urldb_destroy(void)
2848{
2849 struct host_part *a, *b;
2850 int i;
2851
2852 /* Clean up search trees */
2853 for (i = 0; i < NUM_SEARCH_TREES28; i++) {
2854 if (search_trees[i] != &empty) {
2855 urldb_destroy_search_tree(search_trees[i]);
2856 search_trees[i] = &empty;
2857 }
2858 }
2859
2860 /* And database */
2861 for (a = db_root.children; a; a = b) {
2862 b = a->next;
2863 urldb_destroy_host_tree(a);
2864 }
2865 memset(&db_root, 0, sizeof(db_root));
2866
2867 /* And the bloom filter */
2868 if (url_bloom != NULL((void*)0)) {
2869 bloom_destroy(url_bloom);
2870 url_bloom = NULL((void*)0);
2871 }
2872}
2873
2874
2875/* exported interface documented in netsurf/url_db.h */
2876nserror urldb_load(const char *filename)
1
[debug] analyzing from urldb_load
2877{
2878#define MAXIMUM_URL_LENGTH 4096
2879 char s[MAXIMUM_URL_LENGTH];
2880 char host[256];
2881 struct host_part *h;
2882 int urls;
2883 int i;
2884 int version;
2885 int length;
2886 FILE *fp;
2887
2888 assert(filename)((filename) ? (void) (0) : __assert_fail ("filename", "content/urldb.c"
, 2888, __extension__ __PRETTY_FUNCTION__))
;
2
Assuming 'filename' is non-null
3
'?' condition is true
2889
2890 NSLOG(netsurf, INFO, "Loading URL file %s", filename)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2890
, }; nslog__log(&_nslog_ctx, "Loading URL file %s", filename
); } } while(0)
;
4
Taking true branch
5
Loop condition is false. Exiting loop
2891
2892 if (url_bloom == NULL((void*)0))
6
Assuming 'url_bloom' is not equal to NULL
7
Taking false branch
2893 url_bloom = bloom_create(BLOOM_SIZE(1024 * 32));
2894
2895 fp = fopen(filename, "r");
2896 if (!fp
7.1
'fp' is non-null
) {
8
Taking false branch
2897 NSLOG(netsurf, INFO, "Failed to open file '%s' for reading",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2898
, }; nslog__log(&_nslog_ctx, "Failed to open file '%s' for reading"
, filename); } } while(0)
2898 filename)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2898
, }; nslog__log(&_nslog_ctx, "Failed to open file '%s' for reading"
, filename); } } while(0)
;
2899 return NSERROR_NOT_FOUND;
2900 }
2901
2902 if (!fgets(s, MAXIMUM_URL_LENGTH, fp)) {
9
Taking false branch
2903 fclose(fp);
2904 return NSERROR_NEED_DATA;
2905 }
2906
2907 version = atoi(s);
2908 if (version < MIN_URL_FILE_VERSION106) {
10
Assuming 'version' is >= MIN_URL_FILE_VERSION
11
Taking false branch
2909 NSLOG(netsurf, INFO, "Unsupported URL file version.")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2909
, }; nslog__log(&_nslog_ctx, "Unsupported URL file version."
); } } while(0)
;
2910 fclose(fp);
2911 return NSERROR_INVALID;
2912 }
2913 if (version > URL_FILE_VERSION107) {
12
Assuming 'version' is <= URL_FILE_VERSION
13
Taking false branch
2914 NSLOG(netsurf, INFO, "Unknown URL file version.")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2914
, }; nslog__log(&_nslog_ctx, "Unknown URL file version.")
; } } while(0)
;
2915 fclose(fp);
2916 return NSERROR_INVALID;
2917 }
2918
2919 while (fgets(host, sizeof host, fp)) {
14
Loop condition is true. Entering loop body
23
Execution continues on line 2919
24
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior
2920 time_t hsts_expiry = 0;
2921 int hsts_include_sub_domains = 0;
2922
2923 /* get the hostname */
2924 length = strlen(host) - 1;
2925 host[length] = '\0';
2926
2927 /* skip data that has ended up with a host of '' */
2928 if (length == 0) {
15
Assuming 'length' is equal to 0
16
Taking true branch
2929 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
17
Taking false branch
2930 break;
2931 urls = atoi(s);
2932 /* Eight fields/url */
2933 for (i = 0; i < (8 * urls); i++) {
18
Assuming the condition is true
19
Loop condition is true. Entering loop body
2934 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
20
Assuming this stream operation fails
21
Taking true branch
2935 break;
22
Execution continues on line 2937
2936 }
2937 continue;
2938 }
2939
2940 if (version >= 107) {
2941 char *p = host;
2942 while (*p && *p != ' ') p++;
2943 while (*p && *p == ' ') { *p = '\0'; p++; }
2944 hsts_include_sub_domains = (*p == '1');
2945 while (*p && *p != ' ') p++;
2946 while (*p && *p == ' ') p++;
2947 nsc_snptimet(p, strlen(p), &hsts_expiry);
2948 }
2949
2950 h = urldb_add_host(host);
2951 if (!h) {
2952 NSLOG(netsurf, INFO, "Failed adding host: '%s'", host)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2952
, }; nslog__log(&_nslog_ctx, "Failed adding host: '%s'", host
); } } while(0)
;
2953 fclose(fp);
2954 return NSERROR_NOMEM;
2955 }
2956 h->hsts.expires = hsts_expiry;
2957 h->hsts.include_sub_domains = hsts_include_sub_domains;
2958
2959 /* read number of URLs */
2960 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
2961 break;
2962 urls = atoi(s);
2963
2964 /* no URLs => try next host */
2965 if (urls == 0) {
2966 NSLOG(netsurf, INFO, "No URLs for '%s'", host)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 2966
, }; nslog__log(&_nslog_ctx, "No URLs for '%s'", host); }
} while(0)
;
2967 continue;
2968 }
2969
2970 /* load the non-corrupt data */
2971 for (i = 0; i < urls; i++) {
2972 struct path_data *p = NULL((void*)0);
2973 char scheme[64], ports[10];
2974 char url[64 + 3 + 256 + 6 + 4096 + 1 + 1];
2975 unsigned int port;
2976 bool_Bool is_file = false0;
2977 nsurl *nsurl;
2978 lwc_string *scheme_lwc, *fragment_lwc;
2979 char *path_query;
2980 size_t len;
2981
2982 if (!fgets(scheme, sizeof scheme, fp))
2983 break;
2984 length = strlen(scheme) - 1;
2985 scheme[length] = '\0';
2986
2987 if (!fgets(ports, sizeof ports, fp))
2988 break;
2989 length = strlen(ports) - 1;
2990 ports[length] = '\0';
2991 port = atoi(ports);
2992
2993 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
2994 break;
2995 length = strlen(s) - 1;
2996 s[length] = '\0';
2997
2998 if (!strcasecmp(host, "localhost") &&
2999 !strcasecmp(scheme, "file"))
3000 is_file = true1;
3001
3002 snprintf(url, sizeof url, "%s://%s%s%s%s",
3003 scheme,
3004 /* file URLs have no host */
3005 (is_file ? "" : host),
3006 (port ? ":" : ""),
3007 (port ? ports : ""),
3008 s);
3009
3010 /* TODO: store URLs in pre-parsed state, and make
3011 * a nsurl_load to generate the nsurl more
3012 * swiftly.
3013 * Need a nsurl_save too.
3014 */
3015 if (nsurl_create(url, &nsurl) != NSERROR_OK) {
3016 NSLOG(netsurf, INFO, "Failed inserting '%s'",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3017
, }; nslog__log(&_nslog_ctx, "Failed inserting '%s'", url
); } } while(0)
3017 url)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3017
, }; nslog__log(&_nslog_ctx, "Failed inserting '%s'", url
); } } while(0)
;
3018 fclose(fp);
3019 return NSERROR_NOMEM;
3020 }
3021
3022 if (url_bloom != NULL((void*)0)) {
3023 uint32_t hash = nsurl_hash(nsurl);
3024 bloom_insert_hash(url_bloom, hash);
3025 }
3026
3027 /* Copy and merge path/query strings */
3028 if (nsurl_get(nsurl, NSURL_PATH | NSURL_QUERY,
3029 &path_query, &len) != NSERROR_OK) {
3030 NSLOG(netsurf, INFO, "Failed inserting '%s'",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3031
, }; nslog__log(&_nslog_ctx, "Failed inserting '%s'", url
); } } while(0)
3031 url)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3031
, }; nslog__log(&_nslog_ctx, "Failed inserting '%s'", url
); } } while(0)
;
3032 fclose(fp);
3033 return NSERROR_NOMEM;
3034 }
3035
3036 scheme_lwc = nsurl_get_component(nsurl, NSURL_SCHEME);
3037 fragment_lwc = nsurl_get_component(nsurl,
3038 NSURL_FRAGMENT);
3039 p = urldb_add_path(scheme_lwc, port, h, path_query,
3040 fragment_lwc, nsurl);
3041 if (!p) {
3042 NSLOG(netsurf, INFO, "Failed inserting '%s'",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3043
, }; nslog__log(&_nslog_ctx, "Failed inserting '%s'", url
); } } while(0)
3043 url)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3043
, }; nslog__log(&_nslog_ctx, "Failed inserting '%s'", url
); } } while(0)
;
3044 fclose(fp);
3045 return NSERROR_NOMEM;
3046 }
3047 nsurl_unref(nsurl);
3048 lwc_string_unref(scheme_lwc){ lwc_string *__lwc_s = (scheme_lwc); ((__lwc_s != ((void*)0)
) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3048, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3049 if (fragment_lwc != NULL((void*)0))
3050 lwc_string_unref(fragment_lwc){ lwc_string *__lwc_s = (fragment_lwc); ((__lwc_s != ((void*)
0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3050, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3051
3052 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
3053 break;
3054 if (p)
3055 p->urld.visits = (unsigned int)atoi(s);
3056
3057 /* entry last use time */
3058 if (!fgets(s, MAXIMUM_URL_LENGTH, fp)) {
3059 break;
3060 }
3061 if (p) {
3062 nsc_snptimet(s, strlen(s) - 1, &p->urld.last_visit);
3063 }
3064
3065 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
3066 break;
3067 if (p)
3068 p->urld.type = (content_type)atoi(s);
3069
3070 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
3071 break;
3072
3073
3074 if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
3075 break;
3076 length = strlen(s) - 1;
3077 if (p && length > 0) {
3078 s[length] = '\0';
3079 p->urld.title = malloc(length + 1);
3080 if (p->urld.title)
3081 memcpy(p->urld.title, s, length + 1);
3082 }
3083 }
3084 }
3085
3086 fclose(fp);
3087 NSLOG(netsurf, INFO, "Successfully loaded URL file")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3087
, }; nslog__log(&_nslog_ctx, "Successfully loaded URL file"
); } } while(0)
;
3088#undef MAXIMUM_URL_LENGTH
3089
3090 return NSERROR_OK;
3091}
3092
3093/* exported interface documented in netsurf/url_db.h */
3094nserror urldb_save(const char *filename)
3095{
3096 FILE *fp;
3097 int i;
3098
3099 assert(filename)((filename) ? (void) (0) : __assert_fail ("filename", "content/urldb.c"
, 3099, __extension__ __PRETTY_FUNCTION__))
;
3100
3101 fp = fopen(filename, "w");
3102 if (!fp) {
3103 NSLOG(netsurf, INFO, "Failed to open file '%s' for writing",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3104
, }; nslog__log(&_nslog_ctx, "Failed to open file '%s' for writing"
, filename); } } while(0)
3104 filename)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3104
, }; nslog__log(&_nslog_ctx, "Failed to open file '%s' for writing"
, filename); } } while(0)
;
3105 return NSERROR_SAVE_FAILED;
3106 }
3107
3108 /* file format version number */
3109 fprintf(fp, "%d\n", URL_FILE_VERSION107);
3110
3111 for (i = 0; i != NUM_SEARCH_TREES28; i++) {
3112 urldb_save_search_tree(search_trees[i], fp);
3113 }
3114
3115 fclose(fp);
3116
3117 return NSERROR_OK;
3118}
3119
3120
3121/* exported interface documented in content/urldb.h */
3122nserror urldb_set_url_persistence(nsurl *url, bool_Bool persist)
3123{
3124 struct path_data *p;
3125
3126 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3126, __extension__ __PRETTY_FUNCTION__))
;
3127
3128 p = urldb_find_url(url);
3129 if (!p) {
3130 return NSERROR_NOT_FOUND;
3131 }
3132
3133 p->persistent = persist;
3134
3135 return NSERROR_OK;
3136}
3137
3138
3139/* exported interface documented in content/urldb.h */
3140bool_Bool urldb_add_url(nsurl *url)
3141{
3142 struct host_part *h;
3143 struct path_data *p;
3144 lwc_string *scheme;
3145 lwc_string *port;
3146 lwc_string *host;
3147 lwc_string *fragment;
3148 const char *host_str;
3149 char *path_query = NULL((void*)0);
3150 size_t len;
3151 bool_Bool match;
3152 unsigned int port_int;
3153
3154 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3154, __extension__ __PRETTY_FUNCTION__))
;
3155
3156 if (url_bloom == NULL((void*)0))
3157 url_bloom = bloom_create(BLOOM_SIZE(1024 * 32));
3158
3159 if (url_bloom != NULL((void*)0)) {
3160 uint32_t hash = nsurl_hash(url);
3161 bloom_insert_hash(url_bloom, hash);
3162 }
3163
3164 /* Copy and merge path/query strings */
3165 if (nsurl_get(url, NSURL_PATH | NSURL_QUERY, &path_query, &len) !=
3166 NSERROR_OK) {
3167 return false0;
3168 }
3169 assert(path_query != NULL)((path_query != ((void*)0)) ? (void) (0) : __assert_fail ("path_query != NULL"
, "content/urldb.c", 3169, __extension__ __PRETTY_FUNCTION__)
)
;
3170
3171 scheme = nsurl_get_component(url, NSURL_SCHEME);
3172 if (scheme == NULL((void*)0)) {
3173 free(path_query);
3174 return false0;
3175 }
3176
3177 host = nsurl_get_component(url, NSURL_HOST);
3178 if (host != NULL((void*)0)) {
3179 host_str = lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3179, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
;
3180 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3180
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3181
3182 } else if (lwc_string_isequal(scheme, corestring_lwc_file, &match)((*(&match) = ((scheme) == (corestring_lwc_file))), lwc_error_ok
)
==
3183 lwc_error_ok && match == true1) {
3184 host_str = "localhost";
3185
3186 } else {
3187 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3187, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3188 free(path_query);
3189 return false0;
3190 }
3191
3192 fragment = nsurl_get_component(url, NSURL_FRAGMENT);
3193
3194 port = nsurl_get_component(url, NSURL_PORT);
3195 if (port != NULL((void*)0)) {
3196 port_int = atoi(lwc_string_data(port)({((port != ((void*)0)) ? (void) (0) : __assert_fail ("port != NULL"
, "content/urldb.c", 3196, __extension__ __PRETTY_FUNCTION__)
); (const char *)((port)+1);})
);
3197 lwc_string_unref(port){ lwc_string *__lwc_s = (port); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3197
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3198 } else {
3199 port_int = 0;
3200 }
3201
3202 /* Get host entry */
3203 h = urldb_add_host(host_str);
3204
3205 /* Get path entry */
3206 if (h != NULL((void*)0)) {
3207 p = urldb_add_path(scheme,
3208 port_int,
3209 h,
3210 path_query,
3211 fragment,
3212 url);
3213 } else {
3214 p = NULL((void*)0);
3215 }
3216
3217 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3217, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3218 if (fragment != NULL((void*)0))
3219 lwc_string_unref(fragment){ lwc_string *__lwc_s = (fragment); ((__lwc_s != ((void*)0)) ?
(void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3219, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3220
3221 return (p != NULL((void*)0));
3222}
3223
3224
3225/* exported interface documented in content/urldb.h */
3226nserror urldb_set_url_title(nsurl *url, const char *title)
3227{
3228 struct path_data *p;
3229 char *temp;
3230
3231 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3231, __extension__ __PRETTY_FUNCTION__))
;
3232
3233 p = urldb_find_url(url);
3234 if (p == NULL((void*)0)) {
3235 return NSERROR_NOT_FOUND;
3236 }
3237
3238 /* copy the parameter if necessary */
3239 if (title != NULL((void*)0)) {
3240 temp = strdup(title);
3241 if (temp == NULL((void*)0)) {
3242 return NSERROR_NOMEM;
3243 }
3244 } else {
3245 temp = NULL((void*)0);
3246 }
3247
3248 free(p->urld.title);
3249 p->urld.title = temp;
3250
3251 return NSERROR_OK;
3252}
3253
3254
3255/* exported interface documented in content/urldb.h */
3256nserror urldb_set_url_content_type(nsurl *url, content_type type)
3257{
3258 struct path_data *p;
3259
3260 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3260, __extension__ __PRETTY_FUNCTION__))
;
3261
3262 p = urldb_find_url(url);
3263 if (!p) {
3264 return NSERROR_NOT_FOUND;
3265 }
3266
3267 p->urld.type = type;
3268
3269 return NSERROR_OK;
3270}
3271
3272
3273/* exported interface documented in content/urldb.h */
3274nserror urldb_update_url_visit_data(nsurl *url)
3275{
3276 struct path_data *p;
3277
3278 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3278, __extension__ __PRETTY_FUNCTION__))
;
3279
3280 p = urldb_find_url(url);
3281 if (!p) {
3282 return NSERROR_NOT_FOUND;
3283 }
3284
3285 p->urld.last_visit = time(NULL((void*)0));
3286 p->urld.visits++;
3287
3288 return NSERROR_OK;
3289}
3290
3291
3292/* exported interface documented in content/urldb.h */
3293void urldb_reset_url_visit_data(nsurl *url)
3294{
3295 struct path_data *p;
3296
3297 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3297, __extension__ __PRETTY_FUNCTION__))
;
3298
3299 p = urldb_find_url(url);
3300 if (!p)
3301 return;
3302
3303 p->urld.last_visit = (time_t)0;
3304 p->urld.visits = 0;
3305}
3306
3307
3308/* exported interface documented in netsurf/url_db.h */
3309const struct url_data *urldb_get_url_data(nsurl *url)
3310{
3311 struct path_data *p;
3312 struct url_internal_data *u;
3313
3314 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3314, __extension__ __PRETTY_FUNCTION__))
;
3315
3316 p = urldb_find_url(url);
3317 if (!p)
3318 return NULL((void*)0);
3319
3320 u = &p->urld;
3321
3322 return (const struct url_data *) u;
3323}
3324
3325
3326/* exported interface documented in content/urldb.h */
3327nsurl *urldb_get_url(nsurl *url)
3328{
3329 struct path_data *p;
3330
3331 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3331, __extension__ __PRETTY_FUNCTION__))
;
3332
3333 p = urldb_find_url(url);
3334 if (!p)
3335 return NULL((void*)0);
3336
3337 return p->url;
3338}
3339
3340
3341/* exported interface documented in netsurf/url_db.h */
3342void urldb_set_auth_details(nsurl *url, const char *realm, const char *auth)
3343{
3344 struct path_data *p, *pi;
3345 struct host_part *h;
3346 struct prot_space_data *space, *space_alloc;
3347 char *realm_alloc, *auth_alloc;
3348 bool_Bool match;
3349
3350 assert(url && realm && auth)((url && realm && auth) ? (void) (0) : __assert_fail
("url && realm && auth", "content/urldb.c", 3350
, __extension__ __PRETTY_FUNCTION__))
;
3351
3352 /* add url, in case it's missing */
3353 urldb_add_url(url);
3354
3355 p = urldb_find_url(url);
3356
3357 if (!p)
3358 return;
3359
3360 /* Search for host_part */
3361 for (pi = p; pi->parent != NULL((void*)0); pi = pi->parent)
3362 ;
3363 h = (struct host_part *)pi;
3364
3365 /* Search if given URL belongs to a protection space we already know of. */
3366 for (space = h->prot_space; space; space = space->next) {
3367 if (!strcmp(space->realm, realm) &&
3368 lwc_string_isequal(space->scheme, p->scheme,((*(&match) = ((space->scheme) == (p->scheme))), lwc_error_ok
)
3369 &match)((*(&match) = ((space->scheme) == (p->scheme))), lwc_error_ok
)
== lwc_error_ok &&
3370 match == true1 &&
3371 space->port == p->port)
3372 break;
3373 }
3374
3375 if (space != NULL((void*)0)) {
3376 /* Overrule existing auth. */
3377 free(space->auth);
3378 space->auth = strdup(auth);
3379 } else {
3380 /* Create a new protection space. */
3381 space = space_alloc = malloc(sizeof(struct prot_space_data));
3382 realm_alloc = strdup(realm);
3383 auth_alloc = strdup(auth);
3384
3385 if (!space_alloc || !realm_alloc || !auth_alloc) {
3386 free(space_alloc);
3387 free(realm_alloc);
3388 free(auth_alloc);
3389 return;
3390 }
3391
3392 space->scheme = lwc_string_ref(p->scheme)({lwc_string *__lwc_s = (p->scheme); ((__lwc_s != ((void*)
0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3392, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
++; __lwc_s;})
;
3393 space->port = p->port;
3394 space->realm = realm_alloc;
3395 space->auth = auth_alloc;
3396 space->next = h->prot_space;
3397 h->prot_space = space;
3398 }
3399
3400 p->prot_space = space;
3401}
3402
3403
3404/* exported interface documented in netsurf/url_db.h */
3405const char *urldb_get_auth_details(nsurl *url, const char *realm)
3406{
3407 struct path_data *p, *p_cur, *p_top;
3408
3409 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3409, __extension__ __PRETTY_FUNCTION__))
;
3410
3411 /* add to the db, so our lookup will work */
3412 urldb_add_url(url);
3413
3414 p = urldb_find_url(url);
3415 if (!p)
3416 return NULL((void*)0);
3417
3418 /* Check for any auth details attached to the path_data node or any of
3419 * its parents.
3420 */
3421 for (p_cur = p; p_cur != NULL((void*)0); p_top = p_cur, p_cur = p_cur->parent) {
3422 if (p_cur->prot_space) {
3423 return p_cur->prot_space->auth;
3424 }
3425 }
3426
3427 /* Only when we have a realm (and canonical root of given URL), we can
3428 * uniquely locate the protection space.
3429 */
3430 if (realm != NULL((void*)0)) {
3431 const struct host_part *h = (const struct host_part *)p_top;
3432 const struct prot_space_data *space;
3433 bool_Bool match;
3434
3435 /* Search for a possible matching protection space. */
3436 for (space = h->prot_space; space != NULL((void*)0);
3437 space = space->next) {
3438 if (!strcmp(space->realm, realm) &&
3439 lwc_string_isequal(space->scheme,((*(&match) = ((space->scheme) == (p->scheme))), lwc_error_ok
)
3440 p->scheme, &match)((*(&match) = ((space->scheme) == (p->scheme))), lwc_error_ok
)
==
3441 lwc_error_ok &&
3442 match == true1 &&
3443 space->port == p->port) {
3444 p->prot_space = space;
3445 return p->prot_space->auth;
3446 }
3447 }
3448 }
3449
3450 return NULL((void*)0);
3451}
3452
3453
3454/* exported interface documented in netsurf/url_db.h */
3455void urldb_set_cert_permissions(nsurl *url, bool_Bool permit)
3456{
3457 struct path_data *p;
3458 struct host_part *h;
3459
3460 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3460, __extension__ __PRETTY_FUNCTION__))
;
3461
3462 /* add url, in case it's missing */
3463 urldb_add_url(url);
3464
3465 p = urldb_find_url(url);
3466 if (!p)
3467 return;
3468
3469 for (; p && p->parent; p = p->parent)
3470 /* do nothing */;
3471 assert(p)((p) ? (void) (0) : __assert_fail ("p", "content/urldb.c", 3471
, __extension__ __PRETTY_FUNCTION__))
;
3472
3473 h = (struct host_part *)p;
3474
3475 h->permit_invalid_certs = permit;
3476}
3477
3478
3479/* exported interface documented in content/urldb.h */
3480bool_Bool urldb_get_cert_permissions(nsurl *url)
3481{
3482 struct path_data *p;
3483 const struct host_part *h;
3484
3485 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3485, __extension__ __PRETTY_FUNCTION__))
;
3486
3487 p = urldb_find_url(url);
3488 if (!p)
3489 return false0;
3490
3491 for (; p && p->parent; p = p->parent)
3492 /* do nothing */;
3493 assert(p)((p) ? (void) (0) : __assert_fail ("p", "content/urldb.c", 3493
, __extension__ __PRETTY_FUNCTION__))
;
3494
3495 h = (const struct host_part *)p;
3496
3497 return h->permit_invalid_certs;
3498}
3499
3500
3501/* exported interface documented in content/urldb.h */
3502bool_Bool urldb_set_hsts_policy(struct nsurl *url, const char *header)
3503{
3504 struct path_data *p;
3505 struct host_part *h;
3506 lwc_string *host;
3507 time_t now = time(NULL((void*)0));
3508 http_strict_transport_security *sts;
3509 uint32_t max_age = 0;
3510 nserror error;
3511
3512 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3512, __extension__ __PRETTY_FUNCTION__))
;
3513
3514 host = nsurl_get_component(url, NSURL_HOST);
3515 if (host != NULL((void*)0)) {
3516 if (urldb__host_is_ip_address(lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3516, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
)) {
3517 /* Host is IP: ignore */
3518 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3518
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3519 return true1;
3520 } else if (lwc_string_length(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3520, __extension__ __PRETTY_FUNCTION__)
); (host)->len;})
== 0) {
3521 /* Host is blank: ignore */
3522 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3522
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3523 return true1;
3524 }
3525
3526 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3526
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3527 } else {
3528 /* No host part: ignore */
3529 return true1;
3530 }
3531
3532 /* add url, in case it's missing */
3533 urldb_add_url(url);
3534
3535 p = urldb_find_url(url);
3536 if (!p)
3537 return false0;
3538
3539 for (; p && p->parent; p = p->parent)
3540 /* do nothing */;
3541 assert(p)((p) ? (void) (0) : __assert_fail ("p", "content/urldb.c", 3541
, __extension__ __PRETTY_FUNCTION__))
;
3542
3543 h = (struct host_part *)p;
3544 if (h->permit_invalid_certs) {
3545 /* Transport is tainted: ignore */
3546 return true1;
3547 }
3548
3549 error = http_parse_strict_transport_security(header, &sts);
3550 if (error != NSERROR_OK) {
3551 /* Parse failed: ignore */
3552 return true1;
3553 }
3554
3555 h->hsts.include_sub_domains =
3556 http_strict_transport_security_include_subdomains(sts);
3557
3558 max_age = http_strict_transport_security_max_age(sts);
3559 if (max_age == 0) {
3560 h->hsts.expires = 0;
3561 h->hsts.include_sub_domains = false0;
3562 } else if ((time_t) (now + max_age) > h->hsts.expires) {
3563 h->hsts.expires = now + max_age;
3564 }
3565
3566 http_strict_transport_security_destroy(sts);
3567
3568 return true1;
3569}
3570
3571
3572/* exported interface documented in content/urldb.h */
3573bool_Bool urldb_get_hsts_enabled(struct nsurl *url)
3574{
3575 struct path_data *p;
3576 const struct host_part *h;
3577 lwc_string *host;
3578 time_t now = time(NULL((void*)0));
3579
3580 assert(url)((url) ? (void) (0) : __assert_fail ("url", "content/urldb.c"
, 3580, __extension__ __PRETTY_FUNCTION__))
;
3581
3582 host = nsurl_get_component(url, NSURL_HOST);
3583 if (host != NULL((void*)0)) {
3584 if (urldb__host_is_ip_address(lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3584, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
)) {
3585 /* Host is IP: not enabled */
3586 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3586
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3587 return false0;
3588 } else if (lwc_string_length(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3588, __extension__ __PRETTY_FUNCTION__)
); (host)->len;})
== 0) {
3589 /* Host is blank: not enabled */
3590 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3590
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3591 return false0;
3592 }
3593
3594 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3594
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3595 } else {
3596 /* No host part: not enabled */
3597 return false0;
3598 }
3599
3600 /* The URL must exist in the db in order to find HSTS policy, since
3601 * we search up the tree from the URL node, and policy from further
3602 * up may also apply. */
3603 urldb_add_url(url);
3604
3605 p = urldb_find_url(url);
3606 if (!p)
3607 return false0;
3608
3609 for (; p && p->parent; p = p->parent)
3610 /* do nothing */;
3611 assert(p)((p) ? (void) (0) : __assert_fail ("p", "content/urldb.c", 3611
, __extension__ __PRETTY_FUNCTION__))
;
3612
3613 h = (const struct host_part *)p;
3614
3615 /* Consult record for this host */
3616 if (h->hsts.expires > now) {
3617 /* Not expired */
3618 return true1;
3619 }
3620
3621 /* Consult parent domains */
3622 for (h = h->parent; h && h != &db_root; h = h->parent) {
3623 if (h->hsts.expires > now && h->hsts.include_sub_domains) {
3624 /* Not expired and subdomains included */
3625 return true1;
3626 }
3627 }
3628
3629 return false0;
3630}
3631
3632
3633/* exported interface documented in netsurf/url_db.h */
3634void
3635urldb_iterate_partial(const char *prefix,
3636 bool_Bool (*callback)(nsurl *url, const struct url_data *data))
3637{
3638 char host[256];
3639 char buf[260]; /* max domain + "www." */
3640 const char *slash, *scheme_sep;
3641 struct search_node *tree;
3642 const struct host_part *h;
3643
3644 assert(prefix && callback)((prefix && callback) ? (void) (0) : __assert_fail ("prefix && callback"
, "content/urldb.c", 3644, __extension__ __PRETTY_FUNCTION__)
)
;
3645
3646 /* strip scheme */
3647 scheme_sep = strstr(prefix, "://");
3648 if (scheme_sep)
3649 prefix = scheme_sep + 3;
3650
3651 slash = strchr(prefix, '/');
3652 tree = urldb_get_search_tree(prefix);
3653
3654 if (slash) {
3655 /* if there's a slash in the input, then we can
3656 * assume that we're looking for a path */
3657 snprintf(host, sizeof host, "%.*s",
3658 (int) (slash - prefix), prefix);
3659
3660 h = urldb_search_find(tree, host);
3661 if (!h) {
3662 int len = slash - prefix;
3663
3664 if (len <= 3 || strncasecmp(host, "www.", 4) != 0) {
3665 snprintf(buf, sizeof buf, "www.%s", host);
3666 h = urldb_search_find(
3667 search_trees[ST_DN2 + 'w' - 'a'],
3668 buf);
3669 if (!h)
3670 return;
3671 } else
3672 return;
3673 }
3674
3675 if (h->paths.children) {
3676 /* Have paths, iterate them */
3677 urldb_iterate_partial_path(&h->paths, slash + 1,
3678 callback);
3679 }
3680
3681 } else {
3682 int len = strlen(prefix);
3683
3684 /* looking for hosts */
3685 if (!urldb_iterate_partial_host(tree, prefix, callback))
3686 return;
3687
3688 if (len <= 3 || strncasecmp(prefix, "www.", 4) != 0) {
3689 /* now look for www.prefix */
3690 snprintf(buf, sizeof buf, "www.%s", prefix);
3691 if(!urldb_iterate_partial_host(
3692 search_trees[ST_DN2 + 'w' - 'a'],
3693 buf, callback))
3694 return;
3695 }
3696 }
3697}
3698
3699
3700/* exported interface documented in netsurf/url_db.h */
3701void
3702urldb_iterate_entries(bool_Bool (*callback)(nsurl *url, const struct url_data *data))
3703{
3704 int i;
3705
3706 assert(callback)((callback) ? (void) (0) : __assert_fail ("callback", "content/urldb.c"
, 3706, __extension__ __PRETTY_FUNCTION__))
;
3707
3708 for (i = 0; i < NUM_SEARCH_TREES28; i++) {
3709 if (!urldb_iterate_entries_host(search_trees[i],
3710 callback,
3711 NULL((void*)0))) {
3712 break;
3713 }
3714 }
3715}
3716
3717
3718/* exported interface documented in content/urldb.h */
3719void urldb_iterate_cookies(bool_Bool (*callback)(const struct cookie_data *data))
3720{
3721 int i;
3722
3723 assert(callback)((callback) ? (void) (0) : __assert_fail ("callback", "content/urldb.c"
, 3723, __extension__ __PRETTY_FUNCTION__))
;
3724
3725 for (i = 0; i < NUM_SEARCH_TREES28; i++) {
3726 if (!urldb_iterate_entries_host(search_trees[i],
3727 NULL((void*)0), callback))
3728 break;
3729 }
3730}
3731
3732
3733/* exported interface documented in content/urldb.h */
3734bool_Bool urldb_set_cookie(const char *header, nsurl *url, nsurl *referer)
3735{
3736 const char *cur = header, *end;
3737 lwc_string *path, *host, *scheme;
3738 nsurl *urlt;
3739 bool_Bool match;
3740
3741 assert(url && header)((url && header) ? (void) (0) : __assert_fail ("url && header"
, "content/urldb.c", 3741, __extension__ __PRETTY_FUNCTION__)
)
;
3742
3743 /* Get defragmented URL, as 'urlt' */
3744 if (nsurl_defragment(url, &urlt) != NSERROR_OK)
3745 return NULL((void*)0);
3746
3747 scheme = nsurl_get_component(url, NSURL_SCHEME);
3748 if (scheme == NULL((void*)0)) {
3749 nsurl_unref(urlt);
3750 return false0;
3751 }
3752
3753 path = nsurl_get_component(url, NSURL_PATH);
3754 if (path == NULL((void*)0)) {
3755 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3755, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3756 nsurl_unref(urlt);
3757 return false0;
3758 }
3759
3760 host = nsurl_get_component(url, NSURL_HOST);
3761 if (host == NULL((void*)0)) {
3762 lwc_string_unref(path){ lwc_string *__lwc_s = (path); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3762
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3763 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3763, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3764 nsurl_unref(urlt);
3765 return false0;
3766 }
3767
3768 if (referer) {
3769 lwc_string *rhost;
3770
3771 /* Ensure that url's host name domain matches
3772 * referer's (4.3.5) */
3773 rhost = nsurl_get_component(referer, NSURL_HOST);
3774 if (rhost == NULL((void*)0)) {
3775 goto error;
3776 }
3777
3778 /* Domain match host names */
3779 if (lwc_string_isequal(host, rhost, &match)((*(&match) = ((host) == (rhost))), lwc_error_ok) == lwc_error_ok &&
3780 match == false0) {
3781 const char *hptr;
3782 const char *rptr;
3783 const char *dot;
3784 const char *host_data = lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3784, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
;
3785 const char *rhost_data = lwc_string_data(rhost)({((rhost != ((void*)0)) ? (void) (0) : __assert_fail ("rhost != NULL"
, "content/urldb.c", 3785, __extension__ __PRETTY_FUNCTION__)
); (const char *)((rhost)+1);})
;
3786
3787 /* Ensure neither host nor rhost are IP addresses */
3788 if (urldb__host_is_ip_address(host_data) ||
3789 urldb__host_is_ip_address(rhost_data)) {
3790 /* IP address, so no partial match */
3791 lwc_string_unref(rhost){ lwc_string *__lwc_s = (rhost); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3791, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3792 goto error;
3793 }
3794
3795 /* Not exact match, so try the following:
3796 *
3797 * 1) Find the longest common suffix of host and rhost
3798 * (may be all of host/rhost)
3799 * 2) Discard characters from the start of the suffix
3800 * until the suffix starts with a dot
3801 * (prevents foobar.com matching bar.com)
3802 * 3) Ensure the suffix is non-empty and contains
3803 * embedded dots (to avoid permitting .com as a
3804 * suffix)
3805 *
3806 * Note that the above in no way resembles the
3807 * domain matching algorithm found in RFC2109.
3808 * It does, however, model the real world rather
3809 * more accurately.
3810 */
3811
3812 /** \todo In future, we should consult a TLD service
3813 * instead of just looking for embedded dots.
3814 */
3815
3816 hptr = host_data + lwc_string_length(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3816, __extension__ __PRETTY_FUNCTION__)
); (host)->len;})
- 1;
3817 rptr = rhost_data + lwc_string_length(rhost)({((rhost != ((void*)0)) ? (void) (0) : __assert_fail ("rhost != NULL"
, "content/urldb.c", 3817, __extension__ __PRETTY_FUNCTION__)
); (rhost)->len;})
- 1;
3818
3819 /* 1 */
3820 while (hptr >= host_data && rptr >= rhost_data) {
3821 if (*hptr != *rptr)
3822 break;
3823 hptr--;
3824 rptr--;
3825 }
3826 /* Ensure we end up pointing at the start of the
3827 * common suffix. The above loop will exit pointing
3828 * to the byte before the start of the suffix. */
3829 hptr++;
3830
3831 /* 2 */
3832 while (*hptr != '\0' && *hptr != '.')
3833 hptr++;
3834
3835 /* 3 */
3836 if (*hptr == '\0' ||
3837 (dot = strchr(hptr + 1, '.')) == NULL((void*)0) ||
3838 *(dot + 1) == '\0') {
3839 lwc_string_unref(rhost){ lwc_string *__lwc_s = (rhost); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3839, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3840 goto error;
3841 }
3842 }
3843
3844 lwc_string_unref(rhost){ lwc_string *__lwc_s = (rhost); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3844, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3845 }
3846
3847 end = cur + strlen(cur) - 2 /* Trailing CRLF */;
3848
3849 do {
3850 struct cookie_internal_data *c;
3851 char *dot;
3852 size_t len;
3853#ifdef WITH_NSPSL1
3854 const char *suffix;
3855#endif
3856
3857 c = urldb_parse_cookie(url, &cur);
3858 if (!c) {
3859 /* failed => stop parsing */
3860 goto error;
3861 }
3862
3863 /* validate cookie */
3864
3865 /* 4.2.2:i Cookie must have NAME and VALUE */
3866 if (!c->name || !c->value) {
3867 urldb_free_cookie(c);
3868 goto error;
3869 }
3870
3871 /* 4.3.2:i Cookie path must be a prefix of URL path */
3872 len = strlen(c->path);
3873 if (len > lwc_string_length(path)({((path != ((void*)0)) ? (void) (0) : __assert_fail ("path != NULL"
, "content/urldb.c", 3873, __extension__ __PRETTY_FUNCTION__)
); (path)->len;})
||
3874 strncmp(c->path, lwc_string_data(path)({((path != ((void*)0)) ? (void) (0) : __assert_fail ("path != NULL"
, "content/urldb.c", 3874, __extension__ __PRETTY_FUNCTION__)
); (const char *)((path)+1);})
,
3875 len) != 0) {
3876 urldb_free_cookie(c);
3877 goto error;
3878 }
3879
3880#ifdef WITH_NSPSL1
3881 /* check domain is not a public suffix */
3882 dot = c->domain;
3883 if (*dot == '.') {
3884 dot++;
3885 }
3886 suffix = nspsl_getpublicsuffix(dot);
3887 if (suffix == NULL((void*)0)) {
3888 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3889
, }; nslog__log(&_nslog_ctx, "domain %s was a public suffix domain"
, dot); } } while(0)
3889 "domain %s was a public suffix domain", dot)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 3889
, }; nslog__log(&_nslog_ctx, "domain %s was a public suffix domain"
, dot); } } while(0)
;
3890 urldb_free_cookie(c);
3891 goto error;
3892 }
3893#else
3894 /* 4.3.2:ii Cookie domain must contain embedded dots */
3895 dot = strchr(c->domain + 1, '.');
3896 if (!dot || *(dot + 1) == '\0') {
3897 /* no embedded dots */
3898 urldb_free_cookie(c);
3899 goto error;
3900 }
3901#endif
3902
3903 /* Domain match fetch host with cookie domain */
3904 if (strcasecmp(lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3904, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
, c->domain) != 0) {
3905 int hlen, dlen;
3906 char *domain = c->domain;
3907
3908 /* c->domain must be a domain cookie here because:
3909 * c->domain is either:
3910 * + specified in the header as a domain cookie
3911 * (non-domain cookies in the header are ignored
3912 * by urldb_parse_cookie / urldb_parse_avpair)
3913 * + defaulted to the URL's host part
3914 * (by urldb_parse_cookie if no valid domain was
3915 * specified in the header)
3916 *
3917 * The latter will pass the strcasecmp above, which
3918 * leaves the former (i.e. a domain cookie)
3919 */
3920 assert(c->domain[0] == '.')((c->domain[0] == '.') ? (void) (0) : __assert_fail ("c->domain[0] == '.'"
, "content/urldb.c", 3920, __extension__ __PRETTY_FUNCTION__)
)
;
3921
3922 /* 4.3.2:iii */
3923 if (urldb__host_is_ip_address(lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3923, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
)) {
3924 /* IP address, so no partial match */
3925 urldb_free_cookie(c);
3926 goto error;
3927 }
3928
3929 hlen = lwc_string_length(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3929, __extension__ __PRETTY_FUNCTION__)
); (host)->len;})
;
3930 dlen = strlen(c->domain);
3931
3932 if (hlen <= dlen && hlen != dlen - 1) {
3933 /* Partial match not possible */
3934 urldb_free_cookie(c);
3935 goto error;
3936 }
3937
3938 if (hlen == dlen - 1) {
3939 /* Relax matching to allow
3940 * host a.com to match .a.com */
3941 domain++;
3942 dlen--;
3943 }
3944
3945 if (strcasecmp(lwc_string_data(host)({((host != ((void*)0)) ? (void) (0) : __assert_fail ("host != NULL"
, "content/urldb.c", 3945, __extension__ __PRETTY_FUNCTION__)
); (const char *)((host)+1);})
+ (hlen - dlen),
3946 domain)) {
3947 urldb_free_cookie(c);
3948 goto error;
3949 }
3950
3951 /* 4.3.2:iv Ensure H contains no dots
3952 *
3953 * If you believe the spec, H should contain no
3954 * dots in _any_ cookie. Unfortunately, however,
3955 * reality differs in that many sites send domain
3956 * cookies of the form .foo.com from hosts such
3957 * as bar.bat.foo.com and then expect domain
3958 * matching to work. Thus we have to do what they
3959 * expect, regardless of any potential security
3960 * implications.
3961 *
3962 * This is what code conforming to the spec would
3963 * look like:
3964 *
3965 * for (int i = 0; i < (hlen - dlen); i++) {
3966 * if (host[i] == '.') {
3967 * urldb_free_cookie(c);
3968 * goto error;
3969 * }
3970 * }
3971 */
3972 }
3973
3974 /* Now insert into database */
3975 if (!urldb_insert_cookie(c, scheme, urlt))
3976 goto error;
3977 } while (cur < end);
3978
3979 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3979
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3980 lwc_string_unref(path){ lwc_string *__lwc_s = (path); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3980
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3981 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3981, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3982 nsurl_unref(urlt);
3983
3984 return true1;
3985
3986error:
3987 lwc_string_unref(host){ lwc_string *__lwc_s = (host); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3987
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3988 lwc_string_unref(path){ lwc_string *__lwc_s = (path); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c", 3988
, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt--; if
((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1) &&
(__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s
); }
;
3989 lwc_string_unref(scheme){ lwc_string *__lwc_s = (scheme); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 3989, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
3990 nsurl_unref(urlt);
3991
3992 return false0;
3993}
3994
3995
3996/* exported interface documented in content/urldb.h */
3997char *urldb_get_cookie(nsurl *url, bool_Bool include_http_only)
3998{
3999 const struct path_data *p, *q;
4000 const struct host_part *h;
4001 lwc_string *path_lwc;
4002 struct cookie_internal_data *c;
4003 int count = 0, version = COOKIE_RFC2965;
4004 struct cookie_internal_data **matched_cookies;
4005 int matched_cookies_size = 20;
4006 int ret_alloc = 4096, ret_used = 1;
4007 const char *path;
4008 char *ret;
4009 lwc_string *scheme;
4010 time_t now;
4011 int i;
4012 bool_Bool match;
4013
4014 assert(url != NULL)((url != ((void*)0)) ? (void) (0) : __assert_fail ("url != NULL"
, "content/urldb.c", 4014, __extension__ __PRETTY_FUNCTION__)
)
;
4015
4016 /* The URL must exist in the db in order to find relevant cookies, since
4017 * we search up the tree from the URL node, and cookies from further
4018 * up also apply. */
4019 urldb_add_url(url);
4020
4021 p = urldb_find_url(url);
4022 if (!p)
4023 return NULL((void*)0);
4024
4025 scheme = p->scheme;
4026
4027 matched_cookies = malloc(matched_cookies_size *
4028 sizeof(struct cookie_internal_data *));
4029 if (!matched_cookies)
4030 return NULL((void*)0);
4031
4032#define GROW_MATCHED_COOKIES \
4033 do { \
4034 if (count == matched_cookies_size) { \
4035 struct cookie_internal_data **temp; \
4036 temp = realloc(matched_cookies, \
4037 (matched_cookies_size + 20) * \
4038 sizeof(struct cookie_internal_data *)); \
4039 \
4040 if (temp == NULL((void*)0)) { \
4041 free(ret); \
4042 free(matched_cookies); \
4043 return NULL((void*)0); \
4044 } \
4045 \
4046 matched_cookies = temp; \
4047 matched_cookies_size += 20; \
4048 } \
4049 } while(0)
4050
4051 ret = malloc(ret_alloc);
4052 if (!ret) {
4053 free(matched_cookies);
4054 return NULL((void*)0);
4055 }
4056
4057 ret[0] = '\0';
4058
4059 path_lwc = nsurl_get_component(url, NSURL_PATH);
4060 if (path_lwc == NULL((void*)0)) {
4061 free(ret);
4062 free(matched_cookies);
4063 return NULL((void*)0);
4064 }
4065 path = lwc_string_data(path_lwc)({((path_lwc != ((void*)0)) ? (void) (0) : __assert_fail ("path_lwc != NULL"
, "content/urldb.c", 4065, __extension__ __PRETTY_FUNCTION__)
); (const char *)((path_lwc)+1);})
;
4066 lwc_string_unref(path_lwc){ lwc_string *__lwc_s = (path_lwc); ((__lwc_s != ((void*)0)) ?
(void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 4066, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
4067
4068 now = time(NULL((void*)0));
4069
4070 if (*(p->segment) != '\0') {
4071 /* Match exact path, unless directory, when prefix matching
4072 * will handle this case for us. */
4073 for (q = p->parent->children; q; q = q->next) {
4074 if (strcmp(q->segment, p->segment))
4075 continue;
4076
4077 /* Consider all cookies associated with
4078 * this exact path */
4079 for (c = q->cookies; c; c = c->next) {
4080 if (c->expires != -1 && c->expires < now)
4081 /* cookie has expired => ignore */
4082 continue;
4083
4084 if (c->secure && lwc_string_isequal(((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4085 q->scheme,((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4086 corestring_lwc_https,((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4087 &match)((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
&&
4088 match == false0)
4089 /* secure cookie for insecure host.
4090 * ignore */
4091 continue;
4092
4093 if (c->http_only && !include_http_only)
4094 /* Ignore HttpOnly */
4095 continue;
4096
4097 matched_cookies[count++] = c;
4098
4099 GROW_MATCHED_COOKIES;
4100
4101 if (c->version < (unsigned int)version)
4102 version = c->version;
4103
4104 c->last_used = now;
4105
4106 cookie_manager_add((struct cookie_data *)c);
4107 }
4108 }
4109 }
4110
4111 /* Now consider cookies whose paths prefix-match ours */
4112 for (p = p->parent; p; p = p->parent) {
4113 /* Find directory's path entry(ies) */
4114 /* There are potentially multiple due to differing schemes */
4115 for (q = p->children; q; q = q->next) {
4116 if (*(q->segment) != '\0')
4117 continue;
4118
4119 for (c = q->cookies; c; c = c->next) {
4120 if (c->expires != -1 && c->expires < now)
4121 /* cookie has expired => ignore */
4122 continue;
4123
4124 if (c->secure && lwc_string_isequal(((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4125 q->scheme,((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4126 corestring_lwc_https,((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4127 &match)((*(&match) = ((q->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
&&
4128 match == false0)
4129 /* Secure cookie for insecure server
4130 * => ignore */
4131 continue;
4132
4133 matched_cookies[count++] = c;
4134
4135 GROW_MATCHED_COOKIES;
4136
4137 if (c->version < (unsigned int) version)
4138 version = c->version;
4139
4140 c->last_used = now;
4141
4142 cookie_manager_add((struct cookie_data *)c);
4143 }
4144 }
4145
4146 if (!p->parent) {
4147 /* No parent, so bail here. This can't go in
4148 * the loop exit condition as we also want to
4149 * process the top-level node.
4150 *
4151 * If p->parent is NULL then p->cookies are
4152 * the domain cookies and thus we don't even
4153 * try matching against them.
4154 */
4155 break;
4156 }
4157
4158 /* Consider p itself - may be the result of Path=/foo */
4159 for (c = p->cookies; c; c = c->next) {
4160 if (c->expires != -1 && c->expires < now)
4161 /* cookie has expired => ignore */
4162 continue;
4163
4164 /* Ensure cookie path is a prefix of the resource */
4165 if (strncmp(c->path, path, strlen(c->path)) != 0)
4166 /* paths don't match => ignore */
4167 continue;
4168
4169 if (c->secure && lwc_string_isequal(p->scheme,((*(&match) = ((p->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4170 corestring_lwc_https,((*(&match) = ((p->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
4171 &match)((*(&match) = ((p->scheme) == (corestring_lwc_https)))
, lwc_error_ok)
&&
4172 match == false0)
4173 /* Secure cookie for insecure server
4174 * => ignore */
4175 continue;
4176
4177 matched_cookies[count++] = c;
4178
4179 GROW_MATCHED_COOKIES;
4180
4181 if (c->version < (unsigned int) version)
4182 version = c->version;
4183
4184 c->last_used = now;
4185
4186 cookie_manager_add((struct cookie_data *)c);
4187 }
4188
4189 }
4190
4191 /* Finally consider domain cookies for hosts which domain match ours */
4192 for (h = (const struct host_part *)p; h && h != &db_root;
4193 h = h->parent) {
4194 for (c = h->paths.cookies; c; c = c->next) {
4195 if (c->expires != -1 && c->expires < now)
4196 /* cookie has expired => ignore */
4197 continue;
4198
4199 /* Ensure cookie path is a prefix of the resource */
4200 if (strncmp(c->path, path, strlen(c->path)) != 0)
4201 /* paths don't match => ignore */
4202 continue;
4203
4204 if (c->secure && lwc_string_isequal(scheme,((*(&match) = ((scheme) == (corestring_lwc_https))), lwc_error_ok
)
4205 corestring_lwc_https,((*(&match) = ((scheme) == (corestring_lwc_https))), lwc_error_ok
)
4206 &match)((*(&match) = ((scheme) == (corestring_lwc_https))), lwc_error_ok
)
&&
4207 match == false0)
4208 /* secure cookie for insecure host. ignore */
4209 continue;
4210
4211 matched_cookies[count++] = c;
4212
4213 GROW_MATCHED_COOKIES;
4214
4215 if (c->version < (unsigned int)version)
4216 version = c->version;
4217
4218 c->last_used = now;
4219
4220 cookie_manager_add((struct cookie_data *)c);
4221 }
4222 }
4223
4224 if (count == 0) {
4225 /* No cookies found */
4226 free(ret);
4227 free(matched_cookies);
4228 return NULL((void*)0);
4229 }
4230
4231 /* and build output string */
4232 if (version > COOKIE_NETSCAPE) {
4233 sprintf(ret, "$Version=%d", version);
4234 ret_used = strlen(ret) + 1;
4235 }
4236
4237 for (i = 0; i < count; i++) {
4238 if (!urldb_concat_cookie(matched_cookies[i], version,
4239 &ret_used, &ret_alloc, &ret)) {
4240 free(ret);
4241 free(matched_cookies);
4242 return NULL((void*)0);
4243 }
4244 }
4245
4246 if (version == COOKIE_NETSCAPE) {
4247 /* Old-style cookies => no version & skip "; " */
4248 memmove(ret, ret + 2, ret_used - 2);
4249 ret_used -= 2;
4250 }
4251
4252 /* Now, shrink the output buffer to the required size */
4253 {
4254 char *temp = realloc(ret, ret_used);
4255 if (!temp) {
4256 free(ret);
4257 free(matched_cookies);
4258 return NULL((void*)0);
4259 }
4260
4261 ret = temp;
4262 }
4263
4264 free(matched_cookies);
4265
4266 return ret;
4267
4268#undef GROW_MATCHED_COOKIES
4269}
4270
4271
4272/* exported interface documented in content/urldb.h */
4273void urldb_delete_cookie(const char *domain, const char *path,
4274 const char *name)
4275{
4276 urldb_delete_cookie_hosts(domain, path, name, &db_root);
4277}
4278
4279
4280/* exported interface documented in content/urldb.h */
4281void urldb_load_cookies(const char *filename)
4282{
4283 FILE *fp;
4284 char s[16*1024];
4285
4286 assert(filename)((filename) ? (void) (0) : __assert_fail ("filename", "content/urldb.c"
, 4286, __extension__ __PRETTY_FUNCTION__))
;
4287
4288 fp = fopen(filename, "r");
4289 if (!fp)
4290 return;
4291
4292#define FIND_T { \
4293 for (; *p && *p != '\t'; p++) \
4294 ; /* do nothing */ \
4295 if (p >= end) { \
4296 NSLOG(netsurf, INFO, "Overran input")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 4296
, }; nslog__log(&_nslog_ctx, "Overran input"); } } while(
0)
; \
4297 continue; \
4298 } \
4299 *p++ = '\0'; \
4300 }
4301
4302#define SKIP_T { \
4303 for (; *p && *p == '\t'; p++) \
4304 ; /* do nothing */ \
4305 if (p >= end) { \
4306 NSLOG(netsurf, INFO, "Overran input")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 4306
, }; nslog__log(&_nslog_ctx, "Overran input"); } } while(
0)
; \
4307 continue; \
4308 } \
4309 }
4310
4311 while (fgets(s, sizeof s, fp)) {
4312 char *p = s, *end = 0,
4313 *domain, *path, *name, *value, *scheme, *url,
4314 *comment;
4315 int version, domain_specified, path_specified,
4316 secure, http_only, no_destroy, value_quoted;
4317 time_t expires, last_used;
4318 struct cookie_internal_data *c;
4319
4320 if(s[0] == 0 || s[0] == '#')
4321 /* Skip blank lines or comments */
4322 continue;
4323
4324 s[strlen(s) - 1] = '\0'; /* lose terminating newline */
4325 end = s + strlen(s);
4326
4327 /* Look for file version first
4328 * (all input is ignored until this is read)
4329 */
4330 if (strncasecmp(s, "Version:", 8) == 0) {
4331 FIND_T; SKIP_T; loaded_cookie_file_version = atoi(p);
4332
4333 if (loaded_cookie_file_version <
4334 MIN_COOKIE_FILE_VERSION100) {
4335 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 4336
, }; nslog__log(&_nslog_ctx, "Unsupported Cookie file version"
); } } while(0)
4336 "Unsupported Cookie file version")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/urldb.c", sizeof("content/urldb.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 4336
, }; nslog__log(&_nslog_ctx, "Unsupported Cookie file version"
); } } while(0)
;
4337 break;
4338 }
4339
4340 continue;
4341 } else if (loaded_cookie_file_version == 0) {
4342 /* Haven't yet seen version; skip this input */
4343 continue;
4344 }
4345
4346 /* One cookie/line */
4347
4348 /* Parse input */
4349 FIND_T; version = atoi(s);
4350 SKIP_T; domain = p; FIND_T;
4351 SKIP_T; domain_specified = atoi(p); FIND_T;
4352 SKIP_T; path = p; FIND_T;
4353 SKIP_T; path_specified = atoi(p); FIND_T;
4354 SKIP_T; secure = atoi(p); FIND_T;
4355 if (loaded_cookie_file_version > 101) {
4356 /* Introduced in version 1.02 */
4357 SKIP_T; http_only = atoi(p); FIND_T;
4358 } else {
4359 http_only = 0;
4360 }
4361 SKIP_T; expires = (time_t)atoi(p); FIND_T;
4362 SKIP_T; last_used = (time_t)atoi(p); FIND_T;
4363 SKIP_T; no_destroy = atoi(p); FIND_T;
4364 SKIP_T; name = p; FIND_T;
4365 SKIP_T; value = p; FIND_T;
4366 if (loaded_cookie_file_version > 100) {
4367 /* Introduced in version 1.01 */
4368 SKIP_T; value_quoted = atoi(p); FIND_T;
4369 } else {
4370 value_quoted = 0;
4371 }
4372 SKIP_T; scheme = p; FIND_T;
4373 SKIP_T; url = p; FIND_T;
4374
4375 /* Comment may have no content, so don't
4376 * use macros as they'll break */
4377 for (; *p && *p == '\t'; p++)
4378 ; /* do nothing */
4379 comment = p;
4380
4381 assert(p <= end)((p <= end) ? (void) (0) : __assert_fail ("p <= end", "content/urldb.c"
, 4381, __extension__ __PRETTY_FUNCTION__))
;
4382
4383 /* Now create cookie */
4384 c = malloc(sizeof(struct cookie_internal_data));
4385 if (!c)
4386 break;
4387
4388 c->name = strdup(name);
4389 c->value = strdup(value);
4390 c->value_was_quoted = value_quoted;
4391 c->comment = strdup(comment);
4392 c->domain_from_set = domain_specified;
4393 c->domain = strdup(domain);
4394 c->path_from_set = path_specified;
4395 c->path = strdup(path);
4396 c->expires = expires;
4397 c->last_used = last_used;
4398 c->secure = secure;
4399 c->http_only = http_only;
4400 c->version = version;
4401 c->no_destroy = no_destroy;
4402
4403 if (!(c->name && c->value && c->comment &&
4404 c->domain && c->path)) {
4405 urldb_free_cookie(c);
4406 break;
4407 }
4408
4409 if (c->domain[0] != '.') {
4410 lwc_string *scheme_lwc = NULL((void*)0);
4411 nsurl *url_nsurl = NULL((void*)0);
4412
4413 assert(scheme[0] != 'u')((scheme[0] != 'u') ? (void) (0) : __assert_fail ("scheme[0] != 'u'"
, "content/urldb.c", 4413, __extension__ __PRETTY_FUNCTION__)
)
;
4414
4415 if (nsurl_create(url, &url_nsurl) != NSERROR_OK) {
4416 urldb_free_cookie(c);
4417 break;
4418 }
4419 scheme_lwc = nsurl_get_component(url_nsurl,
4420 NSURL_SCHEME);
4421
4422 /* And insert it into database */
4423 if (!urldb_insert_cookie(c, scheme_lwc, url_nsurl)) {
4424 /* Cookie freed for us */
4425 nsurl_unref(url_nsurl);
4426 lwc_string_unref(scheme_lwc){ lwc_string *__lwc_s = (scheme_lwc); ((__lwc_s != ((void*)0)
) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 4426, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
4427 break;
4428 }
4429 nsurl_unref(url_nsurl);
4430 lwc_string_unref(scheme_lwc){ lwc_string *__lwc_s = (scheme_lwc); ((__lwc_s != ((void*)0)
) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/urldb.c"
, 4430, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
4431
4432 } else {
4433 if (!urldb_insert_cookie(c, NULL((void*)0), NULL((void*)0))) {
4434 /* Cookie freed for us */
4435 break;
4436 }
4437 }
4438 }
4439
4440#undef SKIP_T
4441#undef FIND_T
4442
4443 fclose(fp);
4444}
4445
4446
4447/* exported interface documented in content/urldb.h */
4448void urldb_save_cookies(const char *filename)
4449{
4450 FILE *fp;
4451 int cookie_file_version = max(loaded_cookie_file_version,(((loaded_cookie_file_version)>(102))?(loaded_cookie_file_version
):(102))
4452 COOKIE_FILE_VERSION)(((loaded_cookie_file_version)>(102))?(loaded_cookie_file_version
):(102))
;
4453
4454 assert(filename)((filename) ? (void) (0) : __assert_fail ("filename", "content/urldb.c"
, 4454, __extension__ __PRETTY_FUNCTION__))
;
4455
4456 fp = fopen(filename, "w");
4457 if (!fp)
4458 return;
4459
4460 fprintf(fp, "# NetSurf cookies file.\n"
4461 "#\n"
4462 "# Lines starting with a '#' are comments, "
4463 "blank lines are ignored.\n"
4464 "#\n"
4465 "# All lines prior to \"Version:\t%d\" are discarded.\n"
4466 "#\n"
4467 "# Version\tDomain\tDomain from Set-Cookie\tPath\t"
4468 "Path from Set-Cookie\tSecure\tHTTP-Only\tExpires\tLast used\t"
4469 "No destroy\tName\tValue\tValue was quoted\tScheme\t"
4470 "URL\tComment\n",
4471 cookie_file_version);
4472 fprintf(fp, "Version:\t%d\n", cookie_file_version);
4473
4474 urldb_save_cookie_hosts(fp, &db_root);
4475
4476 fclose(fp);
4477}
4478
4479
4480/* exported interface documented in netsurf/url_db.h */
4481void urldb_dump(void)
4482{
4483 int i;
4484
4485 urldb_dump_hosts(&db_root);
4486
4487 for (i = 0; i != NUM_SEARCH_TREES28; i++) {
4488 urldb_dump_search(search_trees[i], 0);
4489 }
4490}
4491
4492
4493
4494