1 #include "links.h"
2 
3 #define ACCEPT_NONE	0
4 #define ACCEPT_ASK	1
5 #define ACCEPT_ALL	2
6 
7 int accept_cookies = ACCEPT_ALL;
8 
9 tcount cookie_id = 0;
10 
11 struct cookie {
12 	struct cookie *next;
13 	struct cookie *prev;
14 	unsigned char *name, *value;
15 	unsigned char *server;
16 	unsigned char *path, *domain;
17 	time_t expires; /* zero means undefined */
18 	int secure;
19 	int id;
20 };
21 
22 struct list_head cookies = { &cookies, &cookies };
23 
24 struct c_domain {
25 	struct c_domain *next;
26 	struct c_domain *prev;
27 	unsigned char domain[1];
28 };
29 
30 struct list_head c_domains = { &c_domains, &c_domains };
31 
32 struct c_server {
33 	struct c_server *next;
34 	struct c_server *prev;
35 	int accpt;
36 	unsigned char server[1];
37 };
38 
39 struct list_head c_servers = { &c_servers, &c_servers };
40 
41 void accept_cookie(struct cookie *);
42 void delete_cookie(struct cookie *);
43 
free_cookie(struct cookie * c)44 void free_cookie(struct cookie *c)
45 {
46 	mem_free(c->name);
47 	if (c->value) mem_free(c->value);
48 	if (c->server) mem_free(c->server);
49 	if (c->path) mem_free(c->path);
50 	if (c->domain) mem_free(c->domain);
51 }
52 
check_domain_security(unsigned char * server,unsigned char * domain)53 int check_domain_security(unsigned char *server, unsigned char *domain)
54 {
55 	size_t i, j, dl;
56 	int nd;
57 	if (domain[0] == '.') domain++;
58 	dl = strlen(domain);
59 	if (dl > strlen(server)) return 1;
60 	for (i = strlen(server) - dl, j = 0; server[i]; i++, j++)
61 		if (upcase(server[i]) != upcase(domain[j])) return 1;
62 	nd = 2;
63 	if (dl > 4 && domain[dl - 4] == '.') {
64 		unsigned char *tld[] = { "com", "edu", "net", "org", "gov", "mil", "int", NULL };
65 		for (i = 0; tld[i]; i++) if (!casecmp(tld[i], &domain[dl - 3], 3)) {
66 			nd = 1;
67 			break;
68 		}
69 	}
70 	for (i = 0; domain[i]; i++) if (domain[i] == '.') if (!--nd) break;
71 	if (nd > 0) return 1;
72 	return 0;
73 }
74 
set_cookie(struct terminal * term,unsigned char * url,unsigned char * str)75 int set_cookie(struct terminal *term, unsigned char *url, unsigned char *str)
76 {
77 	int noval = 0;
78 	struct cookie *cookie;
79 	struct c_server *cs;
80 	unsigned char *p, *q, *s, *server, *date;
81 	if (accept_cookies == ACCEPT_NONE) return 0;
82 	for (p = str; *p != ';' && *p; p++) /*if (WHITECHAR(*p)) return 0*/;
83 	for (q = str; *q != '='; q++) if (!*q || q >= p) {
84 		noval = 1;
85 		break;
86 	}
87 	if (str == q || q + 1 == p) return 0;
88 	cookie = mem_alloc(sizeof(struct cookie));
89 	server = get_host_name(url);
90 	cookie->name = memacpy(str, q - str);
91 	cookie->value = !noval ? memacpy(q + 1, p - q - 1) : NULL;
92 	cookie->server = stracpy(server);
93 	date = parse_header_param(str, "expires");
94 	if (date) {
95 		cookie->expires = parse_http_date(date);
96 		/* kdo tohle napsal a proc ?? */
97 		/*if (! cookie->expires) cookie->expires++;*/ /* no harm and we can use zero then */
98 		mem_free(date);
99 	} else
100 		cookie->expires = 0;
101 	if (!(cookie->path = parse_header_param(str, "path"))) {
102 		/*unsigned char *w;*/
103 		cookie->path = stracpy("/");
104 		/*
105 		add_to_strn(&cookie->path, document);
106 		for (w = cookie->path; *w; w++) if (end_of_dir(*w)) {
107 			*w = 0;
108 			break;
109 		}
110 		for (w = cookie->path + strlen(cookie->path) - 1; w >= cookie->path; w--)
111 			if (*w == '/') {
112 				w[1] = 0;
113 				break;
114 			}
115 		*/
116 	} else {
117 		if (cookie->path[0] != '/') {
118 			add_to_strn(&cookie->path, "x");
119 			memmove(cookie->path + 1, cookie->path, strlen(cookie->path) - 1);
120 			cookie->path[0] = '/';
121 		}
122 	}
123 	if (!(cookie->domain = parse_header_param(str, "domain"))) cookie->domain = stracpy(server);
124 	if (cookie->domain[0] == '.') memmove(cookie->domain, cookie->domain + 1, strlen(cookie->domain));
125 	if ((s = parse_header_param(str, "secure"))) {
126 		cookie->secure = 1;
127 		mem_free(s);
128 	} else cookie->secure = 0;
129 	if (check_domain_security(server, cookie->domain)) {
130 		mem_free(cookie->domain);
131 		cookie->domain = stracpy(server);
132 	}
133 	cookie->id = cookie_id++;
134 	foreach (cs, c_servers) if (!strcasecmp(cs->server, server)) {
135 		if (cs->accpt) goto ok;
136 		else {
137 			free_cookie(cookie);
138 			mem_free(cookie);
139 			mem_free(server);
140 			return 0;
141 		}
142 	}
143 	if (accept_cookies != ACCEPT_ALL) {
144 		free_cookie(cookie);
145 		mem_free(cookie);
146 		mem_free(server);
147 		return 1;
148 	}
149 	ok:
150 	accept_cookie(cookie);
151 	mem_free(server);
152 	return 0;
153 }
154 
accept_cookie(struct cookie * c)155 void accept_cookie(struct cookie *c)
156 {
157 	struct c_domain *cd;
158 	struct cookie *d, *e;
159 	foreach(d, cookies) if (!strcasecmp(d->name, c->name) && !strcasecmp(d->domain, c->domain)) {
160 		e = d;
161 		d = d->prev;
162 		del_from_list(e);
163 		free_cookie(e);
164 		mem_free(e);
165 	}
166 	if (c->value && !strcasecmp(c->value, "deleted")) {
167 		free_cookie(c);
168 		mem_free(c);
169 		return;
170 	}
171 	add_to_list(cookies, c);
172 	foreach(cd, c_domains) if (!strcasecmp(cd->domain, c->domain)) return;
173 	cd = mem_alloc(sizeof(struct c_domain) + strlen(c->domain) + 1);
174 	strcpy(cd->domain, c->domain);
175 	add_to_list(c_domains, cd);
176 }
177 
delete_cookie(struct cookie * c)178 void delete_cookie(struct cookie *c)
179 {
180 	struct c_domain *cd;
181 	struct cookie *d;
182 	foreach(d, cookies) if (!strcasecmp(d->domain, c->domain)) goto x;
183 	foreach(cd, c_domains) if (!strcasecmp(cd->domain, c->domain)) {
184 		del_from_list(cd);
185 		mem_free(cd);
186 		break;
187 	}
188 	x:
189 	del_from_list(c);
190 	free_cookie(c);
191 	mem_free(c);
192 }
193 
find_cookie_id(void * idp)194 struct cookie *find_cookie_id(void *idp)
195 {
196 	int id = (int)(my_uintptr_t)idp;
197 	struct cookie *c;
198 	foreach(c, cookies) if (c->id == id) return c;
199 	return NULL;
200 }
201 
reject_cookie(void * idp)202 void reject_cookie(void *idp)
203 {
204 	struct cookie *c;
205 	if (!(c = find_cookie_id(idp))) return;
206 	delete_cookie(c);
207 }
208 
cookie_default(void * idp,int a)209 void cookie_default(void *idp, int a)
210 {
211 	struct cookie *c;
212 	struct c_server *s;
213 	if (!(c = find_cookie_id(idp))) return;
214 	foreach(s, c_servers) if (!strcasecmp(s->server, c->server)) goto found;
215 	s = mem_alloc(sizeof(struct c_server) + strlen(c->server) + 1);
216 	strcpy(s->server, c->server);
217 	add_to_list(c_servers, s);
218 	found:
219 	s->accpt = a;
220 }
221 
accept_cookie_always(void * idp)222 void accept_cookie_always(void *idp)
223 {
224 	cookie_default(idp, 1);
225 }
226 
accept_cookie_never(void * idp)227 void accept_cookie_never(void *idp)
228 {
229 	cookie_default(idp, 0);
230 	reject_cookie(idp);
231 }
232 
is_in_domain(unsigned char * d,unsigned char * s)233 int is_in_domain(unsigned char *d, unsigned char *s)
234 {
235 	int dl = strlen(d);
236 	int sl = strlen(s);
237 	if (dl > sl) return 0;
238 	if (dl == sl) return !strcasecmp(d, s);
239 	if (s[sl - dl - 1] != '.') return 0;
240 	return !casecmp(d, s + sl - dl, dl);
241 }
242 
is_path_prefix(unsigned char * d,unsigned char * s)243 int is_path_prefix(unsigned char *d, unsigned char *s)
244 {
245 	int dl = strlen(d);
246 	int sl = strlen(s);
247 	if (!dl) return 1;
248 	if (dl > sl) return 0;
249 	if (memcmp(d, s, dl)) return 0;
250 	return d[dl - 1] == '/' || !s[dl] || s[dl] == '/' || s[dl] == POST_CHAR || s[dl] == '?' || s[dl] == '&';
251 }
252 
cookie_expired(struct cookie * c)253 int cookie_expired(struct cookie *c)	/* parse_http_date is broken */
254 {
255 	time_t t;
256 	EINTRLOOPX(t, time(NULL), (time_t)-1);
257   	return 0 && (c->expires && c->expires < t);
258 }
259 
send_cookies(unsigned char ** s,int * l,unsigned char * url)260 void send_cookies(unsigned char **s, int *l, unsigned char *url)
261 {
262 	int nc = 0;
263 	struct c_domain *cd;
264 	struct cookie *c, *d;
265 	unsigned char *server = get_host_name(url);
266 	unsigned char *data = get_url_data(url);
267 	if (data > url) data--;
268 	foreach (cd, c_domains) if (is_in_domain(cd->domain, server)) goto ok;
269 	mem_free(server);
270 	return;
271 	ok:
272 	foreach (c, cookies) if (is_in_domain(c->domain, server)) if (is_path_prefix(c->path, data)) {
273 		if (cookie_expired(c)) {
274 			d = c;
275 			c = c->prev;
276 			del_from_list(d);
277 			free_cookie(d);
278 			mem_free(d);
279 			continue;
280 		}
281 		if (c->secure && casecmp(url, "https://", 8)) continue;
282 		if (!nc) add_to_str(s, l, "Cookie: "), nc = 1;
283 		else add_to_str(s, l, "; ");
284 		add_to_str(s, l, c->name);
285 		if (c->value) {
286 			add_to_str(s, l, "=");
287 			add_to_str(s, l, c->value);
288 		}
289 	}
290 	if (nc) add_to_str(s, l, "\r\n");
291 	mem_free(server);
292 }
293 
init_cookies()294 void init_cookies()
295 {
296 	/* !!! FIXME: read cookies */
297 }
298 
cleanup_cookies()299 void cleanup_cookies()
300 {
301 	struct cookie *c;
302 	free_list(c_domains);
303 	/* !!! FIXME: save cookies */
304 	foreach (c, cookies) free_cookie(c);
305 	free_list(cookies);
306 }
307 
308