Dillo v3.2.0-180-g355fbd0a
Loading...
Searching...
No Matches
cache.c
Go to the documentation of this file.
1/*
2 * File: cache.c
3 *
4 * Copyright 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright 2024-2026 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program 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; either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
13/*
14 * @file
15 * Dillo's cache module
16 */
17
18#include <sys/types.h>
19
20#include <limits.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "msg.h"
25#include "IO/Url.h"
26#include "IO/IO.h"
27#include "web.hh"
28#include "dicache.h"
29#include "nav.h"
30#include "cookies.h"
31#include "hsts.h"
32#include "misc.h"
33#include "capi.h"
34#include "decode.h"
35#include "auth.h"
36#include "domain.h"
37#include "timeout.hh"
38#include "uicmd.hh"
39#include "dlib/dlib.h"
40#include "prefs.h"
41
43#define MAX_INIT_BUF 1024*1024
45#define HUGE_FILESIZE 15*1024*1024
46
47/*
48 * Local data types
49 */
50
51typedef struct {
52 const DilloUrl *Url;
53 char *TypeDet;
54 char *TypeHdr;
55 char *TypeMeta;
56 char *TypeNorm;
57 char *ContentDisposition;
58 Dstr *Header;
59 const DilloUrl *Location;
60 Dlist *Auth;
61 Dstr *Data;
62 Dstr *UTF8Data;
63 int DataRefcount;
64 DecodeTransfer *TransferDecoder;
65 Decode *ContentDecoder;
66 Decode *CharsetDecoder;
67 int ExpectedSize;
68 int TransferSize;
69 uint_t Flags;
70 int Hits;
71} CacheEntry_t;
72
73
74/*
75 * Local data
76 */
79
83
88
89
90/*
91 * Forward declarations
92 */
93static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry);
94static void Cache_delayed_process_queue(CacheEntry_t *entry);
95static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw);
96static Dstr *Cache_data(CacheEntry_t *entry);
97static void Cache_entry_free(CacheEntry_t *entry, int deep);
98
102static int Cache_entry_cmp(const void *v1, const void *v2)
103{
104 const CacheEntry_t *d1 = v1, *d2 = v2;
105
106 return a_Url_cmp(d1->Url, d2->Url);
107}
108
112static int Cache_entry_by_url_cmp(const void *v1, const void *v2)
113{
114 const DilloUrl *u1 = ((CacheEntry_t*)v1)->Url;
115 const DilloUrl *u2 = v2;
116
117 return a_Url_cmp(u1, u2);
118}
119
123void a_Cache_init(void)
124{
127 CachedURLs = dList_new(256);
128
129 /* inject the splash screen in the cache */
130 {
131 DilloUrl *url = a_Url_new("about:splash", NULL);
133 a_Cache_entry_inject(url, ds->str, ds->len,
135 dStr_free(ds, 1);
136 a_Url_free(url);
137 }
138}
139
140/* Client operations ------------------------------------------------------ */
141
147static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web,
148 CA_Callback_t Callback, void *CbData)
149{
150 static int ClientKey = 0; /* Provide a primary key for each client */
151 CacheClient_t *NewClient;
152
153 if (ClientKey < INT_MAX) /* check for integer overflow */
154 ClientKey++;
155 else
156 ClientKey = 1;
157
158 NewClient = dNew(CacheClient_t, 1);
159 NewClient->Key = ClientKey;
160 NewClient->Url = Url;
161 NewClient->Version = 0;
162 NewClient->Buf = NULL;
163 NewClient->BufSize = 0;
164 NewClient->Callback = Callback;
165 NewClient->CbData = CbData;
166 NewClient->Web = Web;
167
168 dList_append(ClientQueue, NewClient);
169
170 return ClientKey;
171}
172
176static int Cache_client_by_key_cmp(const void *client, const void *key)
177{
178 return ((CacheClient_t *)client)->Key - VOIDP2INT(key);
179}
180
185{
186 if (Client) {
187 dList_remove(ClientQueue, Client);
188 a_Web_free(Client->Web);
189 dFree(Client);
190 }
191}
192
193
194/* Entry operations ------------------------------------------------------- */
195
199static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
200{
201 NewEntry->Url = a_Url_dup(Url);
202 NewEntry->TypeDet = NULL;
203 NewEntry->TypeHdr = NULL;
204 NewEntry->TypeMeta = NULL;
205 NewEntry->TypeNorm = NULL;
206 NewEntry->ContentDisposition = NULL;
207 NewEntry->Header = dStr_new("");
208 NewEntry->Location = NULL;
209 NewEntry->Auth = NULL;
210 NewEntry->Data = dStr_sized_new(8*1024);
211 NewEntry->UTF8Data = NULL;
212 NewEntry->DataRefcount = 0;
213 NewEntry->TransferDecoder = NULL;
214 NewEntry->ContentDecoder = NULL;
215 NewEntry->CharsetDecoder = NULL;
216 NewEntry->ExpectedSize = 0;
217 NewEntry->TransferSize = 0;
218 NewEntry->Flags = CA_IsEmpty | CA_InProgress | CA_KeepAlive;
219 NewEntry->Hits = 0;
220}
221
226static CacheEntry_t *Cache_entry_search(const DilloUrl *Url)
227{
229}
230
234static CacheEntry_t *Cache_entry_search_with_redirect(const DilloUrl *Url)
235{
236 int i;
237 CacheEntry_t *entry;
238
239 for (i = 0; (entry = Cache_entry_search(Url)); ++i) {
240
241 /* Test for a redirection loop */
242 if (entry->Flags & CA_RedirectLoop || i == 3) {
243 _MSG_WARN("Redirect loop for URL: >%s<\n", URL_STR_(Url));
244 break;
245 }
246 /* Test for a working redirection */
247 if (entry->Flags & CA_Redirect && entry->Location) {
248 Url = entry->Location;
249 } else
250 break;
251 }
252 return entry;
253}
254
258static CacheEntry_t *Cache_entry_add(const DilloUrl *Url)
259{
260 CacheEntry_t *old_entry, *new_entry;
261
262 if ((old_entry = Cache_entry_search(Url))) {
263 MSG_WARN("Cache_entry_add, leaking an entry.\n");
264 dList_remove(CachedURLs, old_entry);
265 }
266
267 new_entry = dNew(CacheEntry_t, 1);
268 Cache_entry_init(new_entry, Url); /* Set safe values */
270 return new_entry;
271}
272
277static int Cache_bufsize(CacheEntry_t *e)
278{
279 Dstr *buf = Cache_data(e);
280 if (buf)
281 return buf->len;
282 else
283 return 0;
284}
285
294 const char *buf, size_t len, int flags)
295{
296 CacheEntry_t *entry;
297
298 if (!(entry = Cache_entry_search(Url)))
299 entry = Cache_entry_add(Url);
300
301 if (flags & CA_GotLength) {
302 entry->Flags = flags;
303 if (len)
304 entry->Flags &= ~CA_IsEmpty;
305 dStr_truncate(entry->Data, 0);
306 dStr_append_l(entry->Data, buf, len);
307 dStr_fit(entry->Data);
308 entry->ExpectedSize = entry->TransferSize = entry->Data->len;
309 } else {
310 /* Inject a new empty entry and process it to parse the headers and setup
311 * any decoder or content handler. */
312 Cache_entry_free(entry, 0);
313 Cache_entry_init(entry, Url);
314 a_Cache_process_dbuf(IORead, buf, len, Url);
315 }
316}
317
321static void Cache_auth_free(Dlist *auth)
322{
323 int i;
324 void *auth_field;
325 for (i = 0; (auth_field = dList_nth_data(auth, i)); ++i)
326 dFree(auth_field);
327 dList_free(auth);
328}
329
333static void Cache_entry_free(CacheEntry_t *entry, int deep)
334{
335 a_Url_free((DilloUrl *)entry->Url);
336 dFree(entry->TypeDet);
337 dFree(entry->TypeHdr);
338 dFree(entry->TypeMeta);
339 dFree(entry->TypeNorm);
340 dFree(entry->ContentDisposition);
341 dStr_free(entry->Header, TRUE);
342 a_Url_free((DilloUrl *)entry->Location);
343 Cache_auth_free(entry->Auth);
344 dStr_free(entry->Data, 1);
345 dStr_free(entry->UTF8Data, 1);
346 if (entry->CharsetDecoder)
347 a_Decode_free(entry->CharsetDecoder);
348 if (entry->TransferDecoder)
349 a_Decode_transfer_free(entry->TransferDecoder);
350 if (entry->ContentDecoder)
351 a_Decode_free(entry->ContentDecoder);
352 if (deep)
353 dFree(entry);
354}
355
361static void Cache_entry_remove(CacheEntry_t *entry, DilloUrl *url)
362{
363 int i;
364 CacheClient_t *Client;
365
366 if (!entry && !(entry = Cache_entry_search(url)))
367 return;
368 if (entry->Flags & CA_InternalUrl)
369 return;
370
371 /* remove all clients for this entry */
372 for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
373 if (Client->Url == entry->Url) {
374 a_Cache_stop_client(Client->Key);
375 --i;
376 }
377 }
378
379 /* remove from DelayedQueue */
381
382 /* remove from dicache */
383 a_Dicache_invalidate_entry(entry->Url);
384
385 /* remove from cache */
386 dList_remove(CachedURLs, entry);
387 Cache_entry_free(entry, 1);
388}
389
394{
395 Cache_entry_remove(NULL, url);
396}
397
398/* Misc. operations ------------------------------------------------------- */
399
400static Dstr *Cache_stats(void)
401{
402 float totalKB = 0.0f;
403
404 Dstr *s = dStr_new(
405 "<!DOCTYPE HTML>\n"
406 "<html>\n"
407 "<head><title>Dillo Cache</title></head>\n"
408 "<body>\n");
409
410 int n = dList_length(CachedURLs);
411 dStr_sprintfa(s, "<h1>Cached URLs (%d)</h1>\n", n);
412
413 dStr_append(s, "<table>\n");
414 dStr_append(s, "<tr>\n");
415 dStr_append(s, "<th>Hits</th>\n");
416 dStr_append(s, "<th>Size</th>\n");
417 dStr_append(s, "<th>URL</th>\n");
418 dStr_append(s, "</tr>\n");
419 for (int i = 0; i < n; i++) {
420 CacheEntry_t *e = dList_nth_data(CachedURLs, i);
421 float sizeKB = Cache_bufsize(e) / 1024.0f;
422 const char *url = URL_STR(e->Url);
423 dStr_append(s, "<tr>\n");
424 dStr_sprintfa(s, "<td style='text-align:right'>%d</td>\n", e->Hits);
425 dStr_sprintfa(s, "<td style='text-align:right'>%.2f KiB</td>\n", sizeKB);
426 dStr_sprintfa(s, "<td><a href='%s'>", url);
427 dStr_shorten(s, url, 60);
428 dStr_append(s, "</a></td>\n");
429 dStr_append(s, "</tr>\n");
430 totalKB += sizeKB;
431 }
432 dStr_append(s, "</table>\n");
433 dStr_sprintfa(s, "<p>Total cached: %.2f MiB</p>\n", totalKB / 1024.0f);
434 dStr_append(s,
435 "</body>\n"
436 "</html>\n");
437
438 return s;
439}
440
441static int Cache_internal_url(CacheEntry_t *entry)
442{
443 Dstr *s = NULL;
444
445 if (strcmp(URL_PATH(entry->Url), "cache") == 0) {
446 s = Cache_stats();
447 } else if (strcmp(URL_PATH(entry->Url), "dicache") == 0) {
448 s = a_Dicache_stats();
449 }
450
451 if (s != NULL) {
452 a_Cache_entry_inject(entry->Url, s->str, s->len,
454 dStr_free(s, 1);
455 }
456
457 return 0;
458}
459
472int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
473{
474 int ClientKey;
475 CacheEntry_t *entry;
476 DilloWeb *Web = web;
477 DilloUrl *Url = Web->url;
478 int isInternal = 0;
479
480 if (dStrAsciiCasecmp(URL_SCHEME(Url), "about") == 0) {
481 _MSG("got internal URL: %s\n", URL_STR(Url));
482 isInternal = 1;
483 Cache_entry_remove(NULL, Url);
484 }
485
486 if (URL_FLAGS(Url) & URL_E2EQuery) {
487 /* remove current entry */
488 Cache_entry_remove(NULL, Url);
489 }
490
491 if ((entry = Cache_entry_search(Url))) {
492 _MSG("serving cached entry: %s\n", URL_STR(Url));
493 /* URL is cached: feed our client with cached data */
494 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
496 entry->Hits++;
497
498 } else {
499 _MSG("serving new entry: %s\n", URL_STR(Url));
500 /* URL not cached: create an entry, send our client to the queue,
501 * and open a new connection */
502 entry = Cache_entry_add(Url);
503
504 /* URL is an internal call, populate */
505 if (isInternal) {
506 _MSG("handling internal: %s\n", URL_STR(Url));
507 Cache_internal_url(entry);
508 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
510 } else {
511 ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData);
512 }
513 }
514
515 return ClientKey;
516}
517
522{
523 CacheEntry_t *entry = Cache_entry_search(url);
524 return (entry ? entry->Flags : 0);
525}
526
531{
532 CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
533 return (entry ? entry->Flags : 0);
534}
535
539static void Cache_ref_data(CacheEntry_t *entry)
540{
541 if (entry) {
542 entry->DataRefcount++;
543 _MSG("DataRefcount++: %d\n", entry->DataRefcount);
544 if (entry->CharsetDecoder &&
545 (!entry->UTF8Data || entry->DataRefcount == 1)) {
546 dStr_free(entry->UTF8Data, 1);
547 entry->UTF8Data = a_Decode_process(entry->CharsetDecoder,
548 entry->Data->str,
549 entry->Data->len);
550 }
551 }
552}
553
557static void Cache_unref_data(CacheEntry_t *entry)
558{
559 if (entry) {
560 entry->DataRefcount--;
561 _MSG("DataRefcount--: %d\n", entry->DataRefcount);
562
563 if (entry->CharsetDecoder) {
564 if (entry->DataRefcount == 0) {
565 dStr_free(entry->UTF8Data, 1);
566 entry->UTF8Data = NULL;
567 } else if (entry->DataRefcount < 0) {
568 MSG_ERR("Cache_unref_data: negative refcount\n");
569 entry->DataRefcount = 0;
570 }
571 }
572 }
573}
574
578static const char *Cache_current_content_type(CacheEntry_t *entry)
579{
580 return entry->TypeNorm ? entry->TypeNorm : entry->TypeMeta ? entry->TypeMeta
581 : entry->TypeHdr ? entry->TypeHdr : entry->TypeDet;
582}
583
587const char *a_Cache_get_content_type(const DilloUrl *url)
588{
589 CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
590
591 return (entry) ? Cache_current_content_type(entry) : NULL;
592}
593
597static Dstr *Cache_data(CacheEntry_t *entry)
598{
599 return entry->UTF8Data ? entry->UTF8Data : entry->Data;
600}
601
607const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
608 const char *from)
609{
610 const char *curr;
611 char *major, *minor, *charset;
612 CacheEntry_t *entry = Cache_entry_search(url);
613
614 dReturn_val_if_fail (entry != NULL, NULL);
615
616 _MSG("a_Cache_set_content_type {%s} {%s}\n", ctype, URL_STR(url));
617
618 curr = Cache_current_content_type(entry);
619 if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) {
620 /* Type is already been set. Do nothing.
621 * BTW, META overrides TypeHdr */
622 } else {
623 if (*from == 'h') {
624 /* Content-Type from HTTP header */
625 entry->TypeHdr = dStrdup(ctype);
626 } else {
627 /* Content-Type from META */
628 entry->TypeMeta = dStrdup(ctype);
629 }
630 if (a_Misc_content_type_cmp(curr, ctype)) {
631 /* ctype gives one different from current */
632 a_Misc_parse_content_type(ctype, &major, &minor, &charset);
633 if (*from == 'm' && charset &&
634 ((!major || !*major) && (!minor || !*minor))) {
635 /* META only gives charset; use detected MIME type too */
636 entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL);
637 } else if (*from == 'm' &&
638 !dStrnAsciiCasecmp(ctype, "text/xhtml", 10)) {
639 /* WORKAROUND: doxygen uses "text/xhtml" in META */
640 if (charset) {
641 entry->TypeNorm = dStrconcat("application/xhtml+xml",
642 "; charset=", charset, NULL);
643 } else {
644 entry->TypeNorm = dStrdup("application/xhtml+xml");
645 }
646 }
647 if (charset) {
648 if (entry->CharsetDecoder)
649 a_Decode_free(entry->CharsetDecoder);
650 entry->CharsetDecoder = a_Decode_charset_init(charset);
651 curr = Cache_current_content_type(entry);
652
653 /* Invalidate UTF8Data */
654 dStr_free(entry->UTF8Data, 1);
655 entry->UTF8Data = NULL;
656 }
657 dFree(major); dFree(minor); dFree(charset);
658 }
659 }
660 return curr;
661}
662
667int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
668{
669 CacheEntry_t *entry = Cache_entry_search_with_redirect(Url);
670 if (entry) {
671 Dstr *data;
672 Cache_ref_data(entry);
673 data = Cache_data(entry);
674 *PBuf = data->str;
675 *BufSize = data->len;
676 } else {
677 *PBuf = NULL;
678 *BufSize = 0;
679 }
680 return (entry ? 1 : 0);
681}
682
684{
685 CacheEntry_t *entry = Cache_entry_search_with_redirect(Url);
686 if (entry == NULL)
687 return NULL;
688
689 return entry->Header;
690}
691
699
700
707static char *Cache_parse_field(const char *header, const char *fieldname)
708{
709 char *field;
710 uint_t i, j;
711
712 for (i = 0; header[i]; i++) {
713 /* Search fieldname */
714 for (j = 0; fieldname[j]; j++)
715 if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j]))
716 break;
717 if (fieldname[j]) {
718 /* skip to next line */
719 for ( i += j; header[i] != '\n'; i++);
720 continue;
721 }
722
723 i += j;
724 if (header[i] == ':') {
725 /* Field found! */
726 while (header[++i] == ' ' || header[i] == '\t');
727 for (j = 0; header[i + j] != '\n'; j++);
728 while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
729 j--;
730 field = dStrndup(header + i, j);
731 return field;
732 }
733 while (header[i] != '\n') i++;
734 }
735 return NULL;
736}
737
741static Dlist *Cache_parse_multiple_fields(const char *header,
742 const char *fieldname)
743{
744 uint_t i, j;
745 Dlist *fields = dList_new(8);
746 char *field;
747
748 for (i = 0; header[i]; i++) {
749 /* Search fieldname */
750 for (j = 0; fieldname[j]; j++)
751 if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j]))
752 break;
753 if (fieldname[j]) {
754 /* skip to next line */
755 for (i += j; header[i] != '\n'; i++);
756 continue;
757 }
758
759 i += j;
760 if (header[i] == ':') {
761 /* Field found! */
762 while (header[++i] == ' ' || header[i] == '\t');
763 for (j = 0; header[i + j] != '\n'; j++);
764 while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
765 j--;
766 field = dStrndup(header + i, j);
767 dList_append(fields, field);
768 } else {
769 while (header[i] != '\n') i++;
770 }
771 }
772
773 if (dList_length(fields) == 0) {
774 dList_free(fields);
775 fields = NULL;
776 }
777 return fields;
778}
779
784static void Cache_parse_header(CacheEntry_t *entry)
785{
786 char *header = entry->Header->str;
787 bool_t server1point0 = !strncmp(entry->Header->str, "HTTP/1.0", 8);
788 char *Length, *Type, *location_str, *encoding, *connection, *hsts;
789#ifndef DISABLE_COOKIES
790 Dlist *Cookies;
791#endif
792 Dlist *warnings;
793 void *data;
794 int i;
795
796 _MSG("Cache_parse_header\n");
797
798 if (entry->Header->len > 12) {
799 if (header[9] == '1' && header[10] == '0' && header[11] == '0') {
800 /* 100: Continue. The "real" header has not come yet. */
801 MSG("An actual 100 Continue header!\n");
802 entry->Flags &= ~CA_GotHeader;
803 dStr_free(entry->Header, 1);
804 entry->Header = dStr_new("");
805 return;
806 }
807 if (header[9] == '3' && header[10] == '0' &&
808 (location_str = Cache_parse_field(header, "Location"))) {
809 /* 30x: URL redirection */
810 entry->Location = a_Url_new(location_str, URL_STR_(entry->Url));
811
812 if (!a_Domain_permit(entry->Url, entry->Location) ||
813 (URL_FLAGS(entry->Location) & (URL_Post + URL_Get) &&
814 dStrAsciiCasecmp(URL_SCHEME(entry->Location), "dpi") == 0 &&
815 dStrAsciiCasecmp(URL_SCHEME(entry->Url), "dpi") != 0)) {
816 /* Domain test, and forbid dpi GET and POST from non dpi-generated
817 * urls.
818 */
819 MSG("Redirection not followed from %s to %s\n",
820 URL_HOST(entry->Url), URL_STR(entry->Location));
821 } else {
822 entry->Flags |= CA_Redirect;
823 if (header[11] == '1')
824 entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
825 else if (header[11] == '2')
826 entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */
827 }
828 dFree(location_str);
829 } else if (strncmp(header + 9, "401", 3) == 0) {
830 entry->Auth =
831 Cache_parse_multiple_fields(header, "WWW-Authenticate");
832 } else if (strncmp(header + 9, "404", 3) == 0) {
833 entry->Flags |= CA_NotFound;
834 }
835 }
836
837 if ((warnings = Cache_parse_multiple_fields(header, "Warning"))) {
838 for (i = 0; (data = dList_nth_data(warnings, i)); ++i) {
839 MSG_HTTP("%s\n", (char *)data);
840 dFree(data);
841 }
842 dList_free(warnings);
843 }
844
845 if (server1point0)
846 entry->Flags &= ~CA_KeepAlive;
847
848 if ((connection = Cache_parse_field(header, "Connection"))) {
849 if (!dStrAsciiCasecmp(connection, "close"))
850 entry->Flags &= ~CA_KeepAlive;
851 else if (server1point0 && !dStrAsciiCasecmp(connection, "keep-alive"))
852 entry->Flags |= CA_KeepAlive;
853 dFree(connection);
854 }
855
857 !dStrAsciiCasecmp(URL_SCHEME(entry->Url), "https") &&
858 a_Url_host_type(URL_HOST(entry->Url)) == URL_HOST_NAME &&
859 (hsts = Cache_parse_field(header, "Strict-Transport-Security"))) {
860 a_Hsts_set(hsts, entry->Url);
861 dFree(hsts);
862 }
863
864 /*
865 * Get Transfer-Encoding and initialize decoder
866 */
867 encoding = Cache_parse_field(header, "Transfer-Encoding");
868 entry->TransferDecoder = a_Decode_transfer_init(encoding);
869
870
871 if ((Length = Cache_parse_field(header, "Content-Length")) != NULL) {
872 if (encoding) {
873 /*
874 * If Transfer-Encoding is present, Content-Length must be ignored.
875 * If the Transfer-Encoding is non-identity, it is an error.
876 */
877 if (dStrAsciiCasecmp(encoding, "identity"))
878 MSG_HTTP("Content-Length and non-identity Transfer-Encoding "
879 "headers both present.\n");
880 } else {
881 entry->Flags |= CA_GotLength;
882 entry->ExpectedSize = MAX(strtol(Length, NULL, 10), 0);
883 }
884 dFree(Length);
885 }
886
887 dFree(encoding); /* free Transfer-Encoding */
888
889#ifndef DISABLE_COOKIES
890 if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) {
891 CacheClient_t *client;
892
893 for (i = 0; (client = dList_nth_data(ClientQueue, i)); ++i) {
894 if (client->Url == entry->Url) {
895 DilloWeb *web = client->Web;
896
897 /* Only set cookies if any of:
898 * - User made request (requester == NULL)
899 * - Is a redirect for the root url (safe)
900 * - Same organization (first party cookie)
901 *
902 * Always block third party cookies from images or css files which
903 * are commonly used to track users (those that don't have the
904 * WEB_RootUrl flag and come from a different organization).
905 * https://en.wikipedia.org/wiki/Third-party_cookies
906 * https://support.mozilla.org/en-US/kb/third-party-trackers
907 */
908 int safe_redirect =
909 entry->Flags & CA_Redirect && web->flags & WEB_RootUrl;
910
911 if (!web->requester || safe_redirect ||
912 a_Url_same_organization(entry->Url, web->requester)) {
913 char *server_date = Cache_parse_field(header, "Date");
914 a_Cookies_set(Cookies, entry->Url, server_date);
915 dFree(server_date);
916 break;
917 }
918 }
919 }
920 for (i = 0; (data = dList_nth_data(Cookies, i)); ++i)
921 dFree(data);
922 dList_free(Cookies);
923 }
924#endif /* !DISABLE_COOKIES */
925
926 /*
927 * Get Content-Encoding and initialize decoder
928 */
929 encoding = Cache_parse_field(header, "Content-Encoding");
930 entry->ContentDecoder = a_Decode_content_init(encoding);
931 dFree(encoding);
932
933 if (entry->ExpectedSize > 0) {
934 if (entry->ExpectedSize > HUGE_FILESIZE) {
935 entry->Flags |= CA_HugeFile;
936 }
937 /* Avoid some reallocs. With MAX_INIT_BUF we avoid a SEGFAULT
938 * with huge files (e.g. iso files).
939 * Note: the buffer grows automatically. */
940 dStr_free(entry->Data, 1);
941 entry->Data = dStr_sized_new(MIN(entry->ExpectedSize, MAX_INIT_BUF));
942 }
943
944 /* Get Content-Type */
945 if ((Type = Cache_parse_field(header, "Content-Type"))) {
946 /* This HTTP Content-Type is not trusted. It's checked against real data
947 * in Cache_process_queue(); only then CA_GotContentType becomes true. */
948 a_Cache_set_content_type(entry->Url, Type, "http");
949 _MSG("TypeHdr {%s} {%s}\n", Type, URL_STR(entry->Url));
950 _MSG("TypeMeta {%s}\n", entry->TypeMeta);
951 dFree(Type);
952 }
953
954 entry->ContentDisposition = Cache_parse_field(header, "Content-Disposition");
955
956 Cache_ref_data(entry);
957}
958
963static int Cache_get_header(CacheEntry_t *entry,
964 const char *buf, size_t buf_size)
965{
966 size_t N, i;
967 Dstr *hdr = entry->Header;
968
969 /* Header finishes when N = 2 */
970 N = (hdr->len && hdr->str[hdr->len - 1] == '\n');
971 for (i = 0; i < buf_size && N < 2; ++i) {
972 if (buf[i] == '\r' || !buf[i])
973 continue;
974 if (N == 1 && (buf[i] == ' ' || buf[i] == '\t')) {
975 /* unfold multiple-line header */
976 _MSG("Multiple-line header!\n");
977 dStr_erase(hdr, hdr->len - 1, 1);
978 }
979 N = (buf[i] == '\n') ? N + 1 : 0;
980 dStr_append_c(hdr, buf[i]);
981 }
982
983 if (N == 2) {
984 /* Got whole header */
985 _MSG("Header [buf_size=%d]\n%s", i, hdr->str);
986 entry->Flags |= CA_GotHeader;
987 dStr_fit(hdr);
988
989 if (prefs.trace_http)
990 MSG("<<< receiving HTTP:\n%s\n", dStr_printable(hdr, 8192));
991
992 /* Return number of header bytes in 'buf' [1 based] */
993 return i;
994 }
995 return 0;
996}
997
998static void Cache_finish_msg(CacheEntry_t *entry)
999{
1000 if (!(entry->Flags & CA_InProgress)) {
1001 /* already finished */
1002 return;
1003 }
1004
1005 if ((entry->ExpectedSize || entry->TransferSize) &&
1006 entry->TypeHdr == NULL) {
1007 MSG_HTTP("Message with a body lacked Content-Type header.\n");
1008 }
1009 if ((entry->Flags & CA_GotLength) &&
1010 (entry->ExpectedSize != entry->TransferSize)) {
1011 MSG_HTTP("Content-Length (%d) does NOT match message body (%d) for %s\n",
1012 entry->ExpectedSize, entry->TransferSize, URL_STR_(entry->Url));
1013 }
1014 entry->Flags &= ~CA_InProgress;
1015 if (entry->TransferDecoder) {
1016 a_Decode_transfer_free(entry->TransferDecoder);
1017 entry->TransferDecoder = NULL;
1018 }
1019 if (entry->ContentDecoder) {
1020 a_Decode_free(entry->ContentDecoder);
1021 entry->ContentDecoder = NULL;
1022 }
1023 dStr_fit(entry->Data); /* fit buffer size! */
1024
1025 if ((entry = Cache_process_queue(entry))) {
1026 if (entry->Flags & CA_GotHeader) {
1027 Cache_unref_data(entry);
1028 }
1029 }
1030}
1031
1040bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
1041 const DilloUrl *Url)
1042{
1043 int offset, len;
1044 const char *str;
1045 Dstr *dstr1, *dstr2, *dstr3;
1046 bool_t done = FALSE;
1047 CacheEntry_t *entry = Cache_entry_search(Url);
1048
1049 /* Assert a valid entry (not aborted) */
1050 dReturn_val_if_fail (entry != NULL, FALSE);
1051
1052 _MSG("__a_Cache_process_dbuf__\n");
1053
1054 if (Op == IORead) {
1055 /*
1056 * Cache_get_header() will set CA_GotHeader if it has a full header, and
1057 * Cache_parse_header() will unset it if the header ends being
1058 * merely an informational response from the server (i.e., 100 Continue)
1059 */
1060 for (offset = 0; !(entry->Flags & CA_GotHeader) &&
1061 (len = Cache_get_header(entry, buf + offset, buf_size - offset));
1062 Cache_parse_header(entry) ) {
1063 offset += len;
1064 }
1065
1066 if (entry->Flags & CA_GotHeader) {
1067 str = buf + offset;
1068 len = buf_size - offset;
1069 entry->TransferSize += len;
1070 dstr1 = dstr2 = dstr3 = NULL;
1071
1072 /* Decode arrived data (<= 3 stages) */
1073 if (entry->TransferDecoder) {
1074 dstr1 = a_Decode_transfer_process(entry->TransferDecoder, str,len);
1075 done = a_Decode_transfer_finished(entry->TransferDecoder);
1076 str = dstr1->str;
1077 len = dstr1->len;
1078 }
1079 if (entry->ContentDecoder) {
1080 dstr2 = a_Decode_process(entry->ContentDecoder, str, len);
1081 str = dstr2->str;
1082 len = dstr2->len;
1083 }
1084 dStr_append_l(entry->Data, str, len);
1085 if (entry->CharsetDecoder && entry->UTF8Data) {
1086 dstr3 = a_Decode_process(entry->CharsetDecoder, str, len);
1087 dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len);
1088 }
1089 dStr_free(dstr1, 1);
1090 dStr_free(dstr2, 1);
1091 dStr_free(dstr3, 1);
1092
1093 if (entry->Data->len)
1094 entry->Flags &= ~CA_IsEmpty;
1095
1096 if ((entry->Flags & CA_GotLength) &&
1097 (entry->TransferSize >= entry->ExpectedSize)) {
1098 done = TRUE;
1099 }
1100 if (!(entry->Flags & CA_KeepAlive)) {
1101 /* Let IOClose finish it later */
1102 done = FALSE;
1103 }
1104
1105 entry = Cache_process_queue(entry);
1106
1107 if (entry && done)
1108 Cache_finish_msg(entry);
1109 }
1110 } else if (Op == IOClose) {
1111 Cache_finish_msg(entry);
1112 } else if (Op == IOAbort) {
1113 entry->Flags |= CA_Aborted;
1114 if (entry->Data->len) {
1115 MSG("Premature close for %s\n", URL_STR(entry->Url));
1116 Cache_finish_msg(entry);
1117 } else {
1118 int i;
1119 CacheClient_t *Client;
1120
1121 for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
1122 if (Client->Url == entry->Url) {
1123 DilloWeb *web = (DilloWeb *)Client->Web;
1124
1125 a_Bw_remove_client(web->bw, Client->Key);
1126 Cache_client_dequeue(Client);
1127 --i; /* Keep the index value in the next iteration */
1128 }
1129 }
1130 }
1131 }
1132 return done;
1133}
1134
1139static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
1140{
1141 DilloUrl *NewUrl;
1142
1143 _MSG(" Cache_redirect: redirect_level = %d\n", bw->redirect_level);
1144
1145 /* Don't allow redirection for SpamSafe/local URLs */
1146 if (URL_FLAGS(entry->Url) & URL_SpamSafe) {
1147 a_UIcmd_set_msg(bw, "WARNING: local URL with redirection. Aborting.");
1148 return 0;
1149 }
1150
1151 /* if there's a redirect loop, stop now */
1152 if (bw->redirect_level >= 5)
1153 entry->Flags |= CA_RedirectLoop;
1154
1155 if (entry->Flags & CA_RedirectLoop) {
1156 a_UIcmd_set_msg(bw, "ERROR: redirect loop for: %s", URL_STR_(entry->Url));
1157 bw->redirect_level = 0;
1158 return 0;
1159 }
1160
1161 if ((entry->Flags & CA_Redirect && entry->Location) &&
1162 (entry->Flags & CA_ForceRedirect || entry->Flags & CA_TempRedirect ||
1163 !entry->Data->len || entry->Data->len < 1024)) {
1164
1165 MSG("Redirect to: %s\n", URL_STR_(entry->Location));
1166 _MSG("%s", entry->Header->str);
1167
1168 if (Flags & WEB_RootUrl) {
1169 /* Redirection of the main page */
1170 NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url));
1171 if (entry->Flags & CA_TempRedirect)
1172 a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EQuery);
1173 a_Nav_push(bw, NewUrl, entry->Url);
1174 a_Url_free(NewUrl);
1175 } else {
1176 /* Sub entity redirection (most probably an image) */
1177 if (!entry->Data->len) {
1178 _MSG(">>>> Image redirection without entity-content <<<<\n");
1179 } else {
1180 _MSG(">>>> Image redirection with entity-content <<<<\n");
1181 }
1182 }
1183 }
1184 return 0;
1185}
1186
1187typedef struct {
1188 Dlist *auth;
1189 DilloUrl *url;
1190 BrowserWindow *bw;
1191} CacheAuthData_t;
1192
1196static void Cache_auth_callback(void *vdata)
1197{
1198 CacheAuthData_t *data = (CacheAuthData_t *)vdata;
1199 if (a_Auth_do_auth(data->auth, data->url))
1200 a_Nav_reload(data->bw);
1201 Cache_auth_free(data->auth);
1202 a_Url_free(data->url);
1203 dFree(data);
1204 Cache_auth_entry(NULL, NULL);
1206}
1207
1211static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw)
1212{
1213 static int busy = 0;
1214 CacheAuthData_t *data;
1215
1216 if (!entry) {
1217 busy = 0;
1218 } else if (busy) {
1219 MSG_WARN("Cache_auth_entry: caught busy!\n");
1220 } else if (entry->Auth) {
1221 busy = 1;
1222 data = dNew(CacheAuthData_t, 1);
1223 data->auth = entry->Auth;
1224 data->url = a_Url_dup(entry->Url);
1225 data->bw = bw;
1226 entry->Auth = NULL;
1228 }
1229}
1230
1236{
1237 if (!dStrAsciiCasecmp(URL_SCHEME(url), "http") ||
1238 !dStrAsciiCasecmp(URL_SCHEME(url), "https") ||
1239 !dStrAsciiCasecmp(URL_SCHEME(url), "ftp"))
1240 return 1;
1241 return 0;
1242}
1243
1249static void Cache_null_client(int Op, CacheClient_t *Client)
1250{
1251 DilloWeb *Web = Client->Web;
1252
1253 /* make the stop button insensitive when done */
1254 if (Op == CA_Close) {
1255 if (Web->flags & WEB_RootUrl) {
1256 /* Remove this client from our active list */
1257 a_Bw_close_client(Web->bw, Client->Key);
1258 }
1259 }
1260
1261 /* else ignore */
1262
1263 return;
1264}
1265
1266typedef struct {
1267 BrowserWindow *bw;
1268 DilloUrl *url;
1269 char* filename;
1270} Cache_savelink_t;
1271
1276static void Cache_savelink_cb(void *vdata)
1277{
1278 Cache_savelink_t *data = (Cache_savelink_t*) vdata;
1279
1280 a_UIcmd_save_link(data->bw, data->url, data->filename);
1281 a_Url_free(data->url);
1282 dFree(data);
1283}
1284
1288static void Cache_provide_redirection_blocked_page(CacheEntry_t *entry,
1289 CacheClient_t *client)
1290{
1291 DilloWeb *clientWeb = client->Web;
1292
1293 a_Web_dispatch_by_type("text/html", clientWeb, &client->Callback,
1294 &client->CbData);
1295 client->Buf = dStrconcat("<!doctype html><html><body>"
1296 "Dillo blocked a redirection from <a href=\"",
1297 URL_STR(entry->Url), "\">", URL_STR(entry->Url),
1298 "</a> to <a href=\"", URL_STR(entry->Location), "\">",
1299 URL_STR(entry->Location), "</a> based on your domainrc "
1300 "settings.</body></html>", NULL);
1301 client->BufSize = strlen(client->Buf);
1302 (client->Callback)(CA_Send, client);
1303 dFree(client->Buf);
1304}
1305
1318static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry)
1319{
1320 uint_t i;
1321 int st;
1322 const char *Type;
1323 Dstr *data;
1324 CacheClient_t *Client;
1325 DilloWeb *ClientWeb;
1326 BrowserWindow *Client_bw = NULL;
1327 static bool_t Busy = FALSE;
1328 bool_t AbortEntry = FALSE;
1329 bool_t OfferDownload = FALSE;
1330 bool_t TypeMismatch = FALSE;
1331 char *dtype = NULL;
1332 char *dfilename = NULL;
1333
1334 if (Busy)
1335 MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n");
1336 if (!(entry->Flags & CA_GotHeader))
1337 return entry;
1338 if (!(entry->Flags & CA_GotContentType)) {
1340 entry->Data->str, entry->Data->len, &Type);
1341 _MSG("Cache: detected Content-Type '%s'\n", Type);
1342 if (st == 0 || !(entry->Flags & CA_InProgress)) {
1343 if (a_Misc_content_type_check(entry->TypeHdr, Type) < 0) {
1344 MSG_HTTP("Content-Type '%s' doesn't match the real data.\n",
1345 entry->TypeHdr);
1346 TypeMismatch = TRUE;
1347 }
1348 entry->TypeDet = dStrdup(Type);
1349 entry->Flags |= CA_GotContentType;
1350 } else
1351 return entry; /* i.e., wait for more data */
1352 }
1353
1354 Busy = TRUE;
1355 for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) {
1356 if (Client->Url == entry->Url) {
1357 ClientWeb = Client->Web; /* It was a (void*) */
1358 Client_bw = ClientWeb->bw; /* 'bw' in a local var */
1359
1360 if (ClientWeb->flags & WEB_RootUrl) {
1361 /* Only parse Content-Disposition on root URLs */
1362 if (entry->ContentDisposition) {
1363 a_Misc_parse_content_disposition(entry->ContentDisposition,
1364 &dtype, &dfilename);
1365 }
1366 if (!(entry->Flags & CA_MsgErased)) {
1367 /* clear the "expecting for reply..." message */
1368 a_UIcmd_set_msg(Client_bw, "");
1369 entry->Flags |= CA_MsgErased;
1370 }
1371 if (TypeMismatch) {
1372 a_UIcmd_set_msg(Client_bw,"HTTP warning: Content-Type '%s' "
1373 "doesn't match the real data.", entry->TypeHdr);
1374 OfferDownload = TRUE;
1375 }
1376 if (entry->Flags & CA_Redirect) {
1377 if (!Client->Callback) {
1378 Client->Callback = Cache_null_client;
1379 Client_bw->redirect_level++;
1380 }
1381 } else {
1382 Client_bw->redirect_level = 0;
1383 }
1384 if (entry->Flags & CA_HugeFile) {
1385 a_UIcmd_set_msg(Client_bw, "Huge file! (%d MB)",
1386 entry->ExpectedSize / (1024*1024));
1387 AbortEntry = OfferDownload = TRUE;
1388 }
1389 } else {
1390 /* For non root URLs, ignore redirections and 404 answers */
1391 if (entry->Flags & CA_Redirect || entry->Flags & CA_NotFound)
1392 Client->Callback = Cache_null_client;
1393 }
1394
1395 /* Set the client function */
1396 if (!Client->Callback) {
1397 Client->Callback = Cache_null_client;
1398
1399 if (entry->Location && !(entry->Flags & CA_Redirect)) {
1400 /* Not following redirection, so don't display page body. */
1401 } else {
1402 if (TypeMismatch) {
1403 AbortEntry = TRUE;
1404 } else {
1405 const char *curr_type = Cache_current_content_type(entry);
1406 st = a_Web_dispatch_by_type(curr_type, ClientWeb,
1407 &Client->Callback,
1408 &Client->CbData);
1409 if (st == -1) {
1410 /* MIME type is not viewable */
1411 if (ClientWeb->flags & WEB_RootUrl) {
1412 MSG("Content-Type '%s' not viewable.\n", curr_type);
1413 /* prepare a download offer... */
1414 AbortEntry = OfferDownload = TRUE;
1415 } else {
1416 /* TODO: Resource Type not handled.
1417 * Not aborted to avoid multiple connections on the
1418 * same resource. A better idea is to abort the
1419 * connection and to keep a failed-resource flag in
1420 * the cache entry. */
1421 }
1422 } else if (dtype && dStrnAsciiCasecmp(dtype, "inline", 6) != 0) {
1423 AbortEntry = OfferDownload = TRUE;
1424 }
1425 }
1426 if (AbortEntry) {
1427 if (ClientWeb->flags & WEB_RootUrl)
1428 a_Nav_cancel_expect_if_eq(Client_bw, Client->Url);
1429 a_Bw_remove_client(Client_bw, Client->Key);
1430 Cache_client_dequeue(Client);
1431 --i; /* Keep the index value in the next iteration */
1432 continue;
1433 }
1434 }
1435 }
1436
1437 /* Send data to our client */
1438 if (ClientWeb->flags & WEB_Download) {
1439 /* for download, always provide original data, not translated */
1440 data = entry->Data;
1441 } else {
1442 data = Cache_data(entry);
1443 }
1444 if ((Client->BufSize = data->len) > 0) {
1445 Client->Buf = data->str;
1446 (Client->Callback)(CA_Send, Client);
1447 if (ClientWeb->flags & WEB_RootUrl) {
1448 /* show size of page received */
1449 a_UIcmd_set_page_prog(Client_bw, entry->Data->len, 1);
1450 }
1451 }
1452
1453 /* Remove client when done */
1454 if (!(entry->Flags & CA_InProgress)) {
1455 /* Copy flags to a local var */
1456 int flags = ClientWeb->flags;
1457
1458 if (ClientWeb->flags & WEB_RootUrl && entry->Location &&
1459 !(entry->Flags & CA_Redirect)) {
1461 }
1462 /* We finished sending data, let the client know */
1463 (Client->Callback)(CA_Close, Client);
1464 if (ClientWeb->flags & WEB_RootUrl) {
1465 if (entry->Flags & CA_Aborted) {
1466 a_UIcmd_set_msg(Client_bw, "ERROR: Connection closed early, "
1467 "read not complete.");
1468 }
1469 a_UIcmd_set_page_prog(Client_bw, 0, 0);
1470 }
1471 Cache_client_dequeue(Client);
1472 --i; /* Keep the index value in the next iteration */
1473
1474 /* we assert just one redirect call */
1475 if (entry->Flags & CA_Redirect)
1476 Cache_redirect(entry, flags, Client_bw);
1477 }
1478 }
1479 } /* for */
1480
1481 if (AbortEntry) {
1482 /* Abort the entry, remove it from cache, and maybe offer download. */
1483 DilloUrl *url = a_Url_dup(entry->Url);
1485 entry = NULL;
1486 if (OfferDownload) {
1487 /* Remove entry when 'conn' is already done */
1488 Cache_entry_remove(NULL, url);
1489 if (a_Cache_download_enabled(url)) {
1490 Cache_savelink_t *data = dNew(Cache_savelink_t, 1);
1491 data->bw = Client_bw;
1492 data->url = a_Url_dup(url);
1493 data->filename = dStrdup(dfilename);
1494 a_Timeout_add(0.0, Cache_savelink_cb, data);
1495 }
1496 }
1497 a_Url_free(url);
1498 } else if (entry->Auth && !(entry->Flags & CA_InProgress)) {
1499 Cache_auth_entry(entry, Client_bw);
1500 }
1501
1502 dFree(dtype); dFree(dfilename);
1503
1504 /* Trigger cleanup when there are no cache clients */
1505 if (dList_length(ClientQueue) == 0) {
1507 }
1508
1509 Busy = FALSE;
1510 _MSG("QueueSize ====> %d\n", dList_length(ClientQueue));
1511 return entry;
1512}
1513
1518{
1519 CacheEntry_t *entry;
1520 (void) ptr; /* Unused */
1521
1522 while ((entry = (CacheEntry_t *)dList_nth_data(DelayedQueue, 0))) {
1523 Cache_ref_data(entry);
1524 if ((entry = Cache_process_queue(entry))) {
1525 Cache_unref_data(entry);
1526 dList_remove(DelayedQueue, entry);
1527 }
1528 }
1531}
1532
1536static void Cache_delayed_process_queue(CacheEntry_t *entry)
1537{
1538 /* there's no need to repeat entries in the queue */
1539 if (!dList_find(DelayedQueue, entry))
1540 dList_append(DelayedQueue, entry);
1541
1542 if (DelayedQueueIdleId == 0) {
1543 _MSG(" Setting timeout callback\n");
1546 }
1547}
1548
1555{
1556 int i, n = 0;
1557 CacheClient_t *Client, *iClient;
1558
1559 if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
1561 for (i = 0; (iClient = dList_nth_data(ClientQueue, i)); ++i) {
1562 if (iClient->Url == Client->Url) {
1563 ++n;
1564 }
1565 }
1566 }
1567 return (n == 1) ? Client : NULL;
1568}
1569
1575{
1576 CacheClient_t *Client;
1577 CacheEntry_t *entry;
1578 DICacheEntry *DicEntry;
1579
1580 /* The client can be in both queues at the same time */
1581 if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
1583 /* Dicache */
1584 if ((DicEntry = a_Dicache_get_entry(Client->Url, Client->Version)))
1585 a_Dicache_unref(Client->Url, Client->Version);
1586
1587 /* DelayedQueue */
1588 if ((entry = Cache_entry_search(Client->Url)))
1589 dList_remove(DelayedQueue, entry);
1590
1591 /* Main queue */
1592 Cache_client_dequeue(Client);
1593
1594 } else {
1595 _MSG("WARNING: Cache_stop_client, nonexistent client\n");
1596 }
1597}
1598
1599
1604{
1605 CacheClient_t *Client;
1606 void *data;
1607
1608 /* free the client queue */
1609 while ((Client = dList_nth_data(ClientQueue, 0)))
1610 Cache_client_dequeue(Client);
1611
1612 /* Remove every cache entry */
1613 while ((data = dList_nth_data(CachedURLs, 0))) {
1615 Cache_entry_free(data, 1);
1616 }
1617 /* Remove the cache list */
1619}
#define IORead
Definition IO.h:11
#define IOAbort
Definition IO.h:14
#define IOClose
Definition IO.h:13
const char *const AboutSplash
HTML text for startup screen.
Definition about.c:18
int a_Auth_do_auth(Dlist *challenges, const DilloUrl *url)
Given authentication challenge(s), prepare authorization.
Definition auth.c:670
#define _MSG(...)
Definition bookmarks.c:44
static char * Header
Definition bookmarks.c:85
#define MSG(...)
Definition bookmarks.c:45
int a_Bw_remove_client(BrowserWindow *bw, int ClientKey)
Remove the cache-client from the bw's list (client can be a image or a html page) Return: 0 if found,...
Definition bw.c:149
void a_Bw_close_client(BrowserWindow *bw, int ClientKey)
Close a cache-client upon successful retrieval.
Definition bw.c:167
int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
Get the pointer to the URL document, and its size, from the cache entry.
Definition cache.c:667
CacheClient_t * a_Cache_client_get_if_unique(int Key)
Last Client for this entry?
Definition cache.c:1554
static void Cache_savelink_cb(void *vdata)
Save link from behind a timeout so that Cache_process_queue() can get on with its work.
Definition cache.c:1276
static void Cache_entry_remove(CacheEntry_t *entry, DilloUrl *url)
Remove an entry, from the cache.
Definition cache.c:361
static void Cache_delayed_process_queue(CacheEntry_t *entry)
Set a call to Cache_process_queue from the main cycle.
Definition cache.c:1536
static void Cache_finish_msg(CacheEntry_t *entry)
Definition cache.c:998
static Dlist * ClientQueue
A list for cache clients.
Definition cache.c:82
static Dstr * Cache_stats(void)
Definition cache.c:400
uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url)
Get cache entry status (following redirections).
Definition cache.c:530
static void Cache_parse_header(CacheEntry_t *entry)
Scan, allocate, and set things according to header info.
Definition cache.c:784
static int Cache_entry_by_url_cmp(const void *v1, const void *v2)
Determine if two cache entries are equal, using a URL as key.
Definition cache.c:112
static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw)
Set a timeout function to ask for user/password.
Definition cache.c:1211
static CacheEntry_t * Cache_entry_search_with_redirect(const DilloUrl *Url)
Given a URL, find its cache entry, following redirections.
Definition cache.c:234
static Dlist * DelayedQueue
A list for delayed clients (it holds weak pointers to cache entries, which are used to make deferred ...
Definition cache.c:86
static uint_t DelayedQueueIdleId
Definition cache.c:87
static Dlist * CachedURLs
A sorted list for cached data.
Definition cache.c:78
static int Cache_get_header(CacheEntry_t *entry, const char *buf, size_t buf_size)
Consume bytes until the whole header is got (up to a "\r\n\r\n" sequence) (Also unfold multi-line fie...
Definition cache.c:963
static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
Set safe values for a new cache entry.
Definition cache.c:199
static const char * Cache_current_content_type(CacheEntry_t *entry)
Get current content type.
Definition cache.c:578
const char * a_Cache_set_content_type(const DilloUrl *url, const char *ctype, const char *from)
Change Content-Type for cache entry found by url.
Definition cache.c:607
static void Cache_client_dequeue(CacheClient_t *Client)
Remove a client from the queue.
Definition cache.c:184
static void Cache_auth_free(Dlist *auth)
Free Authentication fields.
Definition cache.c:321
void a_Cache_unref_buf(const DilloUrl *Url)
Unreference the data buffer when no longer using it.
Definition cache.c:695
static void Cache_null_client(int Op, CacheClient_t *Client)
Don't process data any further, but let the cache fill the entry.
Definition cache.c:1249
static char * Cache_parse_field(const char *header, const char *fieldname)
Extract a single field from the header, allocating and storing the value in 'field'.
Definition cache.c:707
static void Cache_auth_callback(void *vdata)
Ask for user/password and reload the page.
Definition cache.c:1196
Dstr * a_Cache_get_header(const DilloUrl *Url)
Definition cache.c:683
const char * a_Cache_get_content_type(const DilloUrl *url)
Get current Content-Type for cache entry found by URL.
Definition cache.c:587
static void Cache_ref_data(CacheEntry_t *entry)
Reference the cache data.
Definition cache.c:539
static CacheEntry_t * Cache_process_queue(CacheEntry_t *entry)
Update cache clients for a single cache-entry Tasks:
Definition cache.c:1318
static void Cache_provide_redirection_blocked_page(CacheEntry_t *entry, CacheClient_t *client)
Let the client know that we're not following a redirection.
Definition cache.c:1288
void a_Cache_entry_remove_by_url(DilloUrl *url)
Wrapper for capi.
Definition cache.c:393
static CacheEntry_t * Cache_entry_search(const DilloUrl *Url)
Get the data structure for a cached URL (using 'Url' as the search key) If 'Url' isn't cached,...
Definition cache.c:226
static Dstr * Cache_data(CacheEntry_t *entry)
Get pointer to entry's data.
Definition cache.c:597
static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web, CA_Callback_t Callback, void *CbData)
Add a client to ClientQueue.
Definition cache.c:147
bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, const DilloUrl *Url)
Receive new data, update the reception buffer (for next read), update the cache, and service the clie...
Definition cache.c:1040
int a_Cache_download_enabled(const DilloUrl *url)
Check whether a URL scheme is downloadable.
Definition cache.c:1235
static void Cache_entry_free(CacheEntry_t *entry, int deep)
Free the components of a CacheEntry_t struct.
Definition cache.c:333
void a_Cache_entry_inject(const DilloUrl *Url, const char *buf, size_t len, int flags)
Inject full page content directly into the cache.
Definition cache.c:293
static int Cache_internal_url(CacheEntry_t *entry)
Definition cache.c:441
static void Cache_unref_data(CacheEntry_t *entry)
Unreference the cache data.
Definition cache.c:557
#define HUGE_FILESIZE
Maximum filesize for a URL, before offering a download.
Definition cache.c:45
static int Cache_entry_cmp(const void *v1, const void *v2)
Determine if two cache entries are equal (used by CachedURLs)
Definition cache.c:102
void a_Cache_init(void)
Initialize cache data.
Definition cache.c:123
static void Cache_delayed_process_queue_callback(void *ptr)
Callback function for Cache_delayed_process_queue.
Definition cache.c:1517
static CacheEntry_t * Cache_entry_add(const DilloUrl *Url)
Allocate and set a new entry in the cache list.
Definition cache.c:258
#define MAX_INIT_BUF
Maximum initial size for the automatically-growing data buffer.
Definition cache.c:43
static int Cache_client_by_key_cmp(const void *client, const void *key)
Compare function for searching a Client by its key.
Definition cache.c:176
static int Cache_bufsize(CacheEntry_t *e)
Compute the actual size occupied by a cache entry.
Definition cache.c:277
static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
Process redirections (HTTP 30x answers) (This is a work in progress –not finished yet)
Definition cache.c:1139
int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
Try finding the url in the cache.
Definition cache.c:472
static Dlist * Cache_parse_multiple_fields(const char *header, const char *fieldname)
Extract multiple fields from the header.
Definition cache.c:741
void a_Cache_freeall(void)
Memory deallocator (only called at exit time)
Definition cache.c:1603
uint_t a_Cache_get_flags(const DilloUrl *url)
Get cache entry status.
Definition cache.c:521
void a_Cache_stop_client(int Key)
Remove a client from the client queue TODO: notify the dicache and upper layers.
Definition cache.c:1574
#define CA_Send
Definition cache.h:27
#define CA_KeepAlive
Definition cache.h:48
#define CA_NotFound
Definition cache.h:41
#define CA_InternalUrl
Definition cache.h:45
void(* CA_Callback_t)(int Op, CacheClient_t *Client)
Callback type for cache clients.
Definition cache.h:55
#define CA_Redirect
Definition cache.h:38
#define CA_ForceRedirect
Definition cache.h:39
#define CA_RedirectLoop
Definition cache.h:44
#define CA_IsEmpty
Definition cache.h:47
#define CA_GotContentType
Definition cache.h:35
#define CA_Aborted
Definition cache.h:42
#define CA_Close
Definition cache.h:28
#define CA_HugeFile
Definition cache.h:46
#define CA_MsgErased
Definition cache.h:43
#define CA_TempRedirect
Definition cache.h:40
#define CA_InProgress
Definition cache.h:37
#define CA_GotHeader
Definition cache.h:34
#define CA_GotLength
Definition cache.h:36
void a_Capi_conn_abort_by_url(const DilloUrl *url)
Abort the connection for a given url, using its CCC.
Definition capi.c:200
unsigned int uint_t
Definition d_size.h:20
unsigned char bool_t
Definition d_size.h:21
Decode * a_Decode_charset_init(const char *format)
Initialize decoder to translate from any character set known to iconv() to UTF-8.
Definition decode.c:453
Dstr * a_Decode_transfer_process(DecodeTransfer *dc, const char *instr, int inlen)
Decode 'Transfer-Encoding: chunked' data.
Definition decode.c:33
void a_Decode_transfer_free(DecodeTransfer *dc)
Definition decode.c:97
Decode * a_Decode_content_init(const char *format)
Initialize content decoder.
Definition decode.c:410
DecodeTransfer * a_Decode_transfer_init(const char *format)
Initialize transfer decoder.
Definition decode.c:374
Dstr * a_Decode_process(Decode *dc, const char *instr, int inlen)
Decode data.
Definition decode.c:480
void a_Decode_free(Decode *dc)
Definition decode.c:488
bool_t a_Decode_transfer_finished(DecodeTransfer *dc)
Definition decode.c:92
DICacheEntry * a_Dicache_get_entry(const DilloUrl *Url, int version)
Search a particular version of a URL in the Dicache.
Definition dicache.c:156
void a_Dicache_cleanup(void)
Free the imgbuf (RGB data) of unused entries.
Definition dicache.c:574
void a_Dicache_invalidate_entry(const DilloUrl *Url)
Invalidate this entry.
Definition dicache.c:246
void a_Dicache_unref(const DilloUrl *Url, int version)
Unrefs the counter of a dicache entry (it counts cache clients).
Definition dicache.c:214
Dstr * a_Dicache_stats(void)
Definition dicache.c:617
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
Definition dlib.c:101
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
Definition dlib.c:796
void dFree(void *mem)
Definition dlib.c:67
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:202
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
Definition dlib.c:463
void dStr_append(Dstr *ds, const char *s)
Append a C string to a Dstr.
Definition dlib.c:315
char * dStrdup(const char *s)
Definition dlib.c:76
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:575
Dstr * dStr_sized_new(int sz)
Create a new string with a given size.
Definition dlib.c:253
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
Definition dlib.c:214
void dStr_erase(Dstr *ds, int pos_0, int len)
Erase a substring.
Definition dlib.c:387
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:640
void dStr_shorten(Dstr *dst, const char *src, int n)
Shorten string so it fits in n characters.
Definition dlib.c:550
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
Definition dlib.c:689
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:650
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:336
void dStr_append_l(Dstr *ds, const char *s, int l)
Append a C string to a Dstr (providing length).
Definition dlib.c:307
void dStr_append_c(Dstr *ds, int c)
Append one character.
Definition dlib.c:348
char * dStrndup(const char *s, size_t sz)
Definition dlib.c:87
Dstr * dStr_new(const char *s)
Create a new string.
Definition dlib.c:324
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
Definition dlib.c:624
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
Definition dlib.c:823
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:591
void * dList_find_custom(Dlist *lp, const void *data, dCompareFunc func)
Search a data item using a custom function.
Definition dlib.c:731
void dStr_fit(Dstr *ds)
Return memory if there's too much allocated.
Definition dlib.c:268
const char * dStr_printable(Dstr *in, int maxlen)
Return a printable representation of the provided Dstr, limited to a length of roughly maxlen.
Definition dlib.c:512
void dStr_truncate(Dstr *ds, int len)
Truncate a Dstr to be 'len' bytes long.
Definition dlib.c:367
void dList_remove(Dlist *lp, const void *data)
Definition dlib.c:668
void * dList_find(Dlist *lp, const void *data)
Return the found data item, or NULL if not present.
Definition dlib.c:699
#define MIN(a, b)
Definition dlib.h:43
#define dReturn_val_if_fail(expr, val)
Definition dlib.h:105
#define D_ASCII_TOLOWER(c)
Definition dlib.h:60
#define VOIDP2INT(p)
Definition dlib.h:72
#define TRUE
Definition dlib.h:36
#define FALSE
Definition dlib.h:32
#define INT2VOIDP(i)
Definition dlib.h:73
#define MAX(a, b)
Definition dlib.h:40
#define dNew(type, count)
Definition dlib.h:78
bool_t a_Domain_permit(const DilloUrl *source, const DilloUrl *dest)
Is the resource at 'source' permitted to request the resource at 'dest'?
Definition domain.c:116
#define MSG_ERR(...)
Definition dpid_common.h:23
void a_Hsts_set(const char *header, const DilloUrl *url)
The response for this url had an HSTS header, so let's take action.
Definition hsts.c:200
#define _MSG_WARN(...)
Definition msg.h:15
#define MSG_WARN(...)
Definition msg.h:26
int a_Misc_content_type_check(const char *EntryType, const char *DetectedType)
Check the server-supplied 'Content-Type' against our detected type.
Definition misc.c:322
int a_Misc_content_type_cmp(const char *ct1, const char *ct2)
Compare two Content-Type strings.
Definition misc.c:275
int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
Detects 'Content-Type' from a data stream sample.
Definition misc.c:137
void a_Misc_parse_content_type(const char *type, char **major, char **minor, char **charset)
Parse Content-Type string, e.g., "text/html; charset=utf-8".
Definition misc.c:211
static void a_Misc_parse_content_disposition(const char *disposition, char **type, char **filename)
Parse Content-Disposition string, e.g., "attachment; filename="file name.jpg"".
Definition misc.h:28
void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url)
Definition nav.c:260
void a_Nav_reload(BrowserWindow *bw)
Definition nav.c:543
void a_Nav_push(BrowserWindow *bw, const DilloUrl *url, const DilloUrl *requester)
Definition nav.c:343
DilloPrefs prefs
Global Data.
Definition prefs.c:33
void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url, const char *date)
Set the value corresponding to the cookie string.
Definition cookies.c:142
#define MSG_HTTP(...)
Definition msg.h:25
Contains the specific data for a single window.
Definition bw.h:27
int redirect_level
Counter for the number of hops on a redirection.
Definition bw.h:63
Data structure for cache clients.
Definition cache.h:60
CA_Callback_t Callback
Client function.
Definition cache.h:66
const DilloUrl * Url
Pointer to a cache entry Url.
Definition cache.h:62
int Key
Primary Key for this client.
Definition cache.h:61
int Version
Dicache version of this Url (0 if not used)
Definition cache.h:63
void * CbData
Client function data.
Definition cache.h:67
uint_t BufSize
Valid size of cache-data.
Definition cache.h:65
void * Buf
Pointer to cache-data.
Definition cache.h:64
void * Web
Pointer to the Web structure of our client.
Definition cache.h:68
bool_t trace_http
Definition prefs.h:124
bool_t http_strict_transport_security
Definition prefs.h:106
Definition url.h:88
Definition dlib.h:161
Definition dlib.h:131
Dstr_char_t * str
Definition dlib.h:134
int len
Definition dlib.h:133
int flags
Additional info.
Definition web.hh:29
DilloUrl * url
Requested URL.
Definition web.hh:25
DilloUrl * requester
URL that caused this request, or < NULL if user-initiated.
Definition web.hh:26
BrowserWindow * bw
The requesting browser window [reference].
Definition web.hh:28
void a_Timeout_add(float t, TimeoutCb_t cb, void *cbdata)
Hook a one-time timeout function 'cb' after 't' seconds with 'cbdata" as its data.
Definition timeout.cc:26
void a_Timeout_remove()
Stop running a timeout function.
Definition timeout.cc:42
void a_UIcmd_set_msg(BrowserWindow *bw, const char *format,...)
Definition uicmd.cc:1636
void a_UIcmd_set_page_prog(BrowserWindow *bw, size_t nbytes, int cmd)
Definition uicmd.cc:1572
void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url, char *filename)
Definition uicmd.cc:1290
void a_Url_set_flags(DilloUrl *u, int flags)
Set DilloUrl flags.
Definition url.c:526
int a_Url_cmp(const DilloUrl *A, const DilloUrl *B)
Compare two Url's to check if they're the same, or which one is bigger.
Definition url.c:505
void a_Url_free(DilloUrl *url)
Free a DilloUrl.
Definition url.c:207
bool_t a_Url_same_organization(const DilloUrl *u1, const DilloUrl *u2)
Definition url.c:797
DilloUrl * a_Url_new(const char *url_str, const char *base_url)
Transform (and resolve) an URL string into the respective DilloURL.
Definition url.c:370
int a_Url_host_type(const char *host)
What type of host is this?
Definition url.c:682
DilloUrl * a_Url_dup(const DilloUrl *ori)
Duplicate a Url structure.
Definition url.c:476
#define URL_HOST_NAME
Definition url.h:24
#define URL_PATH(u)
Definition url.h:72
#define URL_E2EQuery
Definition url.h:35
#define URL_SpamSafe
Definition url.h:40
#define URL_FLAGS(u)
Definition url.h:79
#define URL_STR(u)
Definition url.h:76
#define URL_STR_(u)
Definition url.h:55
#define URL_SCHEME(u)
Definition url.h:70
#define URL_Post
Definition url.h:33
#define URL_Get
Definition url.h:32
#define URL_HOST(u)
Definition url.h:75
int a_Web_dispatch_by_type(const char *Type, DilloWeb *Web, CA_Callback_t *Call, void **Data)
Given the MIME content type, and a fd to read it from, this function connects the proper MIME viewer ...
Definition web.cc:50
void a_Web_free(DilloWeb *web)
Deallocate a DilloWeb structure.
Definition web.cc:152
#define WEB_RootUrl
Definition web.hh:16
#define WEB_Download
Definition web.hh:19