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