xref: /reactos/dll/win32/wininet/cookie.c (revision e152f78a)
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(&current_domain->path_list);
139             list_init(&current_domain->subdomain_list);
140 
141             list_add_tail(current_list, &current_domain->entry);
142         }
143 
144         if(ptr == domain.str)
145             return current_domain;
146 
147         prev_domain = current_domain;
148         current_list = &current_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