wininet: Properly clean up in InternetGetCookieExW if no cookies were found.
[wine.git] / dlls / wininet / cookie.c
blobd2fbec3ee2e91f2ba6d2a594cd49c4079f5293be
1 /*
2 * Wininet - cookie handling stuff
4 * Copyright 2002 TransGaming Technologies Inc.
6 * David Hammerton
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.
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.
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
23 #include "config.h"
24 #include "wine/port.h"
26 #if defined(__MINGW32__) || defined (_MSC_VER)
27 #include <ws2tcpip.h>
28 #endif
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <assert.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wininet.h"
42 #include "winerror.h"
44 #include "wine/debug.h"
45 #include "internet.h"
47 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */
50 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
52 /* FIXME
53 * Cookies could use A LOT OF MEMORY. We need some kind of memory management here!
56 struct _cookie_domain_t;
57 struct _cookie_container_t;
59 typedef struct _cookie_t {
60 struct list entry;
62 struct _cookie_container_t *container;
64 WCHAR *name;
65 WCHAR *data;
66 DWORD flags;
67 FILETIME expiry;
68 FILETIME create;
69 } cookie_t;
71 typedef struct _cookie_container_t {
72 struct list entry;
74 WCHAR *path;
75 struct _cookie_domain_t *domain;
77 struct list cookie_list;
78 } cookie_container_t;
80 typedef struct _cookie_domain_t {
81 struct list entry;
83 WCHAR *domain;
84 unsigned subdomain_len;
86 struct _cookie_domain_t *parent;
87 struct list subdomain_list;
89 /* List of stored paths sorted by length of the path. */
90 struct list path_list;
91 } cookie_domain_t;
93 static CRITICAL_SECTION cookie_cs;
94 static CRITICAL_SECTION_DEBUG cookie_cs_debug =
96 0, 0, &cookie_cs,
97 { &cookie_cs_debug.ProcessLocksList, &cookie_cs_debug.ProcessLocksList },
98 0, 0, { (DWORD_PTR)(__FILE__ ": cookie_cs") }
100 static CRITICAL_SECTION cookie_cs = { &cookie_cs_debug, -1, 0, 0, 0, 0 };
101 static struct list domain_list = LIST_INIT(domain_list);
103 static cookie_domain_t *get_cookie_domain(const WCHAR *domain, BOOL create)
105 const WCHAR *ptr = domain + strlenW(domain), *ptr_end, *subdomain_ptr;
106 cookie_domain_t *iter, *current_domain, *prev_domain = NULL;
107 struct list *current_list = &domain_list;
109 while(1) {
110 for(ptr_end = ptr--; ptr > domain && *ptr != '.'; ptr--);
111 subdomain_ptr = *ptr == '.' ? ptr+1 : ptr;
113 current_domain = NULL;
114 LIST_FOR_EACH_ENTRY(iter, current_list, cookie_domain_t, entry) {
115 if(ptr_end-subdomain_ptr == iter->subdomain_len && !memcmp(subdomain_ptr, iter->domain, iter->subdomain_len)) {
116 current_domain = iter;
117 break;
121 if(!current_domain) {
122 if(!create)
123 return prev_domain;
125 current_domain = heap_alloc(sizeof(*current_domain));
126 if(!current_domain)
127 return NULL;
129 current_domain->domain = heap_strdupW(subdomain_ptr);
130 if(!current_domain->domain) {
131 heap_free(current_domain);
132 return NULL;
135 current_domain->subdomain_len = ptr_end-subdomain_ptr;
137 current_domain->parent = prev_domain;
138 list_init(&current_domain->path_list);
139 list_init(&current_domain->subdomain_list);
141 list_add_tail(current_list, &current_domain->entry);
144 if(ptr == domain)
145 return current_domain;
147 prev_domain = current_domain;
148 current_list = &current_domain->subdomain_list;
152 static cookie_container_t *get_cookie_container(const WCHAR *domain, const WCHAR *path, BOOL create)
154 cookie_domain_t *cookie_domain;
155 cookie_container_t *cookie_container, *iter;
156 size_t path_len, len;
158 cookie_domain = get_cookie_domain(domain, create);
159 if(!cookie_domain)
160 return NULL;
162 path_len = strlenW(path);
164 LIST_FOR_EACH_ENTRY(cookie_container, &cookie_domain->path_list, cookie_container_t, entry) {
165 len = strlenW(cookie_container->path);
166 if(len < path_len)
167 break;
169 if(!strcmpiW(cookie_container->path, path))
170 return cookie_container;
173 if(!create)
174 return NULL;
176 cookie_container = heap_alloc(sizeof(*cookie_container));
177 if(!cookie_container)
178 return NULL;
180 cookie_container->path = heap_strdupW(path);
181 if(!cookie_container->path) {
182 heap_free(cookie_container);
183 return NULL;
186 cookie_container->domain = cookie_domain;
187 list_init(&cookie_container->cookie_list);
190 LIST_FOR_EACH_ENTRY(iter, &cookie_domain->path_list, cookie_container_t, entry) {
191 if(strlenW(iter->path) <= path_len) {
192 list_add_before(&iter->entry, &cookie_container->entry);
193 return cookie_container;
197 list_add_tail(&cookie_domain->path_list, &cookie_container->entry);
198 return cookie_container;
201 static void delete_cookie(cookie_t *cookie)
203 list_remove(&cookie->entry);
205 heap_free(cookie->name);
206 heap_free(cookie->data);
207 heap_free(cookie);
210 static cookie_t *alloc_cookie(const WCHAR *name, const WCHAR *data, FILETIME expiry, FILETIME create_time, DWORD flags)
212 cookie_t *new_cookie;
214 new_cookie = heap_alloc(sizeof(*new_cookie));
215 if(!new_cookie)
216 return NULL;
218 new_cookie->expiry = expiry;
219 new_cookie->create = create_time;
220 new_cookie->flags = flags;
221 list_init(&new_cookie->entry);
223 new_cookie->name = heap_strdupW(name);
224 new_cookie->data = heap_strdupW(data);
225 if((name && !new_cookie->name) || (data && !new_cookie->data)) {
226 delete_cookie(new_cookie);
227 return NULL;
230 return new_cookie;
233 static cookie_t *find_cookie(cookie_container_t *container, const WCHAR *name)
235 cookie_t *iter;
237 LIST_FOR_EACH_ENTRY(iter, &container->cookie_list, cookie_t, entry) {
238 if(!strcmpiW(iter->name, name))
239 return iter;
242 return NULL;
245 static void add_cookie(cookie_container_t *container, cookie_t *new_cookie)
247 TRACE("Adding %s=%s to %s %s\n", debugstr_w(new_cookie->name), debugstr_w(new_cookie->data),
248 debugstr_w(container->domain->domain), debugstr_w(container->path));
250 list_add_tail(&container->cookie_list, &new_cookie->entry);
251 new_cookie->container = container;
254 static void replace_cookie(cookie_container_t *container, cookie_t *new_cookie)
256 cookie_t *old_cookie;
258 old_cookie = find_cookie(container, new_cookie->name);
259 if(old_cookie)
260 delete_cookie(old_cookie);
262 add_cookie(container, new_cookie);
265 static BOOL cookie_match_path(cookie_container_t *container, const WCHAR *path)
267 return !strncmpiW(container->path, path, strlenW(container->path));
270 static BOOL create_cookie_url(LPCWSTR domain, LPCWSTR path, WCHAR *buf, DWORD buf_len)
272 static const WCHAR cookie_prefix[] = {'C','o','o','k','i','e',':'};
274 WCHAR *p;
275 DWORD len;
277 if(buf_len < sizeof(cookie_prefix)/sizeof(WCHAR))
278 return FALSE;
279 memcpy(buf, cookie_prefix, sizeof(cookie_prefix));
280 buf += sizeof(cookie_prefix)/sizeof(WCHAR);
281 buf_len -= sizeof(cookie_prefix)/sizeof(WCHAR);
282 p = buf;
284 len = buf_len;
285 if(!GetUserNameW(buf, &len))
286 return FALSE;
287 buf += len-1;
288 buf_len -= len-1;
290 if(!buf_len)
291 return FALSE;
292 *(buf++) = '@';
293 buf_len--;
295 len = strlenW(domain);
296 if(len >= buf_len)
297 return FALSE;
298 memcpy(buf, domain, len*sizeof(WCHAR));
299 buf += len;
300 buf_len -= len;
302 len = strlenW(path);
303 if(len >= buf_len)
304 return FALSE;
305 memcpy(buf, path, len*sizeof(WCHAR));
306 buf += len;
308 *buf = 0;
310 for(; *p; p++)
311 *p = tolowerW(*p);
312 return TRUE;
315 static BOOL load_persistent_cookie(LPCWSTR domain, LPCWSTR path)
317 INTERNET_CACHE_ENTRY_INFOW *info;
318 cookie_container_t *cookie_container;
319 cookie_t *new_cookie;
320 WCHAR cookie_url[MAX_PATH];
321 HANDLE cookie;
322 char *str = NULL, *pbeg, *pend;
323 DWORD size, flags;
324 WCHAR *name, *data;
325 FILETIME expiry, create, time;
327 if (!create_cookie_url(domain, path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
328 return FALSE;
330 size = 0;
331 RetrieveUrlCacheEntryStreamW(cookie_url, NULL, &size, FALSE, 0);
332 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
333 return TRUE;
334 info = heap_alloc(size);
335 if(!info)
336 return FALSE;
337 cookie = RetrieveUrlCacheEntryStreamW(cookie_url, info, &size, FALSE, 0);
338 size = info->dwSizeLow;
339 heap_free(info);
340 if(!cookie)
341 return FALSE;
343 if(!(str = heap_alloc(size+1)) || !ReadUrlCacheEntryStream(cookie, 0, str, &size, 0)) {
344 UnlockUrlCacheEntryStream(cookie, 0);
345 heap_free(str);
346 return FALSE;
348 str[size] = 0;
349 UnlockUrlCacheEntryStream(cookie, 0);
351 cookie_container = get_cookie_container(domain, path, TRUE);
352 if(!cookie_container)
353 return FALSE;
355 GetSystemTimeAsFileTime(&time);
356 for(pbeg=str; pbeg && *pbeg; name=data=NULL) {
357 pend = strchr(pbeg, '\n');
358 if(!pend)
359 break;
360 *pend = 0;
361 name = heap_strdupAtoW(pbeg);
363 pbeg = pend+1;
364 pend = strchr(pbeg, '\n');
365 if(!pend)
366 break;
367 *pend = 0;
368 data = heap_strdupAtoW(pbeg);
370 pbeg = pend+1;
371 pbeg = strchr(pend+1, '\n');
372 if(!pbeg)
373 break;
374 sscanf(pbeg, "%u %u %u %u %u", &flags, &expiry.dwLowDateTime, &expiry.dwHighDateTime,
375 &create.dwLowDateTime, &create.dwHighDateTime);
377 /* skip "*\n" */
378 pbeg = strchr(pbeg, '*');
379 if(pbeg) {
380 pbeg++;
381 if(*pbeg)
382 pbeg++;
385 if(!name || !data)
386 break;
388 if(CompareFileTime(&time, &expiry) <= 0) {
389 new_cookie = alloc_cookie(NULL, NULL, expiry, create, flags);
390 if(!new_cookie)
391 break;
393 new_cookie->name = name;
394 new_cookie->data = data;
396 replace_cookie(cookie_container, new_cookie);
397 }else {
398 heap_free(name);
399 heap_free(data);
402 heap_free(str);
403 heap_free(name);
404 heap_free(data);
406 return TRUE;
409 static BOOL save_persistent_cookie(cookie_container_t *container)
411 static const WCHAR txtW[] = {'t','x','t',0};
413 WCHAR cookie_url[MAX_PATH], cookie_file[MAX_PATH];
414 HANDLE cookie_handle;
415 cookie_t *cookie_container = NULL, *cookie_iter;
416 BOOL do_save = FALSE;
417 char buf[64], *dyn_buf;
418 FILETIME time;
419 DWORD bytes_written;
421 if (!create_cookie_url(container->domain->domain, container->path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
422 return FALSE;
424 /* check if there's anything to save */
425 GetSystemTimeAsFileTime(&time);
426 LIST_FOR_EACH_ENTRY_SAFE(cookie_container, cookie_iter, &container->cookie_list, cookie_t, entry)
428 if((cookie_container->expiry.dwLowDateTime || cookie_container->expiry.dwHighDateTime)
429 && CompareFileTime(&time, &cookie_container->expiry) > 0) {
430 delete_cookie(cookie_container);
431 continue;
434 if(!(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)) {
435 do_save = TRUE;
436 break;
439 if(!do_save) {
440 DeleteUrlCacheEntryW(cookie_url);
441 return TRUE;
444 if(!CreateUrlCacheEntryW(cookie_url, 0, txtW, cookie_file, 0))
445 return FALSE;
446 cookie_handle = CreateFileW(cookie_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
447 if(cookie_handle == INVALID_HANDLE_VALUE) {
448 DeleteFileW(cookie_file);
449 return FALSE;
452 LIST_FOR_EACH_ENTRY(cookie_container, &container->cookie_list, cookie_t, entry)
454 if(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)
455 continue;
457 dyn_buf = heap_strdupWtoA(cookie_container->name);
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;
463 heap_free(dyn_buf);
464 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
465 do_save = FALSE;
466 break;
469 dyn_buf = heap_strdupWtoA(cookie_container->data);
470 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
471 heap_free(dyn_buf);
472 do_save = FALSE;
473 break;
475 heap_free(dyn_buf);
476 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
477 do_save = FALSE;
478 break;
481 dyn_buf = heap_strdupWtoA(container->domain->domain);
482 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
483 heap_free(dyn_buf);
484 do_save = FALSE;
485 break;
487 heap_free(dyn_buf);
489 dyn_buf = heap_strdupWtoA(container->path);
490 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
491 heap_free(dyn_buf);
492 do_save = FALSE;
493 break;
495 heap_free(dyn_buf);
497 sprintf(buf, "\n%u\n%u\n%u\n%u\n%u\n*\n", cookie_container->flags,
498 cookie_container->expiry.dwLowDateTime, cookie_container->expiry.dwHighDateTime,
499 cookie_container->create.dwLowDateTime, cookie_container->create.dwHighDateTime);
500 if(!WriteFile(cookie_handle, buf, strlen(buf), &bytes_written, NULL)) {
501 do_save = FALSE;
502 break;
506 CloseHandle(cookie_handle);
507 if(!do_save) {
508 ERR("error saving cookie file\n");
509 DeleteFileW(cookie_file);
510 return FALSE;
513 memset(&time, 0, sizeof(time));
514 return CommitUrlCacheEntryW(cookie_url, cookie_file, time, time, 0, NULL, 0, txtW, 0);
517 static BOOL COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen)
519 URL_COMPONENTSW UrlComponents;
521 UrlComponents.lpszExtraInfo = NULL;
522 UrlComponents.lpszPassword = NULL;
523 UrlComponents.lpszScheme = NULL;
524 UrlComponents.lpszUrlPath = path;
525 UrlComponents.lpszUserName = NULL;
526 UrlComponents.lpszHostName = hostName;
527 UrlComponents.dwExtraInfoLength = 0;
528 UrlComponents.dwPasswordLength = 0;
529 UrlComponents.dwSchemeLength = 0;
530 UrlComponents.dwUserNameLength = 0;
531 UrlComponents.dwHostNameLength = hostNameLen;
532 UrlComponents.dwUrlPathLength = pathLen;
534 if (!InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents)) return FALSE;
536 /* discard the webpage off the end of the path */
537 if (UrlComponents.dwUrlPathLength)
539 if (path[UrlComponents.dwUrlPathLength - 1] != '/')
541 WCHAR *ptr;
542 if ((ptr = strrchrW(path, '/'))) *(++ptr) = 0;
543 else
545 path[0] = '/';
546 path[1] = 0;
550 else if (pathLen >= 2)
552 path[0] = '/';
553 path[1] = 0;
555 return TRUE;
558 typedef struct {
559 cookie_t **cookies;
560 unsigned cnt;
561 unsigned size;
563 unsigned string_len;
564 } cookie_set_t;
566 static DWORD get_cookie(const WCHAR *host, const WCHAR *path, DWORD flags, cookie_set_t *res)
568 static const WCHAR empty_path[] = { '/',0 };
570 WCHAR *ptr, subpath[INTERNET_MAX_PATH_LENGTH];
571 const WCHAR *p;
572 cookie_domain_t *domain;
573 cookie_container_t *container;
574 unsigned len;
575 FILETIME tm;
577 GetSystemTimeAsFileTime(&tm);
579 len = strlenW(host);
580 p = host+len;
581 while(p>host && p[-1]!='.') p--;
582 while(p != host) {
583 p--;
584 while(p>host && p[-1]!='.') p--;
585 if(p == host) break;
587 load_persistent_cookie(p, empty_path);
590 len = strlenW(path);
591 assert(len+1 < INTERNET_MAX_PATH_LENGTH);
592 memcpy(subpath, path, (len+1)*sizeof(WCHAR));
593 ptr = subpath+len;
594 do {
595 *ptr = 0;
596 load_persistent_cookie(host, subpath);
598 ptr--;
599 while(ptr>subpath && ptr[-1]!='/') ptr--;
600 }while(ptr != subpath);
602 domain = get_cookie_domain(host, FALSE);
603 if(!domain) {
604 TRACE("Unknown host %s\n", debugstr_w(host));
605 return ERROR_NO_MORE_ITEMS;
608 for(domain = get_cookie_domain(host, FALSE); domain; domain = domain->parent) {
609 TRACE("Trying %s domain...\n", debugstr_w(domain->domain));
611 LIST_FOR_EACH_ENTRY(container, &domain->path_list, cookie_container_t, entry) {
612 struct list *cursor, *cursor2;
614 TRACE("path %s\n", debugstr_w(container->path));
616 if(!cookie_match_path(container, path))
617 continue;
619 TRACE("found domain %p\n", domain->domain);
621 LIST_FOR_EACH_SAFE(cursor, cursor2, &container->cookie_list) {
622 cookie_t *cookie_iter = LIST_ENTRY(cursor, cookie_t, entry);
624 /* check for expiry */
625 if((cookie_iter->expiry.dwLowDateTime != 0 || cookie_iter->expiry.dwHighDateTime != 0)
626 && CompareFileTime(&tm, &cookie_iter->expiry) > 0) {
627 TRACE("Found expired cookie. deleting\n");
628 delete_cookie(cookie_iter);
629 continue;
632 if((cookie_iter->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY))
633 continue;
636 if(!res->size) {
637 res->cookies = heap_alloc(4*sizeof(*res->cookies));
638 if(!res->cookies)
639 continue;
640 res->size = 4;
641 }else if(res->cnt == res->size) {
642 cookie_t **new_cookies = heap_realloc(res->cookies, res->size*2*sizeof(*res->cookies));
643 if(!new_cookies)
644 continue;
645 res->cookies = new_cookies;
646 res->size *= 2;
649 if(res->cnt)
650 res->string_len += 2; /* '; ' */
651 res->cookies[res->cnt++] = cookie_iter;
653 res->string_len += strlenW(cookie_iter->name);
654 if(*cookie_iter->data)
655 res->string_len += 1 /* = */ + strlenW(cookie_iter->data);
660 return ERROR_SUCCESS;
663 static void cookie_set_to_string(const cookie_set_t *cookie_set, WCHAR *str)
665 WCHAR *ptr = str;
666 unsigned i, len;
668 for(i=0; i<cookie_set->cnt; i++) {
669 if(i) {
670 *ptr++ = ';';
671 *ptr++ = ' ';
674 len = strlenW(cookie_set->cookies[i]->name);
675 memcpy(ptr, cookie_set->cookies[i]->name, len*sizeof(WCHAR));
676 ptr += len;
678 if(*cookie_set->cookies[i]->data) {
679 *ptr++ = '=';
680 len = strlenW(cookie_set->cookies[i]->data);
681 memcpy(ptr, cookie_set->cookies[i]->data, len*sizeof(WCHAR));
682 ptr += len;
686 assert(ptr-str == cookie_set->string_len);
687 TRACE("%s\n", debugstr_wn(str, ptr-str));
690 DWORD get_cookie_header(const WCHAR *host, const WCHAR *path, WCHAR **ret)
692 cookie_set_t cookie_set = {0};
693 DWORD res;
695 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
697 EnterCriticalSection(&cookie_cs);
699 res = get_cookie(host, path, INTERNET_COOKIE_HTTPONLY, &cookie_set);
700 if(res != ERROR_SUCCESS) {
701 LeaveCriticalSection(&cookie_cs);
702 return res;
705 if(cookie_set.cnt) {
706 WCHAR *header, *ptr;
708 ptr = header = heap_alloc(sizeof(cookieW) + (cookie_set.string_len + 3 /* crlf0 */) * sizeof(WCHAR));
709 if(header) {
710 memcpy(ptr, cookieW, sizeof(cookieW));
711 ptr += sizeof(cookieW)/sizeof(*cookieW);
713 cookie_set_to_string(&cookie_set, ptr);
714 heap_free(cookie_set.cookies);
715 ptr += cookie_set.string_len;
717 *ptr++ = '\r';
718 *ptr++ = '\n';
719 *ptr++ = 0;
721 *ret = header;
722 }else {
723 res = ERROR_NOT_ENOUGH_MEMORY;
725 }else {
726 *ret = NULL;
729 LeaveCriticalSection(&cookie_cs);
730 return ERROR_SUCCESS;
733 /***********************************************************************
734 * InternetGetCookieExW (WININET.@)
736 * Retrieve cookie from the specified url
738 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
739 * So it won't be implemented here.
741 * RETURNS
742 * TRUE on success
743 * FALSE on failure
746 BOOL WINAPI InternetGetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
747 LPWSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
749 WCHAR host[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
750 cookie_set_t cookie_set = {0};
751 DWORD res;
752 BOOL ret;
754 TRACE("(%s, %s, %p, %p, %x, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), lpCookieData, lpdwSize, flags, reserved);
756 if (flags)
757 FIXME("flags 0x%08x not supported\n", flags);
759 if (!lpszUrl)
761 SetLastError(ERROR_INVALID_PARAMETER);
762 return FALSE;
765 host[0] = 0;
766 ret = COOKIE_crackUrlSimple(lpszUrl, host, sizeof(host)/sizeof(host[0]), path, sizeof(path)/sizeof(path[0]));
767 if (!ret || !host[0]) {
768 SetLastError(ERROR_INVALID_PARAMETER);
769 return FALSE;
772 EnterCriticalSection(&cookie_cs);
774 res = get_cookie(host, path, flags, &cookie_set);
775 if(res != ERROR_SUCCESS) {
776 LeaveCriticalSection(&cookie_cs);
777 SetLastError(res);
778 return FALSE;
781 if(cookie_set.cnt) {
782 if(!lpCookieData || cookie_set.string_len+1 > *lpdwSize) {
783 *lpdwSize = (cookie_set.string_len + 1) * sizeof(WCHAR);
784 TRACE("returning %u\n", *lpdwSize);
785 if(lpCookieData) {
786 SetLastError(ERROR_INSUFFICIENT_BUFFER);
787 ret = FALSE;
789 }else {
790 *lpdwSize = cookie_set.string_len + 1;
791 cookie_set_to_string(&cookie_set, lpCookieData);
792 lpCookieData[cookie_set.string_len] = 0;
794 }else {
795 TRACE("no cookies found for %s\n", debugstr_w(host));
796 SetLastError(ERROR_NO_MORE_ITEMS);
797 ret = FALSE;
800 heap_free(cookie_set.cookies);
801 LeaveCriticalSection(&cookie_cs);
802 return ret;
805 /***********************************************************************
806 * InternetGetCookieW (WININET.@)
808 * Retrieve cookie for the specified URL.
810 BOOL WINAPI InternetGetCookieW(const WCHAR *url, const WCHAR *name, WCHAR *data, DWORD *size)
812 TRACE("(%s, %s, %s, %p)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data), size);
814 return InternetGetCookieExW(url, name, data, size, 0, NULL);
817 /***********************************************************************
818 * InternetGetCookieExA (WININET.@)
820 * Retrieve cookie from the specified url
822 * RETURNS
823 * TRUE on success
824 * FALSE on failure
827 BOOL WINAPI InternetGetCookieExA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
828 LPSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
830 WCHAR *url, *name;
831 DWORD len, size;
832 BOOL r;
834 TRACE("(%s %s %p %p(%u) %x %p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
835 lpCookieData, lpdwSize, lpdwSize ? *lpdwSize : 0, flags, reserved);
837 url = heap_strdupAtoW(lpszUrl);
838 name = heap_strdupAtoW(lpszCookieName);
840 r = InternetGetCookieExW( url, name, NULL, &len, flags, reserved );
841 if( r )
843 WCHAR *szCookieData;
845 szCookieData = heap_alloc(len * sizeof(WCHAR));
846 if( !szCookieData )
848 r = FALSE;
850 else
852 r = InternetGetCookieExW( url, name, szCookieData, &len, flags, reserved );
854 if(r) {
855 size = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, NULL, 0, NULL, NULL);
856 if(lpCookieData) {
857 if(*lpdwSize >= size) {
858 WideCharToMultiByte( CP_ACP, 0, szCookieData, len, lpCookieData, *lpdwSize, NULL, NULL);
859 }else {
860 SetLastError(ERROR_INSUFFICIENT_BUFFER);
861 r = FALSE;
864 *lpdwSize = size;
867 heap_free( szCookieData );
870 heap_free( name );
871 heap_free( url );
872 return r;
875 /***********************************************************************
876 * InternetGetCookieA (WININET.@)
878 * See InternetGetCookieW.
880 BOOL WINAPI InternetGetCookieA(const char *url, const char *name, char *data, DWORD *size)
882 TRACE("(%s, %s, %s, %p)\n", debugstr_a(url), debugstr_a(name), debugstr_a(data), size);
884 return InternetGetCookieExA(url, name, data, size, 0, NULL);
887 /***********************************************************************
888 * IsDomainLegalCookieDomainW (WININET.@)
890 BOOL WINAPI IsDomainLegalCookieDomainW( LPCWSTR s1, LPCWSTR s2 )
892 DWORD s1_len, s2_len;
894 FIXME("(%s, %s) semi-stub\n", debugstr_w(s1), debugstr_w(s2));
896 if (!s1 || !s2)
898 SetLastError(ERROR_INVALID_PARAMETER);
899 return FALSE;
901 if (s1[0] == '.' || !s1[0] || s2[0] == '.' || !s2[0])
903 SetLastError(ERROR_INVALID_NAME);
904 return FALSE;
906 if(!strchrW(s1, '.') || !strchrW(s2, '.'))
907 return FALSE;
909 s1_len = strlenW(s1);
910 s2_len = strlenW(s2);
911 if (s1_len > s2_len)
912 return FALSE;
914 if (strncmpiW(s1, s2+s2_len-s1_len, s1_len) || (s2_len>s1_len && s2[s2_len-s1_len-1]!='.'))
916 SetLastError(ERROR_INVALID_PARAMETER);
917 return FALSE;
920 return TRUE;
923 DWORD set_cookie(const WCHAR *domain, const WCHAR *path, const WCHAR *cookie_name, const WCHAR *cookie_data, DWORD flags)
925 cookie_container_t *container;
926 cookie_t *thisCookie;
927 LPWSTR data, value;
928 WCHAR *ptr;
929 FILETIME expiry, create;
930 BOOL expired = FALSE, update_persistent = FALSE;
931 DWORD cookie_flags = 0;
933 TRACE("%s %s %s=%s %x\n", debugstr_w(domain), debugstr_w(path), debugstr_w(cookie_name), debugstr_w(cookie_data), flags);
935 value = data = heap_strdupW(cookie_data);
936 if (!data)
938 ERR("could not allocate the cookie data buffer\n");
939 return COOKIE_STATE_UNKNOWN;
942 memset(&expiry,0,sizeof(expiry));
943 GetSystemTimeAsFileTime(&create);
945 /* lots of information can be parsed out of the cookie value */
947 ptr = data;
948 for (;;)
950 static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
951 static const WCHAR szPath[] = {'p','a','t','h','=',0};
952 static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','=',0};
953 static const WCHAR szSecure[] = {'s','e','c','u','r','e',0};
954 static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y',0};
956 if (!(ptr = strchrW(ptr,';'))) break;
957 *ptr++ = 0;
959 if (value != data) heap_free(value);
960 value = heap_alloc((ptr - data) * sizeof(WCHAR));
961 if (value == NULL)
963 heap_free(data);
964 ERR("could not allocate the cookie value buffer\n");
965 return COOKIE_STATE_UNKNOWN;
967 strcpyW(value, data);
969 while (*ptr == ' ') ptr++; /* whitespace */
971 if (strncmpiW(ptr, szDomain, 7) == 0)
973 WCHAR *end_ptr;
975 ptr += sizeof(szDomain)/sizeof(szDomain[0])-1;
976 if(*ptr == '.')
977 ptr++;
978 end_ptr = strchrW(ptr, ';');
979 if(end_ptr)
980 *end_ptr = 0;
982 if(!IsDomainLegalCookieDomainW(ptr, domain))
984 if(value != data)
985 heap_free(value);
986 heap_free(data);
987 return COOKIE_STATE_UNKNOWN;
990 if(end_ptr)
991 *end_ptr = ';';
993 domain = ptr;
994 TRACE("Parsing new domain %s\n",debugstr_w(domain));
996 else if (strncmpiW(ptr, szPath, 5) == 0)
998 ptr+=strlenW(szPath);
999 path = ptr;
1000 TRACE("Parsing new path %s\n",debugstr_w(path));
1002 else if (strncmpiW(ptr, szExpires, 8) == 0)
1004 SYSTEMTIME st;
1005 ptr+=strlenW(szExpires);
1006 if (InternetTimeToSystemTimeW(ptr, &st, 0))
1008 SystemTimeToFileTime(&st, &expiry);
1010 if (CompareFileTime(&create,&expiry) > 0)
1012 TRACE("Cookie already expired.\n");
1013 expired = TRUE;
1017 else if (strncmpiW(ptr, szSecure, 6) == 0)
1019 FIXME("secure not handled (%s)\n",debugstr_w(ptr));
1020 ptr += strlenW(szSecure);
1022 else if (strncmpiW(ptr, szHttpOnly, 8) == 0)
1024 if(!(flags & INTERNET_COOKIE_HTTPONLY)) {
1025 WARN("HTTP only cookie added without INTERNET_COOKIE_HTTPONLY flag\n");
1026 heap_free(data);
1027 if (value != data) heap_free(value);
1028 SetLastError(ERROR_INVALID_OPERATION);
1029 return COOKIE_STATE_REJECT;
1032 cookie_flags |= INTERNET_COOKIE_HTTPONLY;
1033 ptr += strlenW(szHttpOnly);
1035 else if (*ptr)
1037 FIXME("Unknown additional option %s\n",debugstr_w(ptr));
1038 break;
1042 EnterCriticalSection(&cookie_cs);
1044 load_persistent_cookie(domain, path);
1046 container = get_cookie_container(domain, path, !expired);
1047 if(!container) {
1048 heap_free(data);
1049 if (value != data) heap_free(value);
1050 LeaveCriticalSection(&cookie_cs);
1051 return COOKIE_STATE_ACCEPT;
1054 if(!expiry.dwLowDateTime && !expiry.dwHighDateTime)
1055 cookie_flags |= INTERNET_COOKIE_IS_SESSION;
1056 else
1057 update_persistent = TRUE;
1059 if ((thisCookie = find_cookie(container, cookie_name)))
1061 if ((thisCookie->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) {
1062 WARN("An attempt to override httponly cookie\n");
1063 SetLastError(ERROR_INVALID_OPERATION);
1064 heap_free(data);
1065 if (value != data) heap_free(value);
1066 return COOKIE_STATE_REJECT;
1069 if (!(thisCookie->flags & INTERNET_COOKIE_IS_SESSION))
1070 update_persistent = TRUE;
1071 delete_cookie(thisCookie);
1074 TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_w(cookie_name),
1075 debugstr_w(value), debugstr_w(container->domain->domain),debugstr_w(container->path));
1077 if (!expired) {
1078 cookie_t *new_cookie;
1080 new_cookie = alloc_cookie(cookie_name, value, expiry, create, cookie_flags);
1081 if(!new_cookie) {
1082 heap_free(data);
1083 if (value != data) heap_free(value);
1084 LeaveCriticalSection(&cookie_cs);
1085 return COOKIE_STATE_UNKNOWN;
1088 add_cookie(container, new_cookie);
1090 heap_free(data);
1091 if (value != data) heap_free(value);
1093 if (!update_persistent || save_persistent_cookie(container))
1095 LeaveCriticalSection(&cookie_cs);
1096 return COOKIE_STATE_ACCEPT;
1098 LeaveCriticalSection(&cookie_cs);
1099 return COOKIE_STATE_UNKNOWN;
1102 /***********************************************************************
1103 * InternetSetCookieExW (WININET.@)
1105 * Sets cookie for the specified url
1107 DWORD WINAPI InternetSetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
1108 LPCWSTR lpCookieData, DWORD flags, DWORD_PTR reserved)
1110 BOOL ret;
1111 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
1113 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_w(lpszUrl), debugstr_w(lpszCookieName),
1114 debugstr_w(lpCookieData), flags, reserved);
1116 if (flags & ~INTERNET_COOKIE_HTTPONLY)
1117 FIXME("flags %x not supported\n", flags);
1119 if (!lpszUrl || !lpCookieData)
1121 SetLastError(ERROR_INVALID_PARAMETER);
1122 return COOKIE_STATE_UNKNOWN;
1125 hostName[0] = 0;
1126 ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
1127 if (!ret || !hostName[0]) return COOKIE_STATE_UNKNOWN;
1129 if (!lpszCookieName)
1131 WCHAR *cookie, *data;
1132 DWORD res;
1134 cookie = heap_strdupW(lpCookieData);
1135 if (!cookie)
1137 SetLastError(ERROR_OUTOFMEMORY);
1138 return COOKIE_STATE_UNKNOWN;
1141 /* some apps (or is it us??) try to add a cookie with no cookie name, but
1142 * the cookie data in the form of name[=data].
1144 if (!(data = strchrW(cookie, '='))) data = cookie + strlenW(cookie);
1145 else *data++ = 0;
1147 res = set_cookie(hostName, path, cookie, data, flags);
1149 heap_free(cookie);
1150 return res;
1152 return set_cookie(hostName, path, lpszCookieName, lpCookieData, flags);
1155 /***********************************************************************
1156 * InternetSetCookieW (WININET.@)
1158 * Sets a cookie for the specified URL.
1160 BOOL WINAPI InternetSetCookieW(const WCHAR *url, const WCHAR *name, const WCHAR *data)
1162 TRACE("(%s, %s, %s)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data));
1164 return InternetSetCookieExW(url, name, data, 0, 0) == COOKIE_STATE_ACCEPT;
1167 /***********************************************************************
1168 * InternetSetCookieA (WININET.@)
1170 * Sets cookie for the specified url
1172 * RETURNS
1173 * TRUE on success
1174 * FALSE on failure
1177 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
1178 LPCSTR lpCookieData)
1180 LPWSTR data, url, name;
1181 BOOL r;
1183 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
1184 debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
1186 url = heap_strdupAtoW(lpszUrl);
1187 name = heap_strdupAtoW(lpszCookieName);
1188 data = heap_strdupAtoW(lpCookieData);
1190 r = InternetSetCookieW( url, name, data );
1192 heap_free( data );
1193 heap_free( name );
1194 heap_free( url );
1195 return r;
1198 /***********************************************************************
1199 * InternetSetCookieExA (WININET.@)
1201 * See InternetSetCookieExW.
1203 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData,
1204 DWORD dwFlags, DWORD_PTR dwReserved)
1206 WCHAR *data, *url, *name;
1207 DWORD r;
1209 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_a(lpszURL), debugstr_a(lpszCookieName),
1210 debugstr_a(lpszCookieData), dwFlags, dwReserved);
1212 url = heap_strdupAtoW(lpszURL);
1213 name = heap_strdupAtoW(lpszCookieName);
1214 data = heap_strdupAtoW(lpszCookieData);
1216 r = InternetSetCookieExW(url, name, data, dwFlags, dwReserved);
1218 heap_free( data );
1219 heap_free( name );
1220 heap_free( url );
1221 return r;
1224 /***********************************************************************
1225 * InternetClearAllPerSiteCookieDecisions (WININET.@)
1227 * Clears all per-site decisions about cookies.
1229 * RETURNS
1230 * TRUE on success
1231 * FALSE on failure
1234 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID )
1236 FIXME("stub\n");
1237 return TRUE;
1240 /***********************************************************************
1241 * InternetEnumPerSiteCookieDecisionA (WININET.@)
1243 * See InternetEnumPerSiteCookieDecisionW.
1245 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, ULONG *pcSiteNameSize,
1246 ULONG *pdwDecision, ULONG dwIndex )
1248 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1249 debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1250 return FALSE;
1253 /***********************************************************************
1254 * InternetEnumPerSiteCookieDecisionW (WININET.@)
1256 * Enumerates all per-site decisions about cookies.
1258 * RETURNS
1259 * TRUE on success
1260 * FALSE on failure
1263 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, ULONG *pcSiteNameSize,
1264 ULONG *pdwDecision, ULONG dwIndex )
1266 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1267 debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1268 return FALSE;
1271 /***********************************************************************
1272 * InternetGetPerSiteCookieDecisionA (WININET.@)
1274 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, ULONG *pResult )
1276 FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult);
1277 return FALSE;
1280 /***********************************************************************
1281 * InternetGetPerSiteCookieDecisionW (WININET.@)
1283 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, ULONG *pResult )
1285 FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult);
1286 return FALSE;
1289 /***********************************************************************
1290 * InternetSetPerSiteCookieDecisionA (WININET.@)
1292 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision )
1294 FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision);
1295 return FALSE;
1298 /***********************************************************************
1299 * InternetSetPerSiteCookieDecisionW (WININET.@)
1301 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision )
1303 FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision);
1304 return FALSE;
1307 void free_cookie(void)
1309 DeleteCriticalSection(&cookie_cs);