links/cookies.c

400 lines
10 KiB
C

/* cookies.c
* Cookies
* (c) 2002 Mikulas Patocka
* This file is a part of the Links program, released under GPL
*/
#include "links.h"
#define ACCEPT_NONE 0
#define ACCEPT_ALL 1
struct list_head all_cookies = { &all_cookies, &all_cookies };
struct list_head c_domains = { &c_domains, &c_domains };
void free_cookie(struct cookie *c)
{
mem_free(c->name);
if (c->value) mem_free(c->value);
mem_free(c->server);
mem_free(c->path);
mem_free(c->domain);
mem_free(c);
}
static void accept_cookie(struct cookie *);
/* sezere 1 cookie z retezce str, na zacatku nesmi byt zadne whitechars
* na konci muze byt strednik nebo 0
* cookie musi byt ve tvaru nazev=hodnota, kolem rovnase nesmi byt zadne mezery
* (respektive mezery se budou pocitat do nazvu a do hodnoty)
*/
void set_cookie(unsigned char *url, unsigned char *str)
{
int noval = 0;
struct cookie *cookie;
unsigned char *p, *q, *s, *server, *date, *max_age, *dom;
if (!enable_cookies)
return;
for (p = str; *p != ';' && *p; p++) { /*if (WHITECHAR(*p)) return;*/ }
for (q = str; *q != '='; q++) if (!*q || q >= p) {
noval = 1;
break;
}
if (str == q || q + 1 == p) return;
cookie = mem_alloc(sizeof(struct cookie));
server = get_host_name(url);
cookie->saved_cookie = 0;
cookie->name = memacpy(str, q - str);
cookie->value = !noval ? memacpy(q + 1, p - q - 1) : NULL;
cookie->server = stracpy(server);
cookie->created = get_absolute_seconds();
cookie->expires = (time_t)-1;
date = parse_header_param(str, cast_uchar "expires", 0);
if (date) {
cookie->expires = parse_http_date(date);
/* if (!cookie->expires) debug("unable to parse '%s'", date); */
mem_free(date);
}
max_age = parse_header_param(str, cast_uchar "max-age", 0);
if (max_age) {
char *end;
long ma = strtol(cast_const_char max_age, &end, 10);
if (*max_age && !*end) {
if (ma < 0) {
cookie->expires = 0;
} else {
cookie->expires = (time_t)((uttime)cookie->created + (uttime)ma);
}
}
mem_free(max_age);
}
if (!(cookie->path = parse_header_param(str, cast_uchar "path", 0))) {
cookie->path = stracpy(cast_uchar "/");
} else {
if (cookie->path[0] != '/') {
add_to_strn(&cookie->path, cast_uchar "x");
memmove(cookie->path + 1, cookie->path, strlen(cast_const_char cookie->path) - 1);
cookie->path[0] = '/';
}
}
dom = parse_header_param(str, cast_uchar "domain", 0);
if (!dom) {
cookie->domain = stracpy(server);
} else {
cookie->domain = idn_encode_host(dom, (int)strlen(cast_const_char dom), cast_uchar ".", 0);
if (!cookie->domain)
cookie->domain = stracpy(server);
mem_free(dom);
}
if (cookie->domain[0] == '.') memmove(cookie->domain, cookie->domain + 1, strlen(cast_const_char cookie->domain));
if ((s = parse_header_param(str, cast_uchar "secure", 0))) {
cookie->secure = 1;
mem_free(s);
} else cookie->secure = 0;
if (!allow_cookie_domain(server, cookie->domain)) {
mem_free(cookie->domain);
cookie->domain = stracpy(server);
}
accept_cookie(cookie);
mem_free(server);
}
static void accept_cookie(struct cookie *c)
{
struct c_domain *cd;
struct list_head *lcd;
struct cookie *d;
struct list_head *ld;
size_t sl;
foreach(struct cookie, d, ld, all_cookies) if (!casestrcmp(d->name, c->name) && !casestrcmp(d->domain, c->domain)) {
ld = ld->prev;
del_from_list(d);
free_cookie(d);
}
if (c->value && !casestrcmp(c->value, cast_uchar "deleted")) {
free_cookie(c);
return;
}
add_to_list(all_cookies, c);
foreach(struct c_domain, cd, lcd, c_domains) if (!casestrcmp(cd->domain, c->domain)) return;
sl = strlen(cast_const_char c->domain);
if (sl > MAXINT - sizeof(struct c_domain)) overalloc();
cd = mem_alloc(sizeof(struct c_domain) + sl);
strcpy(cast_char cd->domain, cast_const_char c->domain);
add_to_list(c_domains, cd);
}
int is_in_domain(unsigned char *d, unsigned char *s)
{
int dl = (int)strlen(cast_const_char d);
int sl = (int)strlen(cast_const_char s);
if (dl > sl) return 0;
if (dl == sl) return !casestrcmp(d, s);
if (s[sl - dl - 1] != '.') return 0;
return !casecmp(d, s + sl - dl, dl);
}
int is_path_prefix(unsigned char *d, unsigned char *s)
{
int dl = (int)strlen(cast_const_char d);
int sl = (int)strlen(cast_const_char s);
if (!dl) return 1;
if (dl > sl) return 0;
if (memcmp(d, s, dl)) return 0;
return d[dl - 1] == '/' || !s[dl] || s[dl] == '/' || s[dl] == POST_CHAR || s[dl] == '?' || s[dl] == '&';
}
int cookie_expired(struct cookie *c, time_t now)
{
if (c->expires != (time_t)-1 && c->expires < now)
return 1;
if (max_cookie_age && now - c->created > max_cookie_age * 86400)
return 1;
return 0;
}
void add_cookies(unsigned char **s, int *l, unsigned char *url)
{
int nc = 0;
struct c_domain *cd;
struct list_head *lcd;
struct cookie *c;
struct list_head *lc;
unsigned char *server, *data;
time_t now;
if (!enable_cookies)
return;
server = get_host_name(url);
data = get_url_data(url);
if (data > url) data--;
foreach(struct c_domain, cd, lcd, c_domains) if (is_in_domain(cd->domain, server)) goto ok;
mem_free(server);
return;
ok:
now = get_absolute_seconds();
foreachback(struct cookie, c, lc, all_cookies) if (is_in_domain(c->domain, server)) if (is_path_prefix(c->path, data)) {
if (cookie_expired(c, now)) {
lc = lc->prev;
del_from_list(c);
free_cookie(c);
continue;
}
if (c->saved_cookie && !save_cookies) continue;
if (c->secure && casecmp(url, cast_uchar "https://", 8)) continue;
if (!nc) add_to_str(s, l, cast_uchar "Cookie: "), nc = 1;
else add_to_str(s, l, cast_uchar "; ");
add_to_str(s, l, c->name);
if (c->value) {
add_chr_to_str(s, l, '=');
add_to_str(s, l, c->value);
}
}
if (nc) add_to_str(s, l, cast_uchar "\r\n");
mem_free(server);
}
void clear_cookies_file(void)
{
unsigned char *cookies_file;
if (!links_home)
return;
cookies_file = stracpy(links_home);
add_to_strn(&cookies_file, cast_uchar "cookies.txt");
delete_config_file(cookies_file);
mem_free(cookies_file);
}
void do_save_cookies(void)
{
unsigned char *cookies_file;
unsigned char *s;
int sl;
time_t now;
struct cookie *c;
struct list_head *lc;
if (anonymous || !save_cookies || proxies.only_proxies)
return;
if (!links_home)
return;
s = init_str();
sl = 0;
cookies_file = stracpy(links_home);
add_to_strn(&cookies_file, cast_uchar "cookies.txt");
now = get_absolute_seconds();
foreachback(struct cookie, c, lc, all_cookies) {
if (cookie_expired(c, now) || c->expires == (time_t)-1)
continue;
add_quoted_to_str(&s, &sl, c->domain);
add_chr_to_str(&s, &sl, ' ');
add_quoted_to_str(&s, &sl, c->server);
add_chr_to_str(&s, &sl, ' ');
add_quoted_to_str(&s, &sl, c->path);
add_chr_to_str(&s, &sl, ' ');
add_quoted_to_str(&s, &sl, c->name);
add_chr_to_str(&s, &sl, ' ');
add_quoted_to_str(&s, &sl, c->value ? c->value : cast_uchar "");
add_chr_to_str(&s, &sl, ' ');
add_chr_to_str(&s, &sl, '0' + c->secure);
add_chr_to_str(&s, &sl, ' ');
add_chr_to_str(&s, &sl, '"');
add_to_str(&s, &sl, print_http_date(c->created));
add_chr_to_str(&s, &sl, '"');
add_chr_to_str(&s, &sl, ' ');
add_chr_to_str(&s, &sl, '"');
add_to_str(&s, &sl, print_http_date(c->expires));
add_chr_to_str(&s, &sl, '"');
add_to_str(&s, &sl, cast_uchar NEWLINE);
}
write_to_config_file(cookies_file, s, 0);
mem_free(cookies_file);
mem_free(s);
}
unsigned long free_cookies(void)
{
unsigned long cnt = 0;
time_t now = get_absolute_seconds();
free_list(struct c_domain, c_domains);
while (!list_empty(all_cookies)) {
struct cookie *c = list_struct(all_cookies.next, struct cookie);
if (!cookie_expired(c, now))
cnt++;
del_from_list(c);
free_cookie(c);
}
return cnt;
}
static time_t parse_cookie_time(unsigned char *t)
{
time_t tm;
if (t[strspn(cast_const_char t, "0123456789")] != 0) {
tm = parse_http_date(t);
} else {
char *end;
tm = (time_t)strtod(cast_const_char t, &end);
if (*end)
tm = (time_t)-1;
}
if (tm == (time_t)-1) {
fprintf(stderr, "Invalid cookie time '%s'\n", t);
return (time_t)-1;
}
return tm;
}
void init_cookies(void)
{
int err = 0;
unsigned char *cookies_file;
unsigned char *s, *p;
if (anonymous || proxies.only_proxies)
return;
if (!links_home)
return;
cookies_file = stracpy(links_home);
add_to_strn(&cookies_file, cast_uchar "cookies.txt");
s = read_config_file(cookies_file);
p = s;
if (s) while (*p) {
unsigned char *l, *line;
unsigned char *domain, *server, *path, *name, *value, *secure, *created, *expires;
if (*p == '\r' || *p == '\n') {
p++;
continue;
}
l = p;
while (*l && *l != '\r' && *l != '\n')
l++;
line = memacpy(p, l - p);
p = l;
l = line;
domain = get_token(&l);
server = get_token(&l);
path = get_token(&l);
name = get_token(&l);
value = get_token(&l);
secure = get_token(&l);
created = get_token(&l);
expires = get_token(&l);
mem_free(line);
/*debug("%s %s %s %s %s %s %s %s", name, value, server, path, domain, secure, created expires);*/
if (expires) {
struct cookie *c;
time_t created_time, expire_time;
created_time = parse_cookie_time(created);
expire_time = parse_cookie_time(expires);
if (created_time == (time_t)-1 || expire_time == (time_t)-1)
goto er;
c = mem_alloc(sizeof(struct cookie));
c->saved_cookie = 1;
c->name = name, name = NULL;
if (*value)
c->value = value, value = NULL;
else
c->value = NULL;
c->server = server, server = NULL;
c->path = path, path = NULL;
c->domain = domain, domain = NULL;
c->secure = *secure >= '1';
c->created = created_time;
c->expires = expire_time;
accept_cookie(c);
}
er:
if (domain) mem_free(domain);
if (server) mem_free(server);
if (path) mem_free(path);
if (name) mem_free(name);
if (value) mem_free(value);
if (secure) mem_free(secure);
if (created) mem_free(created);
if (expires) mem_free(expires);
}
mem_free(cookies_file);
if (s)
mem_free(s);
if (err) fprintf(stderr, "\007"), portable_sleep(1000);
/*{
time_t nt, t;
nt = get_absolute_seconds();
unsigned char *now;
now = print_http_date(nt);
debug("now: %s", now);
t = parse_http_date(now);
debug("%ld - %ld", t, nt);
now = print_http_date(t);
debug("now: %s", now);
}*/
}