Declare element types of lists.
[elinks.git] / src / cookies / cookies.c
blobde9f4d429ee6c196f6a61df4f187ff5b245201ac
1 /* Internal cookies implementation */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
12 #ifdef HAVE_TIME_H
13 #include <time.h>
14 #endif
16 #include "elinks.h"
18 #if 0
19 #define DEBUG_COOKIES
20 #endif
22 #include "bfu/dialog.h"
23 #include "cookies/cookies.h"
24 #include "cookies/dialogs.h"
25 #include "cookies/parser.h"
26 #include "config/home.h"
27 #include "config/kbdbind.h"
28 #include "config/options.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/module.h"
31 #include "main/object.h"
32 #include "main/select.h"
33 #include "protocol/date.h"
34 #include "protocol/header.h"
35 #include "protocol/protocol.h"
36 #include "protocol/uri.h"
37 #include "session/session.h"
38 #include "terminal/terminal.h"
39 #include "util/conv.h"
40 #ifdef DEBUG_COOKIES
41 #include "util/error.h"
42 #endif
43 #include "util/file.h"
44 #include "util/memory.h"
45 #include "util/secsave.h"
46 #include "util/string.h"
47 #include "util/time.h"
49 #define COOKIES_FILENAME "cookies"
52 static int cookies_nosave = 0;
54 static INIT_LIST_OF(struct cookie, cookies);
56 struct c_domain {
57 LIST_HEAD(struct c_domain);
59 unsigned char domain[1]; /* Must be at end of struct. */
62 /* List of domains for which there may be cookies. This supposedly
63 * speeds up @send_cookies for other domains. Each element is a
64 * struct c_domain. No other data structures have pointers to these
65 * objects. Currently the domains remain in the list until
66 * @done_cookies clears the whole list. */
67 static INIT_LIST_OF(struct c_domain, c_domains);
69 /* List of servers for which there are cookies. */
70 static INIT_LIST_OF(struct cookie_server, cookie_servers);
72 /* Only @set_cookies_dirty may make this nonzero. */
73 static int cookies_dirty = 0;
75 enum cookies_option {
76 COOKIES_TREE,
78 COOKIES_ACCEPT_POLICY,
79 COOKIES_MAX_AGE,
80 COOKIES_PARANOID_SECURITY,
81 COOKIES_SAVE,
82 COOKIES_RESAVE,
84 COOKIES_OPTIONS,
87 static struct option_info cookies_options[] = {
88 INIT_OPT_TREE("", N_("Cookies"),
89 "cookies", 0,
90 N_("Cookies options.")),
92 INIT_OPT_INT("cookies", N_("Accept policy"),
93 "accept_policy", 0,
94 COOKIES_ACCEPT_NONE, COOKIES_ACCEPT_ALL, COOKIES_ACCEPT_ALL,
95 N_("Cookies accepting policy:\n"
96 "0 is accept no cookies\n"
97 "1 is ask for confirmation before accepting cookie\n"
98 "2 is accept all cookies")),
100 INIT_OPT_INT("cookies", N_("Maximum age"),
101 "max_age", 0, -1, 10000, -1,
102 N_("Cookie maximum age (in days):\n"
103 "-1 is use cookie's expiration date if any\n"
104 "0 is force expiration at the end of session, ignoring cookie's\n"
105 " expiration date\n"
106 "1+ is use cookie's expiration date, but limit age to the given\n"
107 " number of days")),
109 INIT_OPT_BOOL("cookies", N_("Paranoid security"),
110 "paranoid_security", 0, 0,
111 N_("When enabled, we'll require three dots in cookies domain for all\n"
112 "non-international domains (instead of just two dots). Some countries\n"
113 "have generic second level domains (eg. .com.pl, .co.uk) and allowing\n"
114 "sites to set cookies for these generic domains could potentially be\n"
115 "very bad. Note, it is off by default as it breaks a lot of sites.")),
117 INIT_OPT_BOOL("cookies", N_("Saving"),
118 "save", 0, 1,
119 N_("Whether cookies should be loaded from and save to disk.")),
121 INIT_OPT_BOOL("cookies", N_("Resaving"),
122 "resave", 0, 1,
123 N_("Save cookies after each change in cookies list? No effect when\n"
124 "cookie saving (cookies.save) is off.")),
126 NULL_OPTION_INFO,
129 #define get_opt_cookies(which) cookies_options[(which)].option.value
130 #define get_cookies_accept_policy() get_opt_cookies(COOKIES_ACCEPT_POLICY).number
131 #define get_cookies_max_age() get_opt_cookies(COOKIES_MAX_AGE).number
132 #define get_cookies_paranoid_security() get_opt_cookies(COOKIES_PARANOID_SECURITY).number
133 #define get_cookies_save() get_opt_cookies(COOKIES_SAVE).number
134 #define get_cookies_resave() get_opt_cookies(COOKIES_RESAVE).number
136 struct cookie_server *
137 get_cookie_server(unsigned char *host, int hostlen)
139 struct cookie_server *sort_spot = NULL;
140 struct cookie_server *cs;
142 foreach (cs, cookie_servers) {
143 /* XXX: We must count with cases like "x.co" vs "x.co.uk"
144 * below! */
145 int cslen = strlen(cs->host);
146 int cmp = strncasecmp(cs->host, host, hostlen);
148 if (!sort_spot && (cmp > 0 || (cmp == 0 && cslen > hostlen))) {
149 /* This is the first @cs with name greater than @host,
150 * our dream sort spot! */
151 sort_spot = cs->prev;
154 if (cmp || cslen != hostlen)
155 continue;
157 object_lock(cs);
158 return cs;
161 cs = mem_calloc(1, sizeof(*cs) + hostlen);
162 if (!cs) return NULL;
164 memcpy(cs->host, host, hostlen);
165 object_nolock(cs, "cookie_server");
167 cs->box_item = add_listbox_folder(&cookie_browser, NULL, cs);
169 object_lock(cs);
171 if (!sort_spot) {
172 /* No sort spot found, therefore this sorts at the end. */
173 add_to_list_end(cookie_servers, cs);
174 del_from_list(cs->box_item);
175 add_to_list_end(cookie_browser.root.child, cs->box_item);
176 } else {
177 /* Sort spot found, sort after it. */
178 add_at_pos(sort_spot, cs);
179 if (sort_spot != (struct cookie_server *) &cookie_servers) {
180 del_from_list(cs->box_item);
181 add_at_pos(sort_spot->box_item, cs->box_item);
182 } /* else we are already at the top anyway. */
185 return cs;
188 static void
189 done_cookie_server(struct cookie_server *cs)
191 object_unlock(cs);
192 if (is_object_used(cs)) return;
194 if (cs->box_item) done_listbox_item(&cookie_browser, cs->box_item);
195 del_from_list(cs);
196 mem_free(cs);
199 void
200 done_cookie(struct cookie *c)
202 if (c->box_item) done_listbox_item(&cookie_browser, c->box_item);
203 if (c->server) done_cookie_server(c->server);
204 mem_free_if(c->name);
205 mem_free_if(c->value);
206 mem_free_if(c->path);
207 mem_free_if(c->domain);
208 mem_free(c);
211 /* The cookie @c can be either in @cookies or in @cookie_queries.
212 * Because changes in @cookie_queries should not affect the cookie
213 * file, this function does not set @cookies_dirty. Instead, the
214 * caller must do that if appropriate. */
215 void
216 delete_cookie(struct cookie *c)
218 del_from_list(c);
219 done_cookie(c);
223 /* Check whether cookie's domain matches server.
224 * It returns 1 if ok, 0 else. */
225 static int
226 is_domain_security_ok(unsigned char *domain, unsigned char *server, int server_len)
228 int i;
229 int domain_len;
230 int need_dots;
232 if (domain[0] == '.') domain++;
233 domain_len = strlen(domain);
235 /* Match domain and server.. */
237 /* XXX: Hmm, can't we use strlcasecmp() here? --pasky */
239 if (domain_len > server_len) return 0;
241 /* Ensure that the domain is atleast a substring of the server before
242 * continuing. */
243 if (strncasecmp(domain, server + server_len - domain_len, domain_len))
244 return 0;
246 /* Allow domains which are same as servers. --<rono@sentuny.com.au> */
247 /* Mozilla does it as well ;))) and I can't figure out any security
248 * risk. --pasky */
249 if (server_len == domain_len)
250 return 1;
252 /* Check whether the server is an IP address, and require an exact host
253 * match for the cookie, so any chance of IP address funkiness is
254 * eliminated (e.g. the alias 127.1 domain-matching 99.54.127.1). Idea
255 * from mozilla. (bug 562) */
256 if (is_ip_address(server, server_len))
257 return 0;
259 /* Also test if domain is secure en ugh.. */
261 need_dots = 1;
263 if (get_cookies_paranoid_security()) {
264 /* This is somehow controversial attempt (by the way violating
265 * RFC) to increase cookies security in national domains, done
266 * by Mikulas. As it breaks a lot of sites, I decided to make
267 * this optional and off by default. I also don't think this
268 * improves security considerably, as it's SITE'S fault and
269 * also no other browser probably does it. --pasky */
270 /* Mikulas' comment: Some countries have generic 2-nd level
271 * domains (like .com.pl, .co.uk ...) and it would be very bad
272 * if someone set cookies for these generic domains. Imagine
273 * for example that server http://brutalporn.com.pl sets cookie
274 * Set-Cookie: user_is=perverse_pig; domain=.com.pl -- then
275 * this cookie would be sent to all commercial servers in
276 * Poland. */
277 need_dots = 2;
279 if (domain_len > 0) {
280 int pos = end_with_known_tld(domain, domain_len);
282 if (pos >= 1 && domain[pos - 1] == '.')
283 need_dots = 1;
287 for (i = 0; domain[i]; i++)
288 if (domain[i] == '.' && !--need_dots)
289 break;
291 if (need_dots > 0) return 0;
292 return 1;
295 /* Allocate a struct cookie and initialize it with the specified
296 * values (rather than copies). Returns NULL on error. On success,
297 * the cookie is basically safe for @done_cookie or @accept_cookie,
298 * although you may also want to set the remaining members and check
299 * @get_cookies_accept_policy and @is_domain_security_ok.
301 * The unsigned char * arguments must be allocated with @mem_alloc or
302 * equivalent, because @done_cookie will @mem_free them. Likewise,
303 * the caller must already have locked @server. If @init_cookie
304 * fails, then it frees the strings itself, and unlocks @server.
306 * If any parameter is NULL, then @init_cookie fails and does not
307 * consider that a bug. This means callers can use e.g. @stracpy
308 * and let @init_cookie check whether the call ran out of memory. */
309 struct cookie *
310 init_cookie(unsigned char *name, unsigned char *value,
311 unsigned char *path, unsigned char *domain,
312 struct cookie_server *server)
314 struct cookie *cookie = mem_calloc(1, sizeof(*cookie));
315 if (!cookie || !name || !value || !path || !domain || !server) {
316 mem_free_if(cookie);
317 mem_free_if(name);
318 mem_free_if(value);
319 mem_free_if(path);
320 mem_free_if(domain);
321 done_cookie_server(server);
322 return NULL;
324 object_nolock(cookie, "cookie"); /* Debugging purpose. */
326 cookie->name = name;
327 cookie->value = value;
328 cookie->domain = domain;
329 cookie->path = path;
330 cookie->server = server; /* the caller already locked it for us */
332 return cookie;
335 void
336 set_cookie(struct uri *uri, unsigned char *str)
338 unsigned char *path, *domain;
339 struct cookie *cookie;
340 struct cookie_str cstr;
341 int max_age;
343 if (get_cookies_accept_policy() == COOKIES_ACCEPT_NONE)
344 return;
346 #ifdef DEBUG_COOKIES
347 DBG("set_cookie -> (%s) %s", struri(uri), str);
348 #endif
350 if (!parse_cookie_str(&cstr, str)) return;
352 switch (parse_header_param(str, "path", &path)) {
353 unsigned char *path_end;
355 case HEADER_PARAM_FOUND:
356 if (!path[0]
357 || path[strlen(path) - 1] != '/')
358 add_to_strn(&path, "/");
360 if (path[0] != '/') {
361 add_to_strn(&path, "x");
362 memmove(path + 1, path, strlen(path) - 1);
363 path[0] = '/';
365 break;
367 case HEADER_PARAM_NOT_FOUND:
368 path = get_uri_string(uri, URI_PATH);
369 if (!path)
370 return;
372 path_end = strrchr(path, '/');
373 if (path_end)
374 path_end[1] = '\0';
375 break;
377 default: /* error */
378 return;
381 if (parse_header_param(str, "domain", &domain) == HEADER_PARAM_NOT_FOUND)
382 domain = memacpy(uri->host, uri->hostlen);
383 if (domain && domain[0] == '.')
384 memmove(domain, domain + 1, strlen(domain));
386 cookie = init_cookie(memacpy(str, cstr.nam_end - str),
387 memacpy(cstr.val_start, cstr.val_end - cstr.val_start),
388 path,
389 domain,
390 get_cookie_server(uri->host, uri->hostlen));
391 if (!cookie) return;
392 /* @cookie now owns @path and @domain. */
394 #if 0
395 /* We don't actually set ->accept at the moment. But I have kept it
396 * since it will maybe help to fix bug 77 - Support for more
397 * finegrained control upon accepting of cookies. */
398 if (!cookie->server->accept) {
399 #ifdef DEBUG_COOKIES
400 DBG("Dropped.");
401 #endif
402 done_cookie(cookie);
403 return;
405 #endif
407 /* Set cookie expiration if needed.
408 * Cookie expires at end of session by default,
409 * set to 0 by calloc().
411 * max_age:
412 * -1 is use cookie's expiration date if any
413 * 0 is force expiration at the end of session,
414 * ignoring cookie's expiration date
415 * 1+ is use cookie's expiration date,
416 * but limit age to the given number of days.
419 max_age = get_cookies_max_age();
420 if (max_age) {
421 unsigned char *date;
422 time_t expires;
424 switch (parse_header_param(str, "expires", &date)) {
425 case HEADER_PARAM_FOUND:
426 expires = parse_date(&date, NULL, 0, 1); /* Convert date to seconds. */
428 mem_free(date);
430 if (expires) {
431 if (max_age > 0) {
432 time_t seconds = ((time_t) max_age)*24*3600;
433 time_t deadline = time(NULL) + seconds;
435 if (expires > deadline) /* Over-aged cookie ? */
436 expires = deadline;
439 cookie->expires = expires;
441 break;
443 case HEADER_PARAM_NOT_FOUND:
444 break;
446 default: /* error */
447 done_cookie(cookie);
448 return;
452 cookie->secure = (parse_header_param(str, "secure", NULL)
453 == HEADER_PARAM_FOUND);
455 #ifdef DEBUG_COOKIES
457 DBG("Got cookie %s = %s from %s, domain %s, "
458 "expires at %"TIME_PRINT_FORMAT", secure %d", cookie->name,
459 cookie->value, cookie->server->host, cookie->domain,
460 (time_print_T) cookie->expires, cookie->secure);
462 #endif
464 if (!is_domain_security_ok(cookie->domain, uri->host, uri->hostlen)) {
465 #ifdef DEBUG_COOKIES
466 DBG("Domain security violated: %s vs %.*s", cookie->domain,
467 uri->hostlen, uri->host);
468 #endif
469 mem_free(cookie->domain);
470 cookie->domain = memacpy(uri->host, uri->hostlen);
473 /* We have already check COOKIES_ACCEPT_NONE */
474 if (get_cookies_accept_policy() == COOKIES_ACCEPT_ASK) {
475 add_to_list(cookie_queries, cookie);
476 add_questions_entry(accept_cookie_dialog, cookie);
477 return;
480 accept_cookie(cookie);
483 void
484 accept_cookie(struct cookie *cookie)
486 struct c_domain *cd;
487 struct listbox_item *root = cookie->server->box_item;
488 int domain_len;
490 if (root)
491 cookie->box_item = add_listbox_leaf(&cookie_browser, root, cookie);
493 /* Do not weed out duplicates when loading the cookie file. It doesn't
494 * scale at all, being O(N^2) and taking about 2s with my 500 cookies
495 * (so if you don't notice that 100ms with your 100 cookies, that's
496 * not an argument). --pasky */
497 if (!cookies_nosave) {
498 struct cookie *c, *next;
500 foreachsafe (c, next, cookies) {
501 if (strcasecmp(c->name, cookie->name)
502 || strcasecmp(c->domain, cookie->domain))
503 continue;
505 delete_cookie(c);
506 /* @set_cookies_dirty will be called below. */
510 add_to_list(cookies, cookie);
511 set_cookies_dirty();
513 /* XXX: This crunches CPU too. --pasky */
514 foreach (cd, c_domains)
515 if (!strcasecmp(cd->domain, cookie->domain))
516 return;
518 domain_len = strlen(cookie->domain);
519 /* One byte is reserved for domain in struct c_domain. */
520 cd = mem_alloc(sizeof(*cd) + domain_len);
521 if (!cd) return;
523 memcpy(cd->domain, cookie->domain, domain_len + 1);
524 add_to_list(c_domains, cd);
527 #if 0
528 static unsigned int cookie_id = 0;
530 static void
531 delete_cookie(struct cookie *c)
533 struct c_domain *cd;
534 struct cookie *d;
536 foreach (d, cookies)
537 if (!strcasecmp(d->domain, c->domain))
538 goto end;
540 foreach (cd, c_domains) {
541 if (!strcasecmp(cd->domain, c->domain)) {
542 del_from_list(cd);
543 mem_free(cd);
544 break;
548 end:
549 del_from_list(c);
550 done_cookie(c);
554 static struct
555 cookie *find_cookie_id(void *idp)
557 int id = (int) idp;
558 struct cookie *c;
560 foreach (c, cookies)
561 if (c->id == id)
562 return c;
564 return NULL;
568 static void
569 reject_cookie(void *idp)
571 struct cookie *c = find_cookie_id(idp);
573 if (!c) return;
575 delete_cookie(c);
576 set_cookies_dirty(); /* @find_cookie_id doesn't use @cookie_queries */
580 static void
581 cookie_default(void *idp, int a)
583 struct cookie *c = find_cookie_id(idp);
585 if (c) c->server->accept = a;
589 static void
590 accept_cookie_always(void *idp)
592 cookie_default(idp, 1);
596 static void
597 accept_cookie_never(void *idp)
599 cookie_default(idp, 0);
600 reject_cookie(idp);
602 #endif
604 /* Check whether domain is matching server
605 * Ie.
606 * example.com matches www.example.com/
607 * example.com doesn't match www.example.com.org/
608 * example.com doesn't match www.example.comm/
609 * example.com doesn't match example.co
611 static int
612 is_in_domain(unsigned char *domain, unsigned char *server, int server_len)
614 int domain_len = strlen(domain);
615 int len;
617 if (domain_len > server_len)
618 return 0;
620 if (domain_len == server_len)
621 return !strncasecmp(domain, server, server_len);
623 len = server_len - domain_len;
624 if (server[len - 1] != '.')
625 return 0;
627 return !strncasecmp(domain, server + len, domain_len);
631 static inline int
632 is_path_prefix(unsigned char *d, unsigned char *s)
634 int dl = strlen(d);
636 /* TODO: strlcmp()? --pasky */
638 if (dl > strlen(s)) return 0;
640 return !memcmp(d, s, dl);
644 struct string *
645 send_cookies(struct uri *uri)
647 struct c_domain *cd;
648 struct cookie *c, *next;
649 unsigned char *path = NULL;
650 static struct string header;
651 time_t now;
653 if (!uri->host || !uri->data)
654 return NULL;
656 foreach (cd, c_domains)
657 if (is_in_domain(cd->domain, uri->host, uri->hostlen)) {
658 path = get_uri_string(uri, URI_PATH);
659 break;
662 if (!path) return NULL;
664 init_string(&header);
666 now = time(NULL);
667 foreachsafe (c, next, cookies) {
668 if (!is_in_domain(c->domain, uri->host, uri->hostlen)
669 || !is_path_prefix(c->path, path))
670 continue;
672 if (c->expires && c->expires <= now) {
673 #ifdef DEBUG_COOKIES
674 DBG("Cookie %s=%s (exp %"TIME_PRINT_FORMAT") expired.",
675 c->name, c->value, (time_print_T) c->expires);
676 #endif
677 delete_cookie(c);
679 set_cookies_dirty();
680 continue;
683 /* Not sure if this is 100% right..? --pasky */
684 if (c->secure && uri->protocol != PROTOCOL_HTTPS)
685 continue;
687 if (header.length)
688 add_to_string(&header, "; ");
690 add_to_string(&header, c->name);
691 add_char_to_string(&header, '=');
692 add_to_string(&header, c->value);
693 #ifdef DEBUG_COOKIES
694 DBG("Cookie: %s=%s", c->name, c->value);
695 #endif
698 mem_free(path);
700 if (!header.length) {
701 done_string(&header);
702 return NULL;
705 return &header;
708 static void done_cookies(struct module *module);
711 void
712 load_cookies(void) {
713 /* Buffer size is set to be enough to read long lines that
714 * save_cookies may write. 6 is choosen after the fprintf(..) call
715 * in save_cookies(). --Zas */
716 unsigned char in_buffer[6 * MAX_STR_LEN];
717 unsigned char *cookfile = COOKIES_FILENAME;
718 FILE *fp;
719 time_t now;
721 if (elinks_home) {
722 cookfile = straconcat(elinks_home, cookfile,
723 (unsigned char *) NULL);
724 if (!cookfile) return;
727 /* Do it here, as we will delete whole cookies list if the file was
728 * removed */
729 cookies_nosave = 1;
730 done_cookies(&cookies_module);
731 cookies_nosave = 0;
733 fp = fopen(cookfile, "rb");
734 if (elinks_home) mem_free(cookfile);
735 if (!fp) return;
737 /* XXX: We don't want to overwrite the cookies file
738 * periodically to our death. */
739 cookies_nosave = 1;
741 now = time(NULL);
742 while (fgets(in_buffer, 6 * MAX_STR_LEN, fp)) {
743 struct cookie *cookie;
744 unsigned char *p, *q = in_buffer;
745 enum { NAME = 0, VALUE, SERVER, PATH, DOMAIN, EXPIRES, SECURE, MEMBERS } member;
746 struct {
747 unsigned char *pos;
748 int len;
749 } members[MEMBERS];
750 time_t expires;
752 /* First find all members. */
753 for (member = NAME; member < MEMBERS; member++, q = ++p) {
754 p = strchr(q, '\t');
755 if (!p) {
756 if (member + 1 != MEMBERS) break; /* last field ? */
757 p = strchr(q, '\n');
758 if (!p) break;
761 members[member].pos = q;
762 members[member].len = p - q;
765 if (member != MEMBERS) continue; /* Invalid line. */
767 /* Skip expired cookies if any. */
768 expires = str_to_time_t(members[EXPIRES].pos);
769 if (!expires || expires <= now) {
770 set_cookies_dirty();
771 continue;
774 /* Prepare cookie if all members and fields was read. */
775 cookie = mem_calloc(1, sizeof(*cookie));
776 if (!cookie) continue;
778 cookie->server = get_cookie_server(members[SERVER].pos, members[SERVER].len);
779 cookie->name = memacpy(members[NAME].pos, members[NAME].len);
780 cookie->value = memacpy(members[VALUE].pos, members[VALUE].len);
781 cookie->path = memacpy(members[PATH].pos, members[PATH].len);
782 cookie->domain = memacpy(members[DOMAIN].pos, members[DOMAIN].len);
784 /* Check whether all fields were correctly allocated. */
785 if (!cookie->server || !cookie->name || !cookie->value
786 || !cookie->path || !cookie->domain) {
787 done_cookie(cookie);
788 continue;
791 cookie->expires = expires;
792 cookie->secure = !!atoi(members[SECURE].pos);
794 accept_cookie(cookie);
797 cookies_nosave = 0;
798 fclose(fp);
801 static void
802 resave_cookies_bottom_half(void *always_null)
804 if (get_cookies_save() && get_cookies_resave())
805 save_cookies(NULL); /* checks cookies_dirty */
808 /* Note that the cookies have been modified, and register a bottom
809 * half for saving them if appropriate. We use a bottom half so that
810 * if something makes multiple changes and calls this for each change,
811 * the cookies get saved only once at the end. */
812 void
813 set_cookies_dirty(void)
815 /* Do not check @cookies_dirty here. If the previous attempt
816 * to save cookies failed, @cookies_dirty can still be nonzero
817 * even though @resave_cookies_bottom_half is no longer in the
818 * queue. */
819 cookies_dirty = 1;
820 /* If @resave_cookies_bottom_half is already in the queue,
821 * @register_bottom_half does nothing. */
822 register_bottom_half(resave_cookies_bottom_half, NULL);
825 /* @term is non-NULL if the user told ELinks to save cookies, or NULL
826 * if ELinks decided that on its own. In the former case, this
827 * function reports errors to @term, unless CONFIG_SMALL is defined.
828 * In the latter case, this function does not save the cookies if it
829 * thinks the file is already up to date. */
830 void
831 save_cookies(struct terminal *term) {
832 struct cookie *c;
833 unsigned char *cookfile;
834 struct secure_save_info *ssi;
835 time_t now;
837 #ifdef CONFIG_SMALL
838 # define CANNOT_SAVE_COOKIES(flags, message)
839 #else
840 # define CANNOT_SAVE_COOKIES(flags, message) \
841 do { \
842 if (term) \
843 info_box(term, flags, N_("Cannot save cookies"),\
844 ALIGN_LEFT, message); \
845 } while (0)
846 #endif
848 if (cookies_nosave) {
849 assert(term == NULL);
850 if_assert_failed {}
851 return;
853 if (!elinks_home) {
854 CANNOT_SAVE_COOKIES(0, N_("ELinks was started without a home directory."));
855 return;
857 if (!cookies_dirty && !term)
858 return;
859 if (get_cmd_opt_bool("anonymous")) {
860 CANNOT_SAVE_COOKIES(0, N_("ELinks was started with the -anonymous option."));
861 return;
864 cookfile = straconcat(elinks_home, COOKIES_FILENAME,
865 (unsigned char *) NULL);
866 if (!cookfile) {
867 CANNOT_SAVE_COOKIES(0, N_("Out of memory"));
868 return;
871 ssi = secure_open(cookfile);
872 mem_free(cookfile);
873 if (!ssi) {
874 CANNOT_SAVE_COOKIES(MSGBOX_NO_TEXT_INTL,
875 secsave_strerror(secsave_errno, term));
876 return;
879 now = time(NULL);
880 foreach (c, cookies) {
881 if (!c->expires || c->expires <= now) continue;
882 if (secure_fprintf(ssi, "%s\t%s\t%s\t%s\t%s\t%"TIME_PRINT_FORMAT"\t%d\n",
883 c->name, c->value,
884 c->server->host,
885 empty_string_or_(c->path),
886 empty_string_or_(c->domain),
887 (time_print_T) c->expires, c->secure) < 0)
888 break;
891 secsave_errno = SS_ERR_OTHER; /* @secure_close doesn't always set it */
892 if (!secure_close(ssi)) cookies_dirty = 0;
893 else {
894 CANNOT_SAVE_COOKIES(MSGBOX_NO_TEXT_INTL,
895 secsave_strerror(secsave_errno, term));
897 #undef CANNOT_SAVE_COOKIES
900 static void
901 init_cookies(struct module *module)
903 if (get_cookies_save())
904 load_cookies();
907 /* Like @delete_cookie, this function does not set @cookies_dirty.
908 * The caller must do that if appropriate. */
909 static void
910 free_cookies_list(LIST_OF(struct cookie) *list)
912 while (!list_empty(*list)) {
913 struct cookie *cookie = list->next;
915 delete_cookie(cookie);
919 static void
920 done_cookies(struct module *module)
922 free_list(c_domains);
924 if (!cookies_nosave && get_cookies_save())
925 save_cookies(NULL);
927 free_cookies_list(&cookies);
928 free_cookies_list(&cookie_queries);
929 /* If @save_cookies failed above, @cookies_dirty can still be
930 * nonzero. Now if @resave_cookies_bottom_half were in the
931 * queue, it could save the empty @cookies list to the file.
932 * Prevent that. */
933 cookies_dirty = 0;
936 struct module cookies_module = struct_module(
937 /* name: */ N_("Cookies"),
938 /* options: */ cookies_options,
939 /* events: */ NULL,
940 /* submodules: */ NULL,
941 /* data: */ NULL,
942 /* init: */ init_cookies,
943 /* done: */ done_cookies