ddbc92a1c227b727a69470b2b46a4c647b92bd3b
[reactos.git] / reactos / dll / win32 / wininet / cookie.c
1 /*
2 * Wininet - cookie handling stuff
3 *
4 * Copyright 2002 TransGaming Technologies Inc.
5 *
6 * David Hammerton
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include "internet.h"
24
25 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */
26
27 /* FIXME
28 * Cookies could use A LOT OF MEMORY. We need some kind of memory management here!
29 */
30
31 struct _cookie_domain_t;
32 struct _cookie_container_t;
33
34 typedef struct _cookie_t {
35 struct list entry;
36
37 struct _cookie_container_t *container;
38
39 WCHAR *name;
40 WCHAR *data;
41 DWORD flags;
42 FILETIME expiry;
43 FILETIME create;
44 } cookie_t;
45
46 typedef struct _cookie_container_t {
47 struct list entry;
48
49 WCHAR *path;
50 struct _cookie_domain_t *domain;
51
52 struct list cookie_list;
53 } cookie_container_t;
54
55 typedef struct _cookie_domain_t {
56 struct list entry;
57
58 WCHAR *domain;
59 unsigned subdomain_len;
60
61 struct _cookie_domain_t *parent;
62 struct list subdomain_list;
63
64 /* List of stored paths sorted by length of the path. */
65 struct list path_list;
66 } cookie_domain_t;
67
68 static CRITICAL_SECTION cookie_cs;
69 static CRITICAL_SECTION_DEBUG cookie_cs_debug =
70 {
71 0, 0, &cookie_cs,
72 { &cookie_cs_debug.ProcessLocksList, &cookie_cs_debug.ProcessLocksList },
73 0, 0, { (DWORD_PTR)(__FILE__ ": cookie_cs") }
74 };
75 static CRITICAL_SECTION cookie_cs = { &cookie_cs_debug, -1, 0, 0, 0, 0 };
76 static struct list domain_list = LIST_INIT(domain_list);
77
78 static cookie_domain_t *get_cookie_domain(const WCHAR *domain, BOOL create)
79 {
80 const WCHAR *ptr = domain + strlenW(domain), *ptr_end, *subdomain_ptr;
81 cookie_domain_t *iter, *current_domain, *prev_domain = NULL;
82 struct list *current_list = &domain_list;
83
84 while(1) {
85 for(ptr_end = ptr--; ptr > domain && *ptr != '.'; ptr--);
86 subdomain_ptr = *ptr == '.' ? ptr+1 : ptr;
87
88 current_domain = NULL;
89 LIST_FOR_EACH_ENTRY(iter, current_list, cookie_domain_t, entry) {
90 if(ptr_end-subdomain_ptr == iter->subdomain_len && !memcmp(subdomain_ptr, iter->domain, iter->subdomain_len)) {
91 current_domain = iter;
92 break;
93 }
94 }
95
96 if(!current_domain) {
97 if(!create)
98 return prev_domain;
99
100 current_domain = heap_alloc(sizeof(*current_domain));
101 if(!current_domain)
102 return NULL;
103
104 current_domain->domain = heap_strdupW(subdomain_ptr);
105 if(!current_domain->domain) {
106 heap_free(current_domain);
107 return NULL;
108 }
109
110 current_domain->subdomain_len = ptr_end-subdomain_ptr;
111
112 current_domain->parent = prev_domain;
113 list_init(&current_domain->path_list);
114 list_init(&current_domain->subdomain_list);
115
116 list_add_tail(current_list, &current_domain->entry);
117 }
118
119 if(ptr == domain)
120 return current_domain;
121
122 prev_domain = current_domain;
123 current_list = &current_domain->subdomain_list;
124 }
125 }
126
127 static cookie_container_t *get_cookie_container(const WCHAR *domain, const WCHAR *path, BOOL create)
128 {
129 cookie_domain_t *cookie_domain;
130 cookie_container_t *cookie_container, *iter;
131 size_t path_len, len;
132
133 cookie_domain = get_cookie_domain(domain, create);
134 if(!cookie_domain)
135 return NULL;
136
137 path_len = strlenW(path);
138
139 LIST_FOR_EACH_ENTRY(cookie_container, &cookie_domain->path_list, cookie_container_t, entry) {
140 len = strlenW(cookie_container->path);
141 if(len < path_len)
142 break;
143
144 if(!strcmpiW(cookie_container->path, path))
145 return cookie_container;
146 }
147
148 if(!create)
149 return NULL;
150
151 cookie_container = heap_alloc(sizeof(*cookie_container));
152 if(!cookie_container)
153 return NULL;
154
155 cookie_container->path = heap_strdupW(path);
156 if(!cookie_container->path) {
157 heap_free(cookie_container);
158 return NULL;
159 }
160
161 cookie_container->domain = cookie_domain;
162 list_init(&cookie_container->cookie_list);
163
164
165 LIST_FOR_EACH_ENTRY(iter, &cookie_domain->path_list, cookie_container_t, entry) {
166 if(strlenW(iter->path) <= path_len) {
167 list_add_before(&iter->entry, &cookie_container->entry);
168 return cookie_container;
169 }
170 }
171
172 list_add_tail(&cookie_domain->path_list, &cookie_container->entry);
173 return cookie_container;
174 }
175
176 static void delete_cookie(cookie_t *cookie)
177 {
178 list_remove(&cookie->entry);
179
180 heap_free(cookie->name);
181 heap_free(cookie->data);
182 heap_free(cookie);
183 }
184
185 static cookie_t *alloc_cookie(const WCHAR *name, const WCHAR *data, FILETIME expiry, FILETIME create_time, DWORD flags)
186 {
187 cookie_t *new_cookie;
188
189 new_cookie = heap_alloc(sizeof(*new_cookie));
190 if(!new_cookie)
191 return NULL;
192
193 new_cookie->expiry = expiry;
194 new_cookie->create = create_time;
195 new_cookie->flags = flags;
196 list_init(&new_cookie->entry);
197
198 new_cookie->name = heap_strdupW(name);
199 new_cookie->data = heap_strdupW(data);
200 if((name && !new_cookie->name) || (data && !new_cookie->data)) {
201 delete_cookie(new_cookie);
202 return NULL;
203 }
204
205 return new_cookie;
206 }
207
208 static cookie_t *find_cookie(cookie_container_t *container, const WCHAR *name)
209 {
210 cookie_t *iter;
211
212 LIST_FOR_EACH_ENTRY(iter, &container->cookie_list, cookie_t, entry) {
213 if(!strcmpiW(iter->name, name))
214 return iter;
215 }
216
217 return NULL;
218 }
219
220 static void add_cookie(cookie_container_t *container, cookie_t *new_cookie)
221 {
222 TRACE("Adding %s=%s to %s %s\n", debugstr_w(new_cookie->name), debugstr_w(new_cookie->data),
223 debugstr_w(container->domain->domain), debugstr_w(container->path));
224
225 list_add_tail(&container->cookie_list, &new_cookie->entry);
226 new_cookie->container = container;
227 }
228
229 static void replace_cookie(cookie_container_t *container, cookie_t *new_cookie)
230 {
231 cookie_t *old_cookie;
232
233 old_cookie = find_cookie(container, new_cookie->name);
234 if(old_cookie)
235 delete_cookie(old_cookie);
236
237 add_cookie(container, new_cookie);
238 }
239
240 static BOOL cookie_match_path(cookie_container_t *container, const WCHAR *path)
241 {
242 return !strncmpiW(container->path, path, strlenW(container->path));
243 }
244
245 static BOOL create_cookie_url(LPCWSTR domain, LPCWSTR path, WCHAR *buf, DWORD buf_len)
246 {
247 static const WCHAR cookie_prefix[] = {'C','o','o','k','i','e',':'};
248
249 WCHAR *p;
250 DWORD len;
251
252 if(buf_len < sizeof(cookie_prefix)/sizeof(WCHAR))
253 return FALSE;
254 memcpy(buf, cookie_prefix, sizeof(cookie_prefix));
255 buf += sizeof(cookie_prefix)/sizeof(WCHAR);
256 buf_len -= sizeof(cookie_prefix)/sizeof(WCHAR);
257 p = buf;
258
259 len = buf_len;
260 if(!GetUserNameW(buf, &len))
261 return FALSE;
262 buf += len-1;
263 buf_len -= len-1;
264
265 if(!buf_len)
266 return FALSE;
267 *(buf++) = '@';
268 buf_len--;
269
270 len = strlenW(domain);
271 if(len >= buf_len)
272 return FALSE;
273 memcpy(buf, domain, len*sizeof(WCHAR));
274 buf += len;
275 buf_len -= len;
276
277 len = strlenW(path);
278 if(len >= buf_len)
279 return FALSE;
280 memcpy(buf, path, len*sizeof(WCHAR));
281 buf += len;
282
283 *buf = 0;
284
285 for(; *p; p++)
286 *p = tolowerW(*p);
287 return TRUE;
288 }
289
290 static BOOL load_persistent_cookie(LPCWSTR domain, LPCWSTR path)
291 {
292 INTERNET_CACHE_ENTRY_INFOW *info;
293 cookie_container_t *cookie_container;
294 cookie_t *new_cookie;
295 WCHAR cookie_url[MAX_PATH];
296 HANDLE cookie;
297 char *str = NULL, *pbeg, *pend;
298 DWORD size, flags;
299 WCHAR *name, *data;
300 FILETIME expiry, create, time;
301
302 if (!create_cookie_url(domain, path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
303 return FALSE;
304
305 size = 0;
306 RetrieveUrlCacheEntryStreamW(cookie_url, NULL, &size, FALSE, 0);
307 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
308 return TRUE;
309 info = heap_alloc(size);
310 if(!info)
311 return FALSE;
312 cookie = RetrieveUrlCacheEntryStreamW(cookie_url, info, &size, FALSE, 0);
313 size = info->dwSizeLow;
314 heap_free(info);
315 if(!cookie)
316 return FALSE;
317
318 if(!(str = heap_alloc(size+1)) || !ReadUrlCacheEntryStream(cookie, 0, str, &size, 0)) {
319 UnlockUrlCacheEntryStream(cookie, 0);
320 heap_free(str);
321 return FALSE;
322 }
323 str[size] = 0;
324 UnlockUrlCacheEntryStream(cookie, 0);
325
326 cookie_container = get_cookie_container(domain, path, TRUE);
327 if(!cookie_container)
328 return FALSE;
329
330 GetSystemTimeAsFileTime(&time);
331 for(pbeg=str; pbeg && *pbeg; name=data=NULL) {
332 pend = strchr(pbeg, '\n');
333 if(!pend)
334 break;
335 *pend = 0;
336 name = heap_strdupAtoW(pbeg);
337
338 pbeg = pend+1;
339 pend = strchr(pbeg, '\n');
340 if(!pend)
341 break;
342 *pend = 0;
343 data = heap_strdupAtoW(pbeg);
344
345 pbeg = pend+1;
346 pbeg = strchr(pend+1, '\n');
347 if(!pbeg)
348 break;
349 sscanf(pbeg, "%u %u %u %u %u", &flags, &expiry.dwLowDateTime, &expiry.dwHighDateTime,
350 &create.dwLowDateTime, &create.dwHighDateTime);
351
352 /* skip "*\n" */
353 pbeg = strchr(pbeg, '*');
354 if(pbeg) {
355 pbeg++;
356 if(*pbeg)
357 pbeg++;
358 }
359
360 if(!name || !data)
361 break;
362
363 if(CompareFileTime(&time, &expiry) <= 0) {
364 new_cookie = alloc_cookie(NULL, NULL, expiry, create, flags);
365 if(!new_cookie)
366 break;
367
368 new_cookie->name = name;
369 new_cookie->data = data;
370
371 replace_cookie(cookie_container, new_cookie);
372 }else {
373 heap_free(name);
374 heap_free(data);
375 }
376 }
377 heap_free(str);
378 heap_free(name);
379 heap_free(data);
380
381 return TRUE;
382 }
383
384 static BOOL save_persistent_cookie(cookie_container_t *container)
385 {
386 static const WCHAR txtW[] = {'t','x','t',0};
387
388 WCHAR cookie_url[MAX_PATH], cookie_file[MAX_PATH];
389 HANDLE cookie_handle;
390 cookie_t *cookie_container = NULL, *cookie_iter;
391 BOOL do_save = FALSE;
392 char buf[64], *dyn_buf;
393 FILETIME time;
394 DWORD bytes_written;
395
396 if (!create_cookie_url(container->domain->domain, container->path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
397 return FALSE;
398
399 /* check if there's anything to save */
400 GetSystemTimeAsFileTime(&time);
401 LIST_FOR_EACH_ENTRY_SAFE(cookie_container, cookie_iter, &container->cookie_list, cookie_t, entry)
402 {
403 if((cookie_container->expiry.dwLowDateTime || cookie_container->expiry.dwHighDateTime)
404 && CompareFileTime(&time, &cookie_container->expiry) > 0) {
405 delete_cookie(cookie_container);
406 continue;
407 }
408
409 if(!(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)) {
410 do_save = TRUE;
411 break;
412 }
413 }
414 if(!do_save) {
415 DeleteUrlCacheEntryW(cookie_url);
416 return TRUE;
417 }
418
419 if(!CreateUrlCacheEntryW(cookie_url, 0, txtW, cookie_file, 0))
420 return FALSE;
421 cookie_handle = CreateFileW(cookie_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
422 if(cookie_handle == INVALID_HANDLE_VALUE) {
423 DeleteFileW(cookie_file);
424 return FALSE;
425 }
426
427 LIST_FOR_EACH_ENTRY(cookie_container, &container->cookie_list, cookie_t, entry)
428 {
429 if(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)
430 continue;
431
432 dyn_buf = heap_strdupWtoA(cookie_container->name);
433 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
434 heap_free(dyn_buf);
435 do_save = FALSE;
436 break;
437 }
438 heap_free(dyn_buf);
439 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
440 do_save = FALSE;
441 break;
442 }
443
444 dyn_buf = heap_strdupWtoA(cookie_container->data);
445 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
446 heap_free(dyn_buf);
447 do_save = FALSE;
448 break;
449 }
450 heap_free(dyn_buf);
451 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
452 do_save = FALSE;
453 break;
454 }
455
456 dyn_buf = heap_strdupWtoA(container->domain->domain);
457 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
458 heap_free(dyn_buf);
459 do_save = FALSE;
460 break;
461 }
462 heap_free(dyn_buf);
463
464 dyn_buf = heap_strdupWtoA(container->path);
465 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
466 heap_free(dyn_buf);
467 do_save = FALSE;
468 break;
469 }
470 heap_free(dyn_buf);
471
472 sprintf(buf, "\n%u\n%u\n%u\n%u\n%u\n*\n", cookie_container->flags,
473 cookie_container->expiry.dwLowDateTime, cookie_container->expiry.dwHighDateTime,
474 cookie_container->create.dwLowDateTime, cookie_container->create.dwHighDateTime);
475 if(!WriteFile(cookie_handle, buf, strlen(buf), &bytes_written, NULL)) {
476 do_save = FALSE;
477 break;
478 }
479 }
480
481 CloseHandle(cookie_handle);
482 if(!do_save) {
483 ERR("error saving cookie file\n");
484 DeleteFileW(cookie_file);
485 return FALSE;
486 }
487
488 memset(&time, 0, sizeof(time));
489 return CommitUrlCacheEntryW(cookie_url, cookie_file, time, time, 0, NULL, 0, txtW, 0);
490 }
491
492 static BOOL COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen)
493 {
494 URL_COMPONENTSW UrlComponents;
495
496 UrlComponents.lpszExtraInfo = NULL;
497 UrlComponents.lpszPassword = NULL;
498 UrlComponents.lpszScheme = NULL;
499 UrlComponents.lpszUrlPath = path;
500 UrlComponents.lpszUserName = NULL;
501 UrlComponents.lpszHostName = hostName;
502 UrlComponents.dwExtraInfoLength = 0;
503 UrlComponents.dwPasswordLength = 0;
504 UrlComponents.dwSchemeLength = 0;
505 UrlComponents.dwUserNameLength = 0;
506 UrlComponents.dwHostNameLength = hostNameLen;
507 UrlComponents.dwUrlPathLength = pathLen;
508
509 if (!InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents)) return FALSE;
510
511 /* discard the webpage off the end of the path */
512 if (UrlComponents.dwUrlPathLength)
513 {
514 if (path[UrlComponents.dwUrlPathLength - 1] != '/')
515 {
516 WCHAR *ptr;
517 if ((ptr = strrchrW(path, '/'))) *(++ptr) = 0;
518 else
519 {
520 path[0] = '/';
521 path[1] = 0;
522 }
523 }
524 }
525 else if (pathLen >= 2)
526 {
527 path[0] = '/';
528 path[1] = 0;
529 }
530 return TRUE;
531 }
532
533 typedef struct {
534 cookie_t **cookies;
535 unsigned cnt;
536 unsigned size;
537
538 unsigned string_len;
539 } cookie_set_t;
540
541 static DWORD get_cookie(const WCHAR *host, const WCHAR *path, DWORD flags, cookie_set_t *res)
542 {
543 static const WCHAR empty_path[] = { '/',0 };
544
545 WCHAR *ptr, subpath[INTERNET_MAX_PATH_LENGTH];
546 const WCHAR *p;
547 cookie_domain_t *domain;
548 cookie_container_t *container;
549 unsigned len;
550 FILETIME tm;
551
552 GetSystemTimeAsFileTime(&tm);
553
554 len = strlenW(host);
555 p = host+len;
556 while(p>host && p[-1]!='.') p--;
557 while(p != host) {
558 p--;
559 while(p>host && p[-1]!='.') p--;
560 if(p == host) break;
561
562 load_persistent_cookie(p, empty_path);
563 }
564
565 len = strlenW(path);
566 assert(len+1 < INTERNET_MAX_PATH_LENGTH);
567 memcpy(subpath, path, (len+1)*sizeof(WCHAR));
568 ptr = subpath+len;
569 do {
570 *ptr = 0;
571 load_persistent_cookie(host, subpath);
572
573 ptr--;
574 while(ptr>subpath && ptr[-1]!='/') ptr--;
575 }while(ptr != subpath);
576
577 domain = get_cookie_domain(host, FALSE);
578 if(!domain) {
579 TRACE("Unknown host %s\n", debugstr_w(host));
580 return ERROR_NO_MORE_ITEMS;
581 }
582
583 for(domain = get_cookie_domain(host, FALSE); domain; domain = domain->parent) {
584 TRACE("Trying %s domain...\n", debugstr_w(domain->domain));
585
586 LIST_FOR_EACH_ENTRY(container, &domain->path_list, cookie_container_t, entry) {
587 struct list *cursor, *cursor2;
588
589 TRACE("path %s\n", debugstr_w(container->path));
590
591 if(!cookie_match_path(container, path))
592 continue;
593
594 TRACE("found domain %p\n", domain->domain);
595
596 LIST_FOR_EACH_SAFE(cursor, cursor2, &container->cookie_list) {
597 cookie_t *cookie_iter = LIST_ENTRY(cursor, cookie_t, entry);
598
599 /* check for expiry */
600 if((cookie_iter->expiry.dwLowDateTime != 0 || cookie_iter->expiry.dwHighDateTime != 0)
601 && CompareFileTime(&tm, &cookie_iter->expiry) > 0) {
602 TRACE("Found expired cookie. deleting\n");
603 delete_cookie(cookie_iter);
604 continue;
605 }
606
607 if((cookie_iter->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY))
608 continue;
609
610
611 if(!res->size) {
612 res->cookies = heap_alloc(4*sizeof(*res->cookies));
613 if(!res->cookies)
614 continue;
615 res->size = 4;
616 }else if(res->cnt == res->size) {
617 cookie_t **new_cookies = heap_realloc(res->cookies, res->size*2*sizeof(*res->cookies));
618 if(!new_cookies)
619 continue;
620 res->cookies = new_cookies;
621 res->size *= 2;
622 }
623
624 if(res->cnt)
625 res->string_len += 2; /* '; ' */
626 res->cookies[res->cnt++] = cookie_iter;
627
628 res->string_len += strlenW(cookie_iter->name);
629 if(*cookie_iter->data)
630 res->string_len += 1 /* = */ + strlenW(cookie_iter->data);
631 }
632 }
633 }
634
635 return ERROR_SUCCESS;
636 }
637
638 static void cookie_set_to_string(const cookie_set_t *cookie_set, WCHAR *str)
639 {
640 WCHAR *ptr = str;
641 unsigned i, len;
642
643 for(i=0; i<cookie_set->cnt; i++) {
644 if(i) {
645 *ptr++ = ';';
646 *ptr++ = ' ';
647 }
648
649 len = strlenW(cookie_set->cookies[i]->name);
650 memcpy(ptr, cookie_set->cookies[i]->name, len*sizeof(WCHAR));
651 ptr += len;
652
653 if(*cookie_set->cookies[i]->data) {
654 *ptr++ = '=';
655 len = strlenW(cookie_set->cookies[i]->data);
656 memcpy(ptr, cookie_set->cookies[i]->data, len*sizeof(WCHAR));
657 ptr += len;
658 }
659 }
660
661 assert(ptr-str == cookie_set->string_len);
662 TRACE("%s\n", debugstr_wn(str, ptr-str));
663 }
664
665 DWORD get_cookie_header(const WCHAR *host, const WCHAR *path, WCHAR **ret)
666 {
667 cookie_set_t cookie_set = {0};
668 DWORD res;
669
670 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
671
672 EnterCriticalSection(&cookie_cs);
673
674 res = get_cookie(host, path, INTERNET_COOKIE_HTTPONLY, &cookie_set);
675 if(res != ERROR_SUCCESS) {
676 LeaveCriticalSection(&cookie_cs);
677 return res;
678 }
679
680 if(cookie_set.cnt) {
681 WCHAR *header, *ptr;
682
683 ptr = header = heap_alloc(sizeof(cookieW) + (cookie_set.string_len + 3 /* crlf0 */) * sizeof(WCHAR));
684 if(header) {
685 memcpy(ptr, cookieW, sizeof(cookieW));
686 ptr += sizeof(cookieW)/sizeof(*cookieW);
687
688 cookie_set_to_string(&cookie_set, ptr);
689 heap_free(cookie_set.cookies);
690 ptr += cookie_set.string_len;
691
692 *ptr++ = '\r';
693 *ptr++ = '\n';
694 *ptr++ = 0;
695
696 *ret = header;
697 }else {
698 res = ERROR_NOT_ENOUGH_MEMORY;
699 }
700 }else {
701 *ret = NULL;
702 }
703
704 LeaveCriticalSection(&cookie_cs);
705 return ERROR_SUCCESS;
706 }
707
708 /***********************************************************************
709 * InternetGetCookieExW (WININET.@)
710 *
711 * Retrieve cookie from the specified url
712 *
713 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
714 * So it won't be implemented here.
715 *
716 * RETURNS
717 * TRUE on success
718 * FALSE on failure
719 *
720 */
721 BOOL WINAPI InternetGetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
722 LPWSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
723 {
724 WCHAR host[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
725 cookie_set_t cookie_set = {0};
726 DWORD res;
727 BOOL ret;
728
729 TRACE("(%s, %s, %p, %p, %x, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), lpCookieData, lpdwSize, flags, reserved);
730
731 if (flags)
732 FIXME("flags 0x%08x not supported\n", flags);
733
734 if (!lpszUrl)
735 {
736 SetLastError(ERROR_INVALID_PARAMETER);
737 return FALSE;
738 }
739
740 host[0] = 0;
741 ret = COOKIE_crackUrlSimple(lpszUrl, host, sizeof(host)/sizeof(host[0]), path, sizeof(path)/sizeof(path[0]));
742 if (!ret || !host[0]) {
743 SetLastError(ERROR_INVALID_PARAMETER);
744 return FALSE;
745 }
746
747 EnterCriticalSection(&cookie_cs);
748
749 res = get_cookie(host, path, flags, &cookie_set);
750 if(res != ERROR_SUCCESS) {
751 LeaveCriticalSection(&cookie_cs);
752 SetLastError(res);
753 return FALSE;
754 }
755
756 if(cookie_set.cnt) {
757 if(!lpCookieData || cookie_set.string_len+1 > *lpdwSize) {
758 *lpdwSize = (cookie_set.string_len + 1) * sizeof(WCHAR);
759 TRACE("returning %u\n", *lpdwSize);
760 if(lpCookieData) {
761 SetLastError(ERROR_INSUFFICIENT_BUFFER);
762 ret = FALSE;
763 }
764 }else {
765 *lpdwSize = cookie_set.string_len + 1;
766 cookie_set_to_string(&cookie_set, lpCookieData);
767 lpCookieData[cookie_set.string_len] = 0;
768 }
769 }else {
770 TRACE("no cookies found for %s\n", debugstr_w(host));
771 SetLastError(ERROR_NO_MORE_ITEMS);
772 ret = FALSE;
773 }
774
775 heap_free(cookie_set.cookies);
776 LeaveCriticalSection(&cookie_cs);
777 return ret;
778 }
779
780 /***********************************************************************
781 * InternetGetCookieW (WININET.@)
782 *
783 * Retrieve cookie for the specified URL.
784 */
785 BOOL WINAPI InternetGetCookieW(const WCHAR *url, const WCHAR *name, WCHAR *data, DWORD *size)
786 {
787 TRACE("(%s, %s, %s, %p)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data), size);
788
789 return InternetGetCookieExW(url, name, data, size, 0, NULL);
790 }
791
792 /***********************************************************************
793 * InternetGetCookieExA (WININET.@)
794 *
795 * Retrieve cookie from the specified url
796 *
797 * RETURNS
798 * TRUE on success
799 * FALSE on failure
800 *
801 */
802 BOOL WINAPI InternetGetCookieExA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
803 LPSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
804 {
805 WCHAR *url, *name;
806 DWORD len, size;
807 BOOL r;
808
809 TRACE("(%s %s %p %p(%u) %x %p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
810 lpCookieData, lpdwSize, lpdwSize ? *lpdwSize : 0, flags, reserved);
811
812 url = heap_strdupAtoW(lpszUrl);
813 name = heap_strdupAtoW(lpszCookieName);
814
815 r = InternetGetCookieExW( url, name, NULL, &len, flags, reserved );
816 if( r )
817 {
818 WCHAR *szCookieData;
819
820 szCookieData = heap_alloc(len * sizeof(WCHAR));
821 if( !szCookieData )
822 {
823 r = FALSE;
824 }
825 else
826 {
827 r = InternetGetCookieExW( url, name, szCookieData, &len, flags, reserved );
828
829 if(r) {
830 size = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, NULL, 0, NULL, NULL);
831 if(lpCookieData) {
832 if(*lpdwSize >= size) {
833 WideCharToMultiByte( CP_ACP, 0, szCookieData, len, lpCookieData, *lpdwSize, NULL, NULL);
834 }else {
835 SetLastError(ERROR_INSUFFICIENT_BUFFER);
836 r = FALSE;
837 }
838 }
839 *lpdwSize = size;
840 }
841
842 heap_free( szCookieData );
843 }
844 }
845 heap_free( name );
846 heap_free( url );
847 return r;
848 }
849
850 /***********************************************************************
851 * InternetGetCookieA (WININET.@)
852 *
853 * See InternetGetCookieW.
854 */
855 BOOL WINAPI InternetGetCookieA(const char *url, const char *name, char *data, DWORD *size)
856 {
857 TRACE("(%s, %s, %s, %p)\n", debugstr_a(url), debugstr_a(name), debugstr_a(data), size);
858
859 return InternetGetCookieExA(url, name, data, size, 0, NULL);
860 }
861
862 /***********************************************************************
863 * IsDomainLegalCookieDomainW (WININET.@)
864 */
865 BOOL WINAPI IsDomainLegalCookieDomainW( LPCWSTR s1, LPCWSTR s2 )
866 {
867 DWORD s1_len, s2_len;
868
869 FIXME("(%s, %s) semi-stub\n", debugstr_w(s1), debugstr_w(s2));
870
871 if (!s1 || !s2)
872 {
873 SetLastError(ERROR_INVALID_PARAMETER);
874 return FALSE;
875 }
876 if (s1[0] == '.' || !s1[0] || s2[0] == '.' || !s2[0])
877 {
878 SetLastError(ERROR_INVALID_NAME);
879 return FALSE;
880 }
881 if(!strchrW(s1, '.') || !strchrW(s2, '.'))
882 return FALSE;
883
884 s1_len = strlenW(s1);
885 s2_len = strlenW(s2);
886 if (s1_len > s2_len)
887 return FALSE;
888
889 if (strncmpiW(s1, s2+s2_len-s1_len, s1_len) || (s2_len>s1_len && s2[s2_len-s1_len-1]!='.'))
890 {
891 SetLastError(ERROR_INVALID_PARAMETER);
892 return FALSE;
893 }
894
895 return TRUE;
896 }
897
898 DWORD set_cookie(const WCHAR *domain, const WCHAR *path, const WCHAR *cookie_name, const WCHAR *cookie_data, DWORD flags)
899 {
900 cookie_container_t *container;
901 cookie_t *thisCookie;
902 LPWSTR data, value;
903 WCHAR *ptr;
904 FILETIME expiry, create;
905 BOOL expired = FALSE, update_persistent = FALSE;
906 DWORD cookie_flags = 0;
907
908 TRACE("%s %s %s=%s %x\n", debugstr_w(domain), debugstr_w(path), debugstr_w(cookie_name), debugstr_w(cookie_data), flags);
909
910 value = data = heap_strdupW(cookie_data);
911 if (!data)
912 {
913 ERR("could not allocate the cookie data buffer\n");
914 return COOKIE_STATE_UNKNOWN;
915 }
916
917 memset(&expiry,0,sizeof(expiry));
918 GetSystemTimeAsFileTime(&create);
919
920 /* lots of information can be parsed out of the cookie value */
921
922 ptr = data;
923 for (;;)
924 {
925 static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
926 static const WCHAR szPath[] = {'p','a','t','h','=',0};
927 static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','=',0};
928 static const WCHAR szSecure[] = {'s','e','c','u','r','e',0};
929 static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y',0};
930
931 if (!(ptr = strchrW(ptr,';'))) break;
932 *ptr++ = 0;
933
934 if (value != data) heap_free(value);
935 value = heap_alloc((ptr - data) * sizeof(WCHAR));
936 if (value == NULL)
937 {
938 heap_free(data);
939 ERR("could not allocate the cookie value buffer\n");
940 return COOKIE_STATE_UNKNOWN;
941 }
942 strcpyW(value, data);
943
944 while (*ptr == ' ') ptr++; /* whitespace */
945
946 if (strncmpiW(ptr, szDomain, 7) == 0)
947 {
948 WCHAR *end_ptr;
949
950 ptr += sizeof(szDomain)/sizeof(szDomain[0])-1;
951 if(*ptr == '.')
952 ptr++;
953 end_ptr = strchrW(ptr, ';');
954 if(end_ptr)
955 *end_ptr = 0;
956
957 if(!IsDomainLegalCookieDomainW(ptr, domain))
958 {
959 if(value != data)
960 heap_free(value);
961 heap_free(data);
962 return COOKIE_STATE_UNKNOWN;
963 }
964
965 if(end_ptr)
966 *end_ptr = ';';
967
968 domain = ptr;
969 TRACE("Parsing new domain %s\n",debugstr_w(domain));
970 }
971 else if (strncmpiW(ptr, szPath, 5) == 0)
972 {
973 ptr+=strlenW(szPath);
974 path = ptr;
975 TRACE("Parsing new path %s\n",debugstr_w(path));
976 }
977 else if (strncmpiW(ptr, szExpires, 8) == 0)
978 {
979 SYSTEMTIME st;
980 ptr+=strlenW(szExpires);
981 if (InternetTimeToSystemTimeW(ptr, &st, 0))
982 {
983 SystemTimeToFileTime(&st, &expiry);
984
985 if (CompareFileTime(&create,&expiry) > 0)
986 {
987 TRACE("Cookie already expired.\n");
988 expired = TRUE;
989 }
990 }
991 }
992 else if (strncmpiW(ptr, szSecure, 6) == 0)
993 {
994 FIXME("secure not handled (%s)\n",debugstr_w(ptr));
995 ptr += strlenW(szSecure);
996 }
997 else if (strncmpiW(ptr, szHttpOnly, 8) == 0)
998 {
999 if(!(flags & INTERNET_COOKIE_HTTPONLY)) {
1000 WARN("HTTP only cookie added without INTERNET_COOKIE_HTTPONLY flag\n");
1001 heap_free(data);
1002 if (value != data) heap_free(value);
1003 SetLastError(ERROR_INVALID_OPERATION);
1004 return COOKIE_STATE_REJECT;
1005 }
1006
1007 cookie_flags |= INTERNET_COOKIE_HTTPONLY;
1008 ptr += strlenW(szHttpOnly);
1009 }
1010 else if (*ptr)
1011 {
1012 FIXME("Unknown additional option %s\n",debugstr_w(ptr));
1013 break;
1014 }
1015 }
1016
1017 EnterCriticalSection(&cookie_cs);
1018
1019 load_persistent_cookie(domain, path);
1020
1021 container = get_cookie_container(domain, path, !expired);
1022 if(!container) {
1023 heap_free(data);
1024 if (value != data) heap_free(value);
1025 LeaveCriticalSection(&cookie_cs);
1026 return COOKIE_STATE_ACCEPT;
1027 }
1028
1029 if(!expiry.dwLowDateTime && !expiry.dwHighDateTime)
1030 cookie_flags |= INTERNET_COOKIE_IS_SESSION;
1031 else
1032 update_persistent = TRUE;
1033
1034 if ((thisCookie = find_cookie(container, cookie_name)))
1035 {
1036 if ((thisCookie->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) {
1037 WARN("An attempt to override httponly cookie\n");
1038 SetLastError(ERROR_INVALID_OPERATION);
1039 heap_free(data);
1040 if (value != data) heap_free(value);
1041 return COOKIE_STATE_REJECT;
1042 }
1043
1044 if (!(thisCookie->flags & INTERNET_COOKIE_IS_SESSION))
1045 update_persistent = TRUE;
1046 delete_cookie(thisCookie);
1047 }
1048
1049 TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_w(cookie_name),
1050 debugstr_w(value), debugstr_w(container->domain->domain),debugstr_w(container->path));
1051
1052 if (!expired) {
1053 cookie_t *new_cookie;
1054
1055 new_cookie = alloc_cookie(cookie_name, value, expiry, create, cookie_flags);
1056 if(!new_cookie) {
1057 heap_free(data);
1058 if (value != data) heap_free(value);
1059 LeaveCriticalSection(&cookie_cs);
1060 return COOKIE_STATE_UNKNOWN;
1061 }
1062
1063 add_cookie(container, new_cookie);
1064 }
1065 heap_free(data);
1066 if (value != data) heap_free(value);
1067
1068 if (!update_persistent || save_persistent_cookie(container))
1069 {
1070 LeaveCriticalSection(&cookie_cs);
1071 return COOKIE_STATE_ACCEPT;
1072 }
1073 LeaveCriticalSection(&cookie_cs);
1074 return COOKIE_STATE_UNKNOWN;
1075 }
1076
1077 /***********************************************************************
1078 * InternetSetCookieExW (WININET.@)
1079 *
1080 * Sets cookie for the specified url
1081 */
1082 DWORD WINAPI InternetSetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
1083 LPCWSTR lpCookieData, DWORD flags, DWORD_PTR reserved)
1084 {
1085 BOOL ret;
1086 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
1087
1088 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_w(lpszUrl), debugstr_w(lpszCookieName),
1089 debugstr_w(lpCookieData), flags, reserved);
1090
1091 if (flags & ~INTERNET_COOKIE_HTTPONLY)
1092 FIXME("flags %x not supported\n", flags);
1093
1094 if (!lpszUrl || !lpCookieData)
1095 {
1096 SetLastError(ERROR_INVALID_PARAMETER);
1097 return COOKIE_STATE_UNKNOWN;
1098 }
1099
1100 hostName[0] = 0;
1101 ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
1102 if (!ret || !hostName[0]) return COOKIE_STATE_UNKNOWN;
1103
1104 if (!lpszCookieName)
1105 {
1106 WCHAR *cookie, *data;
1107 DWORD res;
1108
1109 cookie = heap_strdupW(lpCookieData);
1110 if (!cookie)
1111 {
1112 SetLastError(ERROR_OUTOFMEMORY);
1113 return COOKIE_STATE_UNKNOWN;
1114 }
1115
1116 /* some apps (or is it us??) try to add a cookie with no cookie name, but
1117 * the cookie data in the form of name[=data].
1118 */
1119 if (!(data = strchrW(cookie, '='))) data = cookie + strlenW(cookie);
1120 else *data++ = 0;
1121
1122 res = set_cookie(hostName, path, cookie, data, flags);
1123
1124 heap_free(cookie);
1125 return res;
1126 }
1127 return set_cookie(hostName, path, lpszCookieName, lpCookieData, flags);
1128 }
1129
1130 /***********************************************************************
1131 * InternetSetCookieW (WININET.@)
1132 *
1133 * Sets a cookie for the specified URL.
1134 */
1135 BOOL WINAPI InternetSetCookieW(const WCHAR *url, const WCHAR *name, const WCHAR *data)
1136 {
1137 TRACE("(%s, %s, %s)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data));
1138
1139 return InternetSetCookieExW(url, name, data, 0, 0) == COOKIE_STATE_ACCEPT;
1140 }
1141
1142 /***********************************************************************
1143 * InternetSetCookieA (WININET.@)
1144 *
1145 * Sets cookie for the specified url
1146 *
1147 * RETURNS
1148 * TRUE on success
1149 * FALSE on failure
1150 *
1151 */
1152 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
1153 LPCSTR lpCookieData)
1154 {
1155 LPWSTR data, url, name;
1156 BOOL r;
1157
1158 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
1159 debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
1160
1161 url = heap_strdupAtoW(lpszUrl);
1162 name = heap_strdupAtoW(lpszCookieName);
1163 data = heap_strdupAtoW(lpCookieData);
1164
1165 r = InternetSetCookieW( url, name, data );
1166
1167 heap_free( data );
1168 heap_free( name );
1169 heap_free( url );
1170 return r;
1171 }
1172
1173 /***********************************************************************
1174 * InternetSetCookieExA (WININET.@)
1175 *
1176 * See InternetSetCookieExW.
1177 */
1178 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData,
1179 DWORD dwFlags, DWORD_PTR dwReserved)
1180 {
1181 WCHAR *data, *url, *name;
1182 DWORD r;
1183
1184 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_a(lpszURL), debugstr_a(lpszCookieName),
1185 debugstr_a(lpszCookieData), dwFlags, dwReserved);
1186
1187 url = heap_strdupAtoW(lpszURL);
1188 name = heap_strdupAtoW(lpszCookieName);
1189 data = heap_strdupAtoW(lpszCookieData);
1190
1191 r = InternetSetCookieExW(url, name, data, dwFlags, dwReserved);
1192
1193 heap_free( data );
1194 heap_free( name );
1195 heap_free( url );
1196 return r;
1197 }
1198
1199 /***********************************************************************
1200 * InternetClearAllPerSiteCookieDecisions (WININET.@)
1201 *
1202 * Clears all per-site decisions about cookies.
1203 *
1204 * RETURNS
1205 * TRUE on success
1206 * FALSE on failure
1207 *
1208 */
1209 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID )
1210 {
1211 FIXME("stub\n");
1212 return TRUE;
1213 }
1214
1215 /***********************************************************************
1216 * InternetEnumPerSiteCookieDecisionA (WININET.@)
1217 *
1218 * See InternetEnumPerSiteCookieDecisionW.
1219 */
1220 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, ULONG *pcSiteNameSize,
1221 ULONG *pdwDecision, ULONG dwIndex )
1222 {
1223 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1224 debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1225 return FALSE;
1226 }
1227
1228 /***********************************************************************
1229 * InternetEnumPerSiteCookieDecisionW (WININET.@)
1230 *
1231 * Enumerates all per-site decisions about cookies.
1232 *
1233 * RETURNS
1234 * TRUE on success
1235 * FALSE on failure
1236 *
1237 */
1238 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, ULONG *pcSiteNameSize,
1239 ULONG *pdwDecision, ULONG dwIndex )
1240 {
1241 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1242 debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1243 return FALSE;
1244 }
1245
1246 /***********************************************************************
1247 * InternetGetPerSiteCookieDecisionA (WININET.@)
1248 */
1249 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, ULONG *pResult )
1250 {
1251 FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult);
1252 return FALSE;
1253 }
1254
1255 /***********************************************************************
1256 * InternetGetPerSiteCookieDecisionW (WININET.@)
1257 */
1258 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, ULONG *pResult )
1259 {
1260 FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult);
1261 return FALSE;
1262 }
1263
1264 /***********************************************************************
1265 * InternetSetPerSiteCookieDecisionA (WININET.@)
1266 */
1267 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision )
1268 {
1269 FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision);
1270 return FALSE;
1271 }
1272
1273 /***********************************************************************
1274 * InternetSetPerSiteCookieDecisionW (WININET.@)
1275 */
1276 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision )
1277 {
1278 FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision);
1279 return FALSE;
1280 }
1281
1282 void free_cookie(void)
1283 {
1284 DeleteCriticalSection(&cookie_cs);
1285 }