399 lines
10 KiB
C
399 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);
|
|
}*/
|
|
}
|