[WININET] Sync with Wine Staging 1.9.4. CORE-10912
[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 heap_free(str);
329 return FALSE;
330 }
331
332 GetSystemTimeAsFileTime(&time);
333 for(pbeg=str; pbeg && *pbeg; name=data=NULL) {
334 pend = strchr(pbeg, '\n');
335 if(!pend)
336 break;
337 *pend = 0;
338 name = heap_strdupAtoW(pbeg);
339
340 pbeg = pend+1;
341 pend = strchr(pbeg, '\n');
342 if(!pend)
343 break;
344 *pend = 0;
345 data = heap_strdupAtoW(pbeg);
346
347 pbeg = strchr(pend+1, '\n');
348 if(!pbeg)
349 break;
350 sscanf(pbeg, "%u %u %u %u %u", &flags, &expiry.dwLowDateTime, &expiry.dwHighDateTime,
351 &create.dwLowDateTime, &create.dwHighDateTime);
352
353 /* skip "*\n" */
354 pbeg = strchr(pbeg, '*');
355 if(pbeg) {
356 pbeg++;
357 if(*pbeg)
358 pbeg++;
359 }
360
361 if(!name || !data)
362 break;
363
364 if(CompareFileTime(&time, &expiry) <= 0) {
365 new_cookie = alloc_cookie(NULL, NULL, expiry, create, flags);
366 if(!new_cookie)
367 break;
368
369 new_cookie->name = name;
370 new_cookie->data = data;
371
372 replace_cookie(cookie_container, new_cookie);
373 }else {
374 heap_free(name);
375 heap_free(data);
376 }
377 }
378 heap_free(str);
379 heap_free(name);
380 heap_free(data);
381
382 return TRUE;
383 }
384
385 static BOOL save_persistent_cookie(cookie_container_t *container)
386 {
387 static const WCHAR txtW[] = {'t','x','t',0};
388
389 WCHAR cookie_url[MAX_PATH], cookie_file[MAX_PATH];
390 HANDLE cookie_handle;
391 cookie_t *cookie_container = NULL, *cookie_iter;
392 BOOL do_save = FALSE;
393 char buf[64], *dyn_buf;
394 FILETIME time;
395 DWORD bytes_written;
396
397 if (!create_cookie_url(container->domain->domain, container->path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
398 return FALSE;
399
400 /* check if there's anything to save */
401 GetSystemTimeAsFileTime(&time);
402 LIST_FOR_EACH_ENTRY_SAFE(cookie_container, cookie_iter, &container->cookie_list, cookie_t, entry)
403 {
404 if((cookie_container->expiry.dwLowDateTime || cookie_container->expiry.dwHighDateTime)
405 && CompareFileTime(&time, &cookie_container->expiry) > 0) {
406 delete_cookie(cookie_container);
407 continue;
408 }
409
410 if(!(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)) {
411 do_save = TRUE;
412 break;
413 }
414 }
415 if(!do_save) {
416 DeleteUrlCacheEntryW(cookie_url);
417 return TRUE;
418 }
419
420 if(!CreateUrlCacheEntryW(cookie_url, 0, txtW, cookie_file, 0))
421 return FALSE;
422 cookie_handle = CreateFileW(cookie_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
423 if(cookie_handle == INVALID_HANDLE_VALUE) {
424 DeleteFileW(cookie_file);
425 return FALSE;
426 }
427
428 LIST_FOR_EACH_ENTRY(cookie_container, &container->cookie_list, cookie_t, entry)
429 {
430 if(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)
431 continue;
432
433 dyn_buf = heap_strdupWtoA(cookie_container->name);
434 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
435 heap_free(dyn_buf);
436 do_save = FALSE;
437 break;
438 }
439 heap_free(dyn_buf);
440 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
441 do_save = FALSE;
442 break;
443 }
444
445 dyn_buf = heap_strdupWtoA(cookie_container->data);
446 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
447 heap_free(dyn_buf);
448 do_save = FALSE;
449 break;
450 }
451 heap_free(dyn_buf);
452 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
453 do_save = FALSE;
454 break;
455 }
456
457 dyn_buf = heap_strdupWtoA(container->domain->domain);
458 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
459 heap_free(dyn_buf);
460 do_save = FALSE;
461 break;
462 }
463 heap_free(dyn_buf);
464
465 dyn_buf = heap_strdupWtoA(container->path);
466 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
467 heap_free(dyn_buf);
468 do_save = FALSE;
469 break;
470 }
471 heap_free(dyn_buf);
472
473 sprintf(buf, "\n%u\n%u\n%u\n%u\n%u\n*\n", cookie_container->flags,
474 cookie_container->expiry.dwLowDateTime, cookie_container->expiry.dwHighDateTime,
475 cookie_container->create.dwLowDateTime, cookie_container->create.dwHighDateTime);
476 if(!WriteFile(cookie_handle, buf, strlen(buf), &bytes_written, NULL)) {
477 do_save = FALSE;
478 break;
479 }
480 }
481
482 CloseHandle(cookie_handle);
483 if(!do_save) {
484 ERR("error saving cookie file\n");
485 DeleteFileW(cookie_file);
486 return FALSE;
487 }
488
489 memset(&time, 0, sizeof(time));
490 return CommitUrlCacheEntryW(cookie_url, cookie_file, time, time, 0, NULL, 0, txtW, 0);
491 }
492
493 static BOOL COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen)
494 {
495 URL_COMPONENTSW UrlComponents;
496
497 UrlComponents.lpszExtraInfo = NULL;
498 UrlComponents.lpszPassword = NULL;
499 UrlComponents.lpszScheme = NULL;
500 UrlComponents.lpszUrlPath = path;
501 UrlComponents.lpszUserName = NULL;
502 UrlComponents.lpszHostName = hostName;
503 UrlComponents.dwExtraInfoLength = 0;
504 UrlComponents.dwPasswordLength = 0;
505 UrlComponents.dwSchemeLength = 0;
506 UrlComponents.dwUserNameLength = 0;
507 UrlComponents.dwHostNameLength = hostNameLen;
508 UrlComponents.dwUrlPathLength = pathLen;
509
510 if (!InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents)) return FALSE;
511
512 /* discard the webpage off the end of the path */
513 if (UrlComponents.dwUrlPathLength)
514 {
515 if (path[UrlComponents.dwUrlPathLength - 1] != '/')
516 {
517 WCHAR *ptr;
518 if ((ptr = strrchrW(path, '/'))) *(++ptr) = 0;
519 else
520 {
521 path[0] = '/';
522 path[1] = 0;
523 }
524 }
525 }
526 else if (pathLen >= 2)
527 {
528 path[0] = '/';
529 path[1] = 0;
530 }
531 return TRUE;
532 }
533
534 typedef struct {
535 cookie_t **cookies;
536 unsigned cnt;
537 unsigned size;
538
539 unsigned string_len;
540 } cookie_set_t;
541
542 static DWORD get_cookie(const WCHAR *host, const WCHAR *path, DWORD flags, cookie_set_t *res)
543 {
544 static const WCHAR empty_path[] = { '/',0 };
545
546 WCHAR *ptr, subpath[INTERNET_MAX_PATH_LENGTH];
547 const WCHAR *p;
548 cookie_domain_t *domain;
549 cookie_container_t *container;
550 unsigned len;
551 FILETIME tm;
552
553 GetSystemTimeAsFileTime(&tm);
554
555 len = strlenW(host);
556 p = host+len;
557 while(p>host && p[-1]!='.') p--;
558 while(p != host) {
559 p--;
560 while(p>host && p[-1]!='.') p--;
561 if(p == host) break;
562
563 load_persistent_cookie(p, empty_path);
564 }
565
566 len = strlenW(path);
567 assert(len+1 < INTERNET_MAX_PATH_LENGTH);
568 memcpy(subpath, path, (len+1)*sizeof(WCHAR));
569 ptr = subpath+len;
570 do {
571 *ptr = 0;
572 load_persistent_cookie(host, subpath);
573
574 ptr--;
575 while(ptr>subpath && ptr[-1]!='/') ptr--;
576 }while(ptr != subpath);
577
578 domain = get_cookie_domain(host, FALSE);
579 if(!domain) {
580 TRACE("Unknown host %s\n", debugstr_w(host));
581 return ERROR_NO_MORE_ITEMS;
582 }
583
584 for(domain = get_cookie_domain(host, FALSE); domain; domain = domain->parent) {
585 TRACE("Trying %s domain...\n", debugstr_w(domain->domain));
586
587 LIST_FOR_EACH_ENTRY(container, &domain->path_list, cookie_container_t, entry) {
588 struct list *cursor, *cursor2;
589
590 TRACE("path %s\n", debugstr_w(container->path));
591
592 if(!cookie_match_path(container, path))
593 continue;
594
595 TRACE("found domain %p\n", domain->domain);
596
597 LIST_FOR_EACH_SAFE(cursor, cursor2, &container->cookie_list) {
598 cookie_t *cookie_iter = LIST_ENTRY(cursor, cookie_t, entry);
599
600 /* check for expiry */
601 if((cookie_iter->expiry.dwLowDateTime != 0 || cookie_iter->expiry.dwHighDateTime != 0)
602 && CompareFileTime(&tm, &cookie_iter->expiry) > 0) {
603 TRACE("Found expired cookie. deleting\n");
604 delete_cookie(cookie_iter);
605 continue;
606 }
607
608 if((cookie_iter->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY))
609 continue;
610
611
612 if(!res->size) {
613 res->cookies = heap_alloc(4*sizeof(*res->cookies));
614 if(!res->cookies)
615 continue;
616 res->size = 4;
617 }else if(res->cnt == res->size) {
618 cookie_t **new_cookies = heap_realloc(res->cookies, res->size*2*sizeof(*res->cookies));
619 if(!new_cookies)
620 continue;
621 res->cookies = new_cookies;
622 res->size *= 2;
623 }
624
625 if(res->cnt)
626 res->string_len += 2; /* '; ' */
627 res->cookies[res->cnt++] = cookie_iter;
628
629 res->string_len += strlenW(cookie_iter->name);
630 if(*cookie_iter->data)
631 res->string_len += 1 /* = */ + strlenW(cookie_iter->data);
632 }
633 }
634 }
635
636 return ERROR_SUCCESS;
637 }
638
639 static void cookie_set_to_string(const cookie_set_t *cookie_set, WCHAR *str)
640 {
641 WCHAR *ptr = str;
642 unsigned i, len;
643
644 for(i=0; i<cookie_set->cnt; i++) {
645 if(i) {
646 *ptr++ = ';';
647 *ptr++ = ' ';
648 }
649
650 len = strlenW(cookie_set->cookies[i]->name);
651 memcpy(ptr, cookie_set->cookies[i]->name, len*sizeof(WCHAR));
652 ptr += len;
653
654 if(*cookie_set->cookies[i]->data) {
655 *ptr++ = '=';
656 len = strlenW(cookie_set->cookies[i]->data);
657 memcpy(ptr, cookie_set->cookies[i]->data, len*sizeof(WCHAR));
658 ptr += len;
659 }
660 }
661
662 assert(ptr-str == cookie_set->string_len);
663 TRACE("%s\n", debugstr_wn(str, ptr-str));
664 }
665
666 DWORD get_cookie_header(const WCHAR *host, const WCHAR *path, WCHAR **ret)
667 {
668 cookie_set_t cookie_set = {0};
669 DWORD res;
670
671 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
672
673 EnterCriticalSection(&cookie_cs);
674
675 res = get_cookie(host, path, INTERNET_COOKIE_HTTPONLY, &cookie_set);
676 if(res != ERROR_SUCCESS) {
677 LeaveCriticalSection(&cookie_cs);
678 return res;
679 }
680
681 if(cookie_set.cnt) {
682 WCHAR *header, *ptr;
683
684 ptr = header = heap_alloc(sizeof(cookieW) + (cookie_set.string_len + 3 /* crlf0 */) * sizeof(WCHAR));
685 if(header) {
686 memcpy(ptr, cookieW, sizeof(cookieW));
687 ptr += sizeof(cookieW)/sizeof(*cookieW);
688
689 cookie_set_to_string(&cookie_set, ptr);
690 heap_free(cookie_set.cookies);
691 ptr += cookie_set.string_len;
692
693 *ptr++ = '\r';
694 *ptr++ = '\n';
695 *ptr++ = 0;
696
697 *ret = header;
698 }else {
699 res = ERROR_NOT_ENOUGH_MEMORY;
700 }
701 }else {
702 *ret = NULL;
703 }
704
705 LeaveCriticalSection(&cookie_cs);
706 return res;
707 }
708
709 /***********************************************************************
710 * InternetGetCookieExW (WININET.@)
711 *
712 * Retrieve cookie from the specified url
713 *
714 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
715 * So it won't be implemented here.
716 *
717 * RETURNS
718 * TRUE on success
719 * FALSE on failure
720 *
721 */
722 BOOL WINAPI InternetGetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
723 LPWSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
724 {
725 WCHAR host[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
726 cookie_set_t cookie_set = {0};
727 DWORD res;
728 BOOL ret;
729
730 TRACE("(%s, %s, %p, %p, %x, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), lpCookieData, lpdwSize, flags, reserved);
731
732 if (flags)
733 FIXME("flags 0x%08x not supported\n", flags);
734
735 if (!lpszUrl)
736 {
737 SetLastError(ERROR_INVALID_PARAMETER);
738 return FALSE;
739 }
740
741 host[0] = 0;
742 ret = COOKIE_crackUrlSimple(lpszUrl, host, sizeof(host)/sizeof(host[0]), path, sizeof(path)/sizeof(path[0]));
743 if (!ret || !host[0]) {
744 SetLastError(ERROR_INVALID_PARAMETER);
745 return FALSE;
746 }
747
748 EnterCriticalSection(&cookie_cs);
749
750 res = get_cookie(host, path, flags, &cookie_set);
751 if(res != ERROR_SUCCESS) {
752 LeaveCriticalSection(&cookie_cs);
753 SetLastError(res);
754 return FALSE;
755 }
756
757 if(cookie_set.cnt) {
758 if(!lpCookieData || cookie_set.string_len+1 > *lpdwSize) {
759 *lpdwSize = (cookie_set.string_len + 1) * sizeof(WCHAR);
760 TRACE("returning %u\n", *lpdwSize);
761 if(lpCookieData) {
762 SetLastError(ERROR_INSUFFICIENT_BUFFER);
763 ret = FALSE;
764 }
765 }else {
766 *lpdwSize = cookie_set.string_len + 1;
767 cookie_set_to_string(&cookie_set, lpCookieData);
768 lpCookieData[cookie_set.string_len] = 0;
769 }
770 }else {
771 TRACE("no cookies found for %s\n", debugstr_w(host));
772 SetLastError(ERROR_NO_MORE_ITEMS);
773 ret = FALSE;
774 }
775
776 heap_free(cookie_set.cookies);
777 LeaveCriticalSection(&cookie_cs);
778 return ret;
779 }
780
781 /***********************************************************************
782 * InternetGetCookieW (WININET.@)
783 *
784 * Retrieve cookie for the specified URL.
785 */
786 BOOL WINAPI InternetGetCookieW(const WCHAR *url, const WCHAR *name, WCHAR *data, DWORD *size)
787 {
788 TRACE("(%s, %s, %s, %p)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data), size);
789
790 return InternetGetCookieExW(url, name, data, size, 0, NULL);
791 }
792
793 /***********************************************************************
794 * InternetGetCookieExA (WININET.@)
795 *
796 * Retrieve cookie from the specified url
797 *
798 * RETURNS
799 * TRUE on success
800 * FALSE on failure
801 *
802 */
803 BOOL WINAPI InternetGetCookieExA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
804 LPSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
805 {
806 WCHAR *url, *name;
807 DWORD len, size;
808 BOOL r;
809
810 TRACE("(%s %s %p %p(%u) %x %p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
811 lpCookieData, lpdwSize, lpdwSize ? *lpdwSize : 0, flags, reserved);
812
813 url = heap_strdupAtoW(lpszUrl);
814 name = heap_strdupAtoW(lpszCookieName);
815
816 r = InternetGetCookieExW( url, name, NULL, &len, flags, reserved );
817 if( r )
818 {
819 WCHAR *szCookieData;
820
821 szCookieData = heap_alloc(len * sizeof(WCHAR));
822 if( !szCookieData )
823 {
824 r = FALSE;
825 }
826 else
827 {
828 r = InternetGetCookieExW( url, name, szCookieData, &len, flags, reserved );
829
830 if(r) {
831 size = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, NULL, 0, NULL, NULL);
832 if(lpCookieData) {
833 if(*lpdwSize >= size) {
834 WideCharToMultiByte( CP_ACP, 0, szCookieData, len, lpCookieData, *lpdwSize, NULL, NULL);
835 }else {
836 SetLastError(ERROR_INSUFFICIENT_BUFFER);
837 r = FALSE;
838 }
839 }
840 *lpdwSize = size;
841 }
842
843 heap_free( szCookieData );
844 }
845 }
846 heap_free( name );
847 heap_free( url );
848 return r;
849 }
850
851 /***********************************************************************
852 * InternetGetCookieA (WININET.@)
853 *
854 * See InternetGetCookieW.
855 */
856 BOOL WINAPI InternetGetCookieA(const char *url, const char *name, char *data, DWORD *size)
857 {
858 TRACE("(%s, %s, %s, %p)\n", debugstr_a(url), debugstr_a(name), debugstr_a(data), size);
859
860 return InternetGetCookieExA(url, name, data, size, 0, NULL);
861 }
862
863 /***********************************************************************
864 * IsDomainLegalCookieDomainW (WININET.@)
865 */
866 BOOL WINAPI IsDomainLegalCookieDomainW( LPCWSTR s1, LPCWSTR s2 )
867 {
868 DWORD s1_len, s2_len;
869
870 FIXME("(%s, %s) semi-stub\n", debugstr_w(s1), debugstr_w(s2));
871
872 if (!s1 || !s2)
873 {
874 SetLastError(ERROR_INVALID_PARAMETER);
875 return FALSE;
876 }
877 if (s1[0] == '.' || !s1[0] || s2[0] == '.' || !s2[0])
878 {
879 SetLastError(ERROR_INVALID_NAME);
880 return FALSE;
881 }
882 if(!strchrW(s1, '.') || !strchrW(s2, '.'))
883 return FALSE;
884
885 s1_len = strlenW(s1);
886 s2_len = strlenW(s2);
887 if (s1_len > s2_len)
888 return FALSE;
889
890 if (strncmpiW(s1, s2+s2_len-s1_len, s1_len) || (s2_len>s1_len && s2[s2_len-s1_len-1]!='.'))
891 {
892 SetLastError(ERROR_INVALID_PARAMETER);
893 return FALSE;
894 }
895
896 return TRUE;
897 }
898
899 DWORD set_cookie(const WCHAR *domain, const WCHAR *path, const WCHAR *cookie_name, const WCHAR *cookie_data, DWORD flags)
900 {
901 cookie_container_t *container;
902 cookie_t *thisCookie;
903 LPWSTR data, value;
904 WCHAR *ptr;
905 FILETIME expiry, create;
906 BOOL expired = FALSE, update_persistent = FALSE;
907 DWORD cookie_flags = 0;
908
909 TRACE("%s %s %s=%s %x\n", debugstr_w(domain), debugstr_w(path), debugstr_w(cookie_name), debugstr_w(cookie_data), flags);
910
911 value = data = heap_strdupW(cookie_data);
912 if (!data)
913 {
914 ERR("could not allocate the cookie data buffer\n");
915 return COOKIE_STATE_UNKNOWN;
916 }
917
918 memset(&expiry,0,sizeof(expiry));
919 GetSystemTimeAsFileTime(&create);
920
921 /* lots of information can be parsed out of the cookie value */
922
923 ptr = data;
924 for (;;)
925 {
926 static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
927 static const WCHAR szPath[] = {'p','a','t','h','=',0};
928 static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','=',0};
929 static const WCHAR szSecure[] = {'s','e','c','u','r','e',0};
930 static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y',0};
931 static const WCHAR szVersion[] = {'v','e','r','s','i','o','n','=',0};
932
933 if (!(ptr = strchrW(ptr,';'))) break;
934 *ptr++ = 0;
935
936 if (value != data) heap_free(value);
937 value = heap_alloc((ptr - data) * sizeof(WCHAR));
938 if (value == NULL)
939 {
940 heap_free(data);
941 ERR("could not allocate the cookie value buffer\n");
942 return COOKIE_STATE_UNKNOWN;
943 }
944 strcpyW(value, data);
945
946 while (*ptr == ' ') ptr++; /* whitespace */
947
948 if (strncmpiW(ptr, szDomain, 7) == 0)
949 {
950 WCHAR *end_ptr;
951
952 ptr += sizeof(szDomain)/sizeof(szDomain[0])-1;
953 if(*ptr == '.')
954 ptr++;
955 end_ptr = strchrW(ptr, ';');
956 if(end_ptr)
957 *end_ptr = 0;
958
959 if(!IsDomainLegalCookieDomainW(ptr, domain))
960 {
961 if(value != data)
962 heap_free(value);
963 heap_free(data);
964 return COOKIE_STATE_UNKNOWN;
965 }
966
967 if(end_ptr)
968 *end_ptr = ';';
969
970 domain = ptr;
971 TRACE("Parsing new domain %s\n",debugstr_w(domain));
972 }
973 else if (strncmpiW(ptr, szPath, 5) == 0)
974 {
975 ptr+=strlenW(szPath);
976 path = ptr;
977 TRACE("Parsing new path %s\n",debugstr_w(path));
978 }
979 else if (strncmpiW(ptr, szExpires, 8) == 0)
980 {
981 SYSTEMTIME st;
982 ptr+=strlenW(szExpires);
983 if (InternetTimeToSystemTimeW(ptr, &st, 0))
984 {
985 SystemTimeToFileTime(&st, &expiry);
986
987 if (CompareFileTime(&create,&expiry) > 0)
988 {
989 TRACE("Cookie already expired.\n");
990 expired = TRUE;
991 }
992 }
993 }
994 else if (strncmpiW(ptr, szSecure, 6) == 0)
995 {
996 FIXME("secure not handled (%s)\n",debugstr_w(ptr));
997 ptr += strlenW(szSecure);
998 }
999 else if (strncmpiW(ptr, szHttpOnly, 8) == 0)
1000 {
1001 if(!(flags & INTERNET_COOKIE_HTTPONLY)) {
1002 WARN("HTTP only cookie added without INTERNET_COOKIE_HTTPONLY flag\n");
1003 heap_free(data);
1004 if (value != data) heap_free(value);
1005 SetLastError(ERROR_INVALID_OPERATION);
1006 return COOKIE_STATE_REJECT;
1007 }
1008
1009 cookie_flags |= INTERNET_COOKIE_HTTPONLY;
1010 ptr += strlenW(szHttpOnly);
1011 }
1012 else if (strncmpiW(ptr, szVersion, 8) == 0)
1013 {
1014 FIXME("version not handled (%s)\n",debugstr_w(ptr));
1015 ptr += strlenW(szVersion);
1016 }
1017 else if (*ptr)
1018 {
1019 FIXME("Unknown additional option %s\n",debugstr_w(ptr));
1020 break;
1021 }
1022 }
1023
1024 EnterCriticalSection(&cookie_cs);
1025
1026 load_persistent_cookie(domain, path);
1027
1028 container = get_cookie_container(domain, path, !expired);
1029 if(!container) {
1030 heap_free(data);
1031 if (value != data) heap_free(value);
1032 LeaveCriticalSection(&cookie_cs);
1033 return COOKIE_STATE_ACCEPT;
1034 }
1035
1036 if(!expiry.dwLowDateTime && !expiry.dwHighDateTime)
1037 cookie_flags |= INTERNET_COOKIE_IS_SESSION;
1038 else
1039 update_persistent = TRUE;
1040
1041 if ((thisCookie = find_cookie(container, cookie_name)))
1042 {
1043 if ((thisCookie->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) {
1044 WARN("An attempt to override httponly cookie\n");
1045 SetLastError(ERROR_INVALID_OPERATION);
1046 heap_free(data);
1047 if (value != data) heap_free(value);
1048 return COOKIE_STATE_REJECT;
1049 }
1050
1051 if (!(thisCookie->flags & INTERNET_COOKIE_IS_SESSION))
1052 update_persistent = TRUE;
1053 delete_cookie(thisCookie);
1054 }
1055
1056 TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_w(cookie_name),
1057 debugstr_w(value), debugstr_w(container->domain->domain),debugstr_w(container->path));
1058
1059 if (!expired) {
1060 cookie_t *new_cookie;
1061
1062 new_cookie = alloc_cookie(cookie_name, value, expiry, create, cookie_flags);
1063 if(!new_cookie) {
1064 heap_free(data);
1065 if (value != data) heap_free(value);
1066 LeaveCriticalSection(&cookie_cs);
1067 return COOKIE_STATE_UNKNOWN;
1068 }
1069
1070 add_cookie(container, new_cookie);
1071 }
1072 heap_free(data);
1073 if (value != data) heap_free(value);
1074
1075 if (!update_persistent || save_persistent_cookie(container))
1076 {
1077 LeaveCriticalSection(&cookie_cs);
1078 return COOKIE_STATE_ACCEPT;
1079 }
1080 LeaveCriticalSection(&cookie_cs);
1081 return COOKIE_STATE_UNKNOWN;
1082 }
1083
1084 /***********************************************************************
1085 * InternetSetCookieExW (WININET.@)
1086 *
1087 * Sets cookie for the specified url
1088 */
1089 DWORD WINAPI InternetSetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
1090 LPCWSTR lpCookieData, DWORD flags, DWORD_PTR reserved)
1091 {
1092 BOOL ret;
1093 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
1094
1095 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_w(lpszUrl), debugstr_w(lpszCookieName),
1096 debugstr_w(lpCookieData), flags, reserved);
1097
1098 if (flags & ~INTERNET_COOKIE_HTTPONLY)
1099 FIXME("flags %x not supported\n", flags);
1100
1101 if (!lpszUrl || !lpCookieData)
1102 {
1103 SetLastError(ERROR_INVALID_PARAMETER);
1104 return COOKIE_STATE_UNKNOWN;
1105 }
1106
1107 hostName[0] = 0;
1108 ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
1109 if (!ret || !hostName[0]) return COOKIE_STATE_UNKNOWN;
1110
1111 if (!lpszCookieName)
1112 {
1113 WCHAR *cookie, *data;
1114 DWORD res;
1115
1116 cookie = heap_strdupW(lpCookieData);
1117 if (!cookie)
1118 {
1119 SetLastError(ERROR_OUTOFMEMORY);
1120 return COOKIE_STATE_UNKNOWN;
1121 }
1122
1123 /* some apps (or is it us??) try to add a cookie with no cookie name, but
1124 * the cookie data in the form of name[=data].
1125 */
1126 if (!(data = strchrW(cookie, '='))) data = cookie + strlenW(cookie);
1127 else *data++ = 0;
1128
1129 res = set_cookie(hostName, path, cookie, data, flags);
1130
1131 heap_free(cookie);
1132 return res;
1133 }
1134 return set_cookie(hostName, path, lpszCookieName, lpCookieData, flags);
1135 }
1136
1137 /***********************************************************************
1138 * InternetSetCookieW (WININET.@)
1139 *
1140 * Sets a cookie for the specified URL.
1141 */
1142 BOOL WINAPI InternetSetCookieW(const WCHAR *url, const WCHAR *name, const WCHAR *data)
1143 {
1144 TRACE("(%s, %s, %s)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data));
1145
1146 return InternetSetCookieExW(url, name, data, 0, 0) == COOKIE_STATE_ACCEPT;
1147 }
1148
1149 /***********************************************************************
1150 * InternetSetCookieA (WININET.@)
1151 *
1152 * Sets cookie for the specified url
1153 *
1154 * RETURNS
1155 * TRUE on success
1156 * FALSE on failure
1157 *
1158 */
1159 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
1160 LPCSTR lpCookieData)
1161 {
1162 LPWSTR data, url, name;
1163 BOOL r;
1164
1165 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
1166 debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
1167
1168 url = heap_strdupAtoW(lpszUrl);
1169 name = heap_strdupAtoW(lpszCookieName);
1170 data = heap_strdupAtoW(lpCookieData);
1171
1172 r = InternetSetCookieW( url, name, data );
1173
1174 heap_free( data );
1175 heap_free( name );
1176 heap_free( url );
1177 return r;
1178 }
1179
1180 /***********************************************************************
1181 * InternetSetCookieExA (WININET.@)
1182 *
1183 * See InternetSetCookieExW.
1184 */
1185 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData,
1186 DWORD dwFlags, DWORD_PTR dwReserved)
1187 {
1188 WCHAR *data, *url, *name;
1189 DWORD r;
1190
1191 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_a(lpszURL), debugstr_a(lpszCookieName),
1192 debugstr_a(lpszCookieData), dwFlags, dwReserved);
1193
1194 url = heap_strdupAtoW(lpszURL);
1195 name = heap_strdupAtoW(lpszCookieName);
1196 data = heap_strdupAtoW(lpszCookieData);
1197
1198 r = InternetSetCookieExW(url, name, data, dwFlags, dwReserved);
1199
1200 heap_free( data );
1201 heap_free( name );
1202 heap_free( url );
1203 return r;
1204 }
1205
1206 /***********************************************************************
1207 * InternetClearAllPerSiteCookieDecisions (WININET.@)
1208 *
1209 * Clears all per-site decisions about cookies.
1210 *
1211 * RETURNS
1212 * TRUE on success
1213 * FALSE on failure
1214 *
1215 */
1216 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID )
1217 {
1218 FIXME("stub\n");
1219 return TRUE;
1220 }
1221
1222 /***********************************************************************
1223 * InternetEnumPerSiteCookieDecisionA (WININET.@)
1224 *
1225 * See InternetEnumPerSiteCookieDecisionW.
1226 */
1227 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, ULONG *pcSiteNameSize,
1228 ULONG *pdwDecision, ULONG dwIndex )
1229 {
1230 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1231 debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1232 return FALSE;
1233 }
1234
1235 /***********************************************************************
1236 * InternetEnumPerSiteCookieDecisionW (WININET.@)
1237 *
1238 * Enumerates all per-site decisions about cookies.
1239 *
1240 * RETURNS
1241 * TRUE on success
1242 * FALSE on failure
1243 *
1244 */
1245 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, ULONG *pcSiteNameSize,
1246 ULONG *pdwDecision, ULONG dwIndex )
1247 {
1248 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1249 debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1250 return FALSE;
1251 }
1252
1253 /***********************************************************************
1254 * InternetGetPerSiteCookieDecisionA (WININET.@)
1255 */
1256 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, ULONG *pResult )
1257 {
1258 FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult);
1259 return FALSE;
1260 }
1261
1262 /***********************************************************************
1263 * InternetGetPerSiteCookieDecisionW (WININET.@)
1264 */
1265 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, ULONG *pResult )
1266 {
1267 FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult);
1268 return FALSE;
1269 }
1270
1271 /***********************************************************************
1272 * InternetSetPerSiteCookieDecisionA (WININET.@)
1273 */
1274 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision )
1275 {
1276 FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision);
1277 return FALSE;
1278 }
1279
1280 /***********************************************************************
1281 * InternetSetPerSiteCookieDecisionW (WININET.@)
1282 */
1283 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision )
1284 {
1285 FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision);
1286 return FALSE;
1287 }
1288
1289 void free_cookie(void)
1290 {
1291 DeleteCriticalSection(&cookie_cs);
1292 }