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