openal32: Prefer native version.
[wine/multimedia.git] / dlls / wininet / cookie.c
blob4784b33d9cf9674f5603809eb3dc9d09b0f21d13
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 "ws2tcpip.h"
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wininet.h"
34 #include "winerror.h"
36 #include "wine/debug.h"
37 #include "internet.h"
39 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */
42 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
44 /* FIXME
45 * Cookies could use A LOT OF MEMORY. We need some kind of memory management here!
48 struct _cookie_domain_t;
49 struct _cookie_container_t;
51 typedef struct _cookie_t {
52 struct list entry;
54 struct _cookie_container_t *container;
56 WCHAR *name;
57 WCHAR *data;
58 DWORD flags;
59 FILETIME expiry;
60 FILETIME create;
61 } cookie_t;
63 typedef struct _cookie_container_t {
64 struct list entry;
66 WCHAR *path;
67 struct _cookie_domain_t *domain;
69 struct list cookie_list;
70 } cookie_container_t;
72 typedef struct _cookie_domain_t {
73 struct list entry;
75 WCHAR *domain;
76 unsigned subdomain_len;
78 struct _cookie_domain_t *parent;
79 struct list subdomain_list;
81 /* List of stored paths sorted by length of the path. */
82 struct list path_list;
83 } cookie_domain_t;
85 static CRITICAL_SECTION cookie_cs;
86 static CRITICAL_SECTION_DEBUG cookie_cs_debug =
88 0, 0, &cookie_cs,
89 { &cookie_cs_debug.ProcessLocksList, &cookie_cs_debug.ProcessLocksList },
90 0, 0, { (DWORD_PTR)(__FILE__ ": cookie_cs") }
92 static CRITICAL_SECTION cookie_cs = { &cookie_cs_debug, -1, 0, 0, 0, 0 };
93 static struct list domain_list = LIST_INIT(domain_list);
95 static cookie_domain_t *get_cookie_domain(const WCHAR *domain, BOOL create)
97 const WCHAR *ptr = domain + strlenW(domain), *ptr_end, *subdomain_ptr;
98 cookie_domain_t *iter, *current_domain, *prev_domain = NULL;
99 struct list *current_list = &domain_list;
101 while(1) {
102 for(ptr_end = ptr--; ptr > domain && *ptr != '.'; ptr--);
103 subdomain_ptr = *ptr == '.' ? ptr+1 : ptr;
105 current_domain = NULL;
106 LIST_FOR_EACH_ENTRY(iter, current_list, cookie_domain_t, entry) {
107 if(ptr_end-subdomain_ptr == iter->subdomain_len && !memcmp(subdomain_ptr, iter->domain, iter->subdomain_len)) {
108 current_domain = iter;
109 break;
113 if(!current_domain) {
114 if(!create)
115 return prev_domain;
117 current_domain = heap_alloc(sizeof(*current_domain));
118 if(!current_domain)
119 return NULL;
121 current_domain->domain = heap_strdupW(subdomain_ptr);
122 if(!current_domain->domain) {
123 heap_free(current_domain);
124 return NULL;
127 current_domain->subdomain_len = ptr_end-subdomain_ptr;
129 current_domain->parent = prev_domain;
130 list_init(&current_domain->path_list);
131 list_init(&current_domain->subdomain_list);
133 list_add_tail(current_list, &current_domain->entry);
136 if(ptr == domain)
137 return current_domain;
139 prev_domain = current_domain;
140 current_list = &current_domain->subdomain_list;
144 static cookie_container_t *get_cookie_container(const WCHAR *domain, const WCHAR *path, BOOL create)
146 cookie_domain_t *cookie_domain;
147 cookie_container_t *cookie_container, *iter;
148 size_t path_len, len;
150 cookie_domain = get_cookie_domain(domain, create);
151 if(!cookie_domain)
152 return NULL;
154 path_len = strlenW(path);
156 LIST_FOR_EACH_ENTRY(cookie_container, &cookie_domain->path_list, cookie_container_t, entry) {
157 len = strlenW(cookie_container->path);
158 if(len < path_len)
159 break;
161 if(!strcmpiW(cookie_container->path, path))
162 return cookie_container;
165 if(!create)
166 return NULL;
168 cookie_container = heap_alloc(sizeof(*cookie_container));
169 if(!cookie_container)
170 return NULL;
172 cookie_container->path = heap_strdupW(path);
173 if(!cookie_container->path) {
174 heap_free(cookie_container);
175 return NULL;
178 cookie_container->domain = cookie_domain;
179 list_init(&cookie_container->cookie_list);
182 LIST_FOR_EACH_ENTRY(iter, &cookie_domain->path_list, cookie_container_t, entry) {
183 if(strlenW(iter->path) <= path_len) {
184 list_add_before(&iter->entry, &cookie_container->entry);
185 return cookie_container;
189 list_add_tail(&cookie_domain->path_list, &cookie_container->entry);
190 return cookie_container;
193 static void delete_cookie(cookie_t *cookie)
195 list_remove(&cookie->entry);
197 heap_free(cookie->name);
198 heap_free(cookie->data);
199 heap_free(cookie);
202 static cookie_t *alloc_cookie(const WCHAR *name, const WCHAR *data, FILETIME expiry, FILETIME create_time, DWORD flags)
204 cookie_t *new_cookie;
206 new_cookie = heap_alloc(sizeof(*new_cookie));
207 if(!new_cookie)
208 return NULL;
210 new_cookie->expiry = expiry;
211 new_cookie->create = create_time;
212 new_cookie->flags = flags;
213 list_init(&new_cookie->entry);
215 new_cookie->name = heap_strdupW(name);
216 new_cookie->data = heap_strdupW(data);
217 if((name && !new_cookie->name) || (data && !new_cookie->data)) {
218 delete_cookie(new_cookie);
219 return NULL;
222 return new_cookie;
225 static cookie_t *find_cookie(cookie_container_t *container, const WCHAR *name)
227 cookie_t *iter;
229 LIST_FOR_EACH_ENTRY(iter, &container->cookie_list, cookie_t, entry) {
230 if(!strcmpiW(iter->name, name))
231 return iter;
234 return NULL;
237 static void add_cookie(cookie_container_t *container, cookie_t *new_cookie)
239 TRACE("Adding %s=%s to %s %s\n", debugstr_w(new_cookie->name), debugstr_w(new_cookie->data),
240 debugstr_w(container->domain->domain), debugstr_w(container->path));
242 list_add_tail(&container->cookie_list, &new_cookie->entry);
243 new_cookie->container = container;
246 static void replace_cookie(cookie_container_t *container, cookie_t *new_cookie)
248 cookie_t *old_cookie;
250 old_cookie = find_cookie(container, new_cookie->name);
251 if(old_cookie)
252 delete_cookie(old_cookie);
254 add_cookie(container, new_cookie);
257 static BOOL cookie_match_path(cookie_container_t *container, const WCHAR *path)
259 return !strncmpiW(container->path, path, strlenW(container->path));
262 static BOOL create_cookie_url(LPCWSTR domain, LPCWSTR path, WCHAR *buf, DWORD buf_len)
264 static const WCHAR cookie_prefix[] = {'C','o','o','k','i','e',':'};
266 WCHAR *p;
267 DWORD len;
269 if(buf_len < sizeof(cookie_prefix)/sizeof(WCHAR))
270 return FALSE;
271 memcpy(buf, cookie_prefix, sizeof(cookie_prefix));
272 buf += sizeof(cookie_prefix)/sizeof(WCHAR);
273 buf_len -= sizeof(cookie_prefix)/sizeof(WCHAR);
274 p = buf;
276 len = buf_len;
277 if(!GetUserNameW(buf, &len))
278 return FALSE;
279 buf += len-1;
280 buf_len -= len-1;
282 if(!buf_len)
283 return FALSE;
284 *(buf++) = '@';
285 buf_len--;
287 len = strlenW(domain);
288 if(len >= buf_len)
289 return FALSE;
290 memcpy(buf, domain, len*sizeof(WCHAR));
291 buf += len;
292 buf_len -= len;
294 len = strlenW(path);
295 if(len >= buf_len)
296 return FALSE;
297 memcpy(buf, path, len*sizeof(WCHAR));
298 buf += len;
300 *buf = 0;
302 for(; *p; p++)
303 *p = tolowerW(*p);
304 return TRUE;
307 static BOOL load_persistent_cookie(LPCWSTR domain, LPCWSTR path)
309 INTERNET_CACHE_ENTRY_INFOW *info;
310 cookie_container_t *cookie_container;
311 cookie_t *new_cookie;
312 WCHAR cookie_url[MAX_PATH];
313 HANDLE cookie;
314 char *str = NULL, *pbeg, *pend;
315 DWORD size, flags;
316 WCHAR *name, *data;
317 FILETIME expiry, create, time;
319 if (!create_cookie_url(domain, path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
320 return FALSE;
322 size = 0;
323 RetrieveUrlCacheEntryStreamW(cookie_url, NULL, &size, FALSE, 0);
324 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
325 return TRUE;
326 info = heap_alloc(size);
327 if(!info)
328 return FALSE;
329 cookie = RetrieveUrlCacheEntryStreamW(cookie_url, info, &size, FALSE, 0);
330 size = info->dwSizeLow;
331 heap_free(info);
332 if(!cookie)
333 return FALSE;
335 if(!(str = heap_alloc(size+1)) || !ReadUrlCacheEntryStream(cookie, 0, str, &size, 0)) {
336 UnlockUrlCacheEntryStream(cookie, 0);
337 heap_free(str);
338 return FALSE;
340 str[size] = 0;
341 UnlockUrlCacheEntryStream(cookie, 0);
343 cookie_container = get_cookie_container(domain, path, TRUE);
344 if(!cookie_container) {
345 heap_free(str);
346 return FALSE;
349 GetSystemTimeAsFileTime(&time);
350 for(pbeg=str; pbeg && *pbeg; name=data=NULL) {
351 pend = strchr(pbeg, '\n');
352 if(!pend)
353 break;
354 *pend = 0;
355 name = heap_strdupAtoW(pbeg);
357 pbeg = pend+1;
358 pend = strchr(pbeg, '\n');
359 if(!pend)
360 break;
361 *pend = 0;
362 data = heap_strdupAtoW(pbeg);
364 pbeg = strchr(pend+1, '\n');
365 if(!pbeg)
366 break;
367 sscanf(pbeg, "%u %u %u %u %u", &flags, &expiry.dwLowDateTime, &expiry.dwHighDateTime,
368 &create.dwLowDateTime, &create.dwHighDateTime);
370 /* skip "*\n" */
371 pbeg = strchr(pbeg, '*');
372 if(pbeg) {
373 pbeg++;
374 if(*pbeg)
375 pbeg++;
378 if(!name || !data)
379 break;
381 if(CompareFileTime(&time, &expiry) <= 0) {
382 new_cookie = alloc_cookie(NULL, NULL, expiry, create, flags);
383 if(!new_cookie)
384 break;
386 new_cookie->name = name;
387 new_cookie->data = data;
389 replace_cookie(cookie_container, new_cookie);
390 }else {
391 heap_free(name);
392 heap_free(data);
395 heap_free(str);
396 heap_free(name);
397 heap_free(data);
399 return TRUE;
402 static BOOL save_persistent_cookie(cookie_container_t *container)
404 static const WCHAR txtW[] = {'t','x','t',0};
406 WCHAR cookie_url[MAX_PATH], cookie_file[MAX_PATH];
407 HANDLE cookie_handle;
408 cookie_t *cookie_container = NULL, *cookie_iter;
409 BOOL do_save = FALSE;
410 char buf[64], *dyn_buf;
411 FILETIME time;
412 DWORD bytes_written;
414 if (!create_cookie_url(container->domain->domain, container->path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0])))
415 return FALSE;
417 /* check if there's anything to save */
418 GetSystemTimeAsFileTime(&time);
419 LIST_FOR_EACH_ENTRY_SAFE(cookie_container, cookie_iter, &container->cookie_list, cookie_t, entry)
421 if((cookie_container->expiry.dwLowDateTime || cookie_container->expiry.dwHighDateTime)
422 && CompareFileTime(&time, &cookie_container->expiry) > 0) {
423 delete_cookie(cookie_container);
424 continue;
427 if(!(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)) {
428 do_save = TRUE;
429 break;
432 if(!do_save) {
433 DeleteUrlCacheEntryW(cookie_url);
434 return TRUE;
437 if(!CreateUrlCacheEntryW(cookie_url, 0, txtW, cookie_file, 0))
438 return FALSE;
439 cookie_handle = CreateFileW(cookie_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
440 if(cookie_handle == INVALID_HANDLE_VALUE) {
441 DeleteFileW(cookie_file);
442 return FALSE;
445 LIST_FOR_EACH_ENTRY(cookie_container, &container->cookie_list, cookie_t, entry)
447 if(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)
448 continue;
450 dyn_buf = heap_strdupWtoA(cookie_container->name);
451 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
452 heap_free(dyn_buf);
453 do_save = FALSE;
454 break;
456 heap_free(dyn_buf);
457 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
458 do_save = FALSE;
459 break;
462 dyn_buf = heap_strdupWtoA(cookie_container->data);
463 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
464 heap_free(dyn_buf);
465 do_save = FALSE;
466 break;
468 heap_free(dyn_buf);
469 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) {
470 do_save = FALSE;
471 break;
474 dyn_buf = heap_strdupWtoA(container->domain->domain);
475 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
476 heap_free(dyn_buf);
477 do_save = FALSE;
478 break;
480 heap_free(dyn_buf);
482 dyn_buf = heap_strdupWtoA(container->path);
483 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) {
484 heap_free(dyn_buf);
485 do_save = FALSE;
486 break;
488 heap_free(dyn_buf);
490 sprintf(buf, "\n%u\n%u\n%u\n%u\n%u\n*\n", cookie_container->flags,
491 cookie_container->expiry.dwLowDateTime, cookie_container->expiry.dwHighDateTime,
492 cookie_container->create.dwLowDateTime, cookie_container->create.dwHighDateTime);
493 if(!WriteFile(cookie_handle, buf, strlen(buf), &bytes_written, NULL)) {
494 do_save = FALSE;
495 break;
499 CloseHandle(cookie_handle);
500 if(!do_save) {
501 ERR("error saving cookie file\n");
502 DeleteFileW(cookie_file);
503 return FALSE;
506 memset(&time, 0, sizeof(time));
507 return CommitUrlCacheEntryW(cookie_url, cookie_file, time, time, 0, NULL, 0, txtW, 0);
510 static BOOL COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen)
512 URL_COMPONENTSW UrlComponents;
514 UrlComponents.lpszExtraInfo = NULL;
515 UrlComponents.lpszPassword = NULL;
516 UrlComponents.lpszScheme = NULL;
517 UrlComponents.lpszUrlPath = path;
518 UrlComponents.lpszUserName = NULL;
519 UrlComponents.lpszHostName = hostName;
520 UrlComponents.dwExtraInfoLength = 0;
521 UrlComponents.dwPasswordLength = 0;
522 UrlComponents.dwSchemeLength = 0;
523 UrlComponents.dwUserNameLength = 0;
524 UrlComponents.dwHostNameLength = hostNameLen;
525 UrlComponents.dwUrlPathLength = pathLen;
527 if (!InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents)) return FALSE;
529 /* discard the webpage off the end of the path */
530 if (UrlComponents.dwUrlPathLength)
532 if (path[UrlComponents.dwUrlPathLength - 1] != '/')
534 WCHAR *ptr;
535 if ((ptr = strrchrW(path, '/'))) *(++ptr) = 0;
536 else
538 path[0] = '/';
539 path[1] = 0;
543 else if (pathLen >= 2)
545 path[0] = '/';
546 path[1] = 0;
548 return TRUE;
551 typedef struct {
552 cookie_t **cookies;
553 unsigned cnt;
554 unsigned size;
556 unsigned string_len;
557 } cookie_set_t;
559 static DWORD get_cookie(const WCHAR *host, const WCHAR *path, DWORD flags, cookie_set_t *res)
561 static const WCHAR empty_path[] = { '/',0 };
563 WCHAR *ptr, subpath[INTERNET_MAX_PATH_LENGTH];
564 const WCHAR *p;
565 cookie_domain_t *domain;
566 cookie_container_t *container;
567 unsigned len;
568 FILETIME tm;
570 GetSystemTimeAsFileTime(&tm);
572 len = strlenW(host);
573 p = host+len;
574 while(p>host && p[-1]!='.') p--;
575 while(p != host) {
576 p--;
577 while(p>host && p[-1]!='.') p--;
578 if(p == host) break;
580 load_persistent_cookie(p, empty_path);
583 len = strlenW(path);
584 assert(len+1 < INTERNET_MAX_PATH_LENGTH);
585 memcpy(subpath, path, (len+1)*sizeof(WCHAR));
586 ptr = subpath+len;
587 do {
588 *ptr = 0;
589 load_persistent_cookie(host, subpath);
591 ptr--;
592 while(ptr>subpath && ptr[-1]!='/') ptr--;
593 }while(ptr != subpath);
595 domain = get_cookie_domain(host, FALSE);
596 if(!domain) {
597 TRACE("Unknown host %s\n", debugstr_w(host));
598 return ERROR_NO_MORE_ITEMS;
601 for(domain = get_cookie_domain(host, FALSE); domain; domain = domain->parent) {
602 TRACE("Trying %s domain...\n", debugstr_w(domain->domain));
604 LIST_FOR_EACH_ENTRY(container, &domain->path_list, cookie_container_t, entry) {
605 struct list *cursor, *cursor2;
607 TRACE("path %s\n", debugstr_w(container->path));
609 if(!cookie_match_path(container, path))
610 continue;
612 TRACE("found domain %p\n", domain->domain);
614 LIST_FOR_EACH_SAFE(cursor, cursor2, &container->cookie_list) {
615 cookie_t *cookie_iter = LIST_ENTRY(cursor, cookie_t, entry);
617 /* check for expiry */
618 if((cookie_iter->expiry.dwLowDateTime != 0 || cookie_iter->expiry.dwHighDateTime != 0)
619 && CompareFileTime(&tm, &cookie_iter->expiry) > 0) {
620 TRACE("Found expired cookie. deleting\n");
621 delete_cookie(cookie_iter);
622 continue;
625 if((cookie_iter->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY))
626 continue;
629 if(!res->size) {
630 res->cookies = heap_alloc(4*sizeof(*res->cookies));
631 if(!res->cookies)
632 continue;
633 res->size = 4;
634 }else if(res->cnt == res->size) {
635 cookie_t **new_cookies = heap_realloc(res->cookies, res->size*2*sizeof(*res->cookies));
636 if(!new_cookies)
637 continue;
638 res->cookies = new_cookies;
639 res->size *= 2;
642 if(res->cnt)
643 res->string_len += 2; /* '; ' */
644 res->cookies[res->cnt++] = cookie_iter;
646 res->string_len += strlenW(cookie_iter->name);
647 if(*cookie_iter->data)
648 res->string_len += 1 /* = */ + strlenW(cookie_iter->data);
653 return ERROR_SUCCESS;
656 static void cookie_set_to_string(const cookie_set_t *cookie_set, WCHAR *str)
658 WCHAR *ptr = str;
659 unsigned i, len;
661 for(i=0; i<cookie_set->cnt; i++) {
662 if(i) {
663 *ptr++ = ';';
664 *ptr++ = ' ';
667 len = strlenW(cookie_set->cookies[i]->name);
668 memcpy(ptr, cookie_set->cookies[i]->name, len*sizeof(WCHAR));
669 ptr += len;
671 if(*cookie_set->cookies[i]->data) {
672 *ptr++ = '=';
673 len = strlenW(cookie_set->cookies[i]->data);
674 memcpy(ptr, cookie_set->cookies[i]->data, len*sizeof(WCHAR));
675 ptr += len;
679 assert(ptr-str == cookie_set->string_len);
680 TRACE("%s\n", debugstr_wn(str, ptr-str));
683 DWORD get_cookie_header(const WCHAR *host, const WCHAR *path, WCHAR **ret)
685 cookie_set_t cookie_set = {0};
686 DWORD res;
688 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
690 EnterCriticalSection(&cookie_cs);
692 res = get_cookie(host, path, INTERNET_COOKIE_HTTPONLY, &cookie_set);
693 if(res != ERROR_SUCCESS) {
694 LeaveCriticalSection(&cookie_cs);
695 return res;
698 if(cookie_set.cnt) {
699 WCHAR *header, *ptr;
701 ptr = header = heap_alloc(sizeof(cookieW) + (cookie_set.string_len + 3 /* crlf0 */) * sizeof(WCHAR));
702 if(header) {
703 memcpy(ptr, cookieW, sizeof(cookieW));
704 ptr += sizeof(cookieW)/sizeof(*cookieW);
706 cookie_set_to_string(&cookie_set, ptr);
707 heap_free(cookie_set.cookies);
708 ptr += cookie_set.string_len;
710 *ptr++ = '\r';
711 *ptr++ = '\n';
712 *ptr++ = 0;
714 *ret = header;
715 }else {
716 res = ERROR_NOT_ENOUGH_MEMORY;
718 }else {
719 *ret = NULL;
722 LeaveCriticalSection(&cookie_cs);
723 return ERROR_SUCCESS;
726 /***********************************************************************
727 * InternetGetCookieExW (WININET.@)
729 * Retrieve cookie from the specified url
731 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
732 * So it won't be implemented here.
734 * RETURNS
735 * TRUE on success
736 * FALSE on failure
739 BOOL WINAPI InternetGetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
740 LPWSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
742 WCHAR host[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
743 cookie_set_t cookie_set = {0};
744 DWORD res;
745 BOOL ret;
747 TRACE("(%s, %s, %p, %p, %x, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), lpCookieData, lpdwSize, flags, reserved);
749 if (flags)
750 FIXME("flags 0x%08x not supported\n", flags);
752 if (!lpszUrl)
754 SetLastError(ERROR_INVALID_PARAMETER);
755 return FALSE;
758 host[0] = 0;
759 ret = COOKIE_crackUrlSimple(lpszUrl, host, sizeof(host)/sizeof(host[0]), path, sizeof(path)/sizeof(path[0]));
760 if (!ret || !host[0]) {
761 SetLastError(ERROR_INVALID_PARAMETER);
762 return FALSE;
765 EnterCriticalSection(&cookie_cs);
767 res = get_cookie(host, path, flags, &cookie_set);
768 if(res != ERROR_SUCCESS) {
769 LeaveCriticalSection(&cookie_cs);
770 SetLastError(res);
771 return FALSE;
774 if(cookie_set.cnt) {
775 if(!lpCookieData || cookie_set.string_len+1 > *lpdwSize) {
776 *lpdwSize = (cookie_set.string_len + 1) * sizeof(WCHAR);
777 TRACE("returning %u\n", *lpdwSize);
778 if(lpCookieData) {
779 SetLastError(ERROR_INSUFFICIENT_BUFFER);
780 ret = FALSE;
782 }else {
783 *lpdwSize = cookie_set.string_len + 1;
784 cookie_set_to_string(&cookie_set, lpCookieData);
785 lpCookieData[cookie_set.string_len] = 0;
787 }else {
788 TRACE("no cookies found for %s\n", debugstr_w(host));
789 SetLastError(ERROR_NO_MORE_ITEMS);
790 ret = FALSE;
793 heap_free(cookie_set.cookies);
794 LeaveCriticalSection(&cookie_cs);
795 return ret;
798 /***********************************************************************
799 * InternetGetCookieW (WININET.@)
801 * Retrieve cookie for the specified URL.
803 BOOL WINAPI InternetGetCookieW(const WCHAR *url, const WCHAR *name, WCHAR *data, DWORD *size)
805 TRACE("(%s, %s, %s, %p)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data), size);
807 return InternetGetCookieExW(url, name, data, size, 0, NULL);
810 /***********************************************************************
811 * InternetGetCookieExA (WININET.@)
813 * Retrieve cookie from the specified url
815 * RETURNS
816 * TRUE on success
817 * FALSE on failure
820 BOOL WINAPI InternetGetCookieExA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
821 LPSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved)
823 WCHAR *url, *name;
824 DWORD len, size;
825 BOOL r;
827 TRACE("(%s %s %p %p(%u) %x %p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
828 lpCookieData, lpdwSize, lpdwSize ? *lpdwSize : 0, flags, reserved);
830 url = heap_strdupAtoW(lpszUrl);
831 name = heap_strdupAtoW(lpszCookieName);
833 r = InternetGetCookieExW( url, name, NULL, &len, flags, reserved );
834 if( r )
836 WCHAR *szCookieData;
838 szCookieData = heap_alloc(len * sizeof(WCHAR));
839 if( !szCookieData )
841 r = FALSE;
843 else
845 r = InternetGetCookieExW( url, name, szCookieData, &len, flags, reserved );
847 if(r) {
848 size = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, NULL, 0, NULL, NULL);
849 if(lpCookieData) {
850 if(*lpdwSize >= size) {
851 WideCharToMultiByte( CP_ACP, 0, szCookieData, len, lpCookieData, *lpdwSize, NULL, NULL);
852 }else {
853 SetLastError(ERROR_INSUFFICIENT_BUFFER);
854 r = FALSE;
857 *lpdwSize = size;
860 heap_free( szCookieData );
863 heap_free( name );
864 heap_free( url );
865 return r;
868 /***********************************************************************
869 * InternetGetCookieA (WININET.@)
871 * See InternetGetCookieW.
873 BOOL WINAPI InternetGetCookieA(const char *url, const char *name, char *data, DWORD *size)
875 TRACE("(%s, %s, %s, %p)\n", debugstr_a(url), debugstr_a(name), debugstr_a(data), size);
877 return InternetGetCookieExA(url, name, data, size, 0, NULL);
880 /***********************************************************************
881 * IsDomainLegalCookieDomainW (WININET.@)
883 BOOL WINAPI IsDomainLegalCookieDomainW( LPCWSTR s1, LPCWSTR s2 )
885 DWORD s1_len, s2_len;
887 FIXME("(%s, %s) semi-stub\n", debugstr_w(s1), debugstr_w(s2));
889 if (!s1 || !s2)
891 SetLastError(ERROR_INVALID_PARAMETER);
892 return FALSE;
894 if (s1[0] == '.' || !s1[0] || s2[0] == '.' || !s2[0])
896 SetLastError(ERROR_INVALID_NAME);
897 return FALSE;
899 if(!strchrW(s1, '.') || !strchrW(s2, '.'))
900 return FALSE;
902 s1_len = strlenW(s1);
903 s2_len = strlenW(s2);
904 if (s1_len > s2_len)
905 return FALSE;
907 if (strncmpiW(s1, s2+s2_len-s1_len, s1_len) || (s2_len>s1_len && s2[s2_len-s1_len-1]!='.'))
909 SetLastError(ERROR_INVALID_PARAMETER);
910 return FALSE;
913 return TRUE;
916 DWORD set_cookie(const WCHAR *domain, const WCHAR *path, const WCHAR *cookie_name, const WCHAR *cookie_data, DWORD flags)
918 cookie_container_t *container;
919 cookie_t *thisCookie;
920 LPWSTR data, value;
921 WCHAR *ptr;
922 FILETIME expiry, create;
923 BOOL expired = FALSE, update_persistent = FALSE;
924 DWORD cookie_flags = 0;
926 TRACE("%s %s %s=%s %x\n", debugstr_w(domain), debugstr_w(path), debugstr_w(cookie_name), debugstr_w(cookie_data), flags);
928 value = data = heap_strdupW(cookie_data);
929 if (!data)
931 ERR("could not allocate the cookie data buffer\n");
932 return COOKIE_STATE_UNKNOWN;
935 memset(&expiry,0,sizeof(expiry));
936 GetSystemTimeAsFileTime(&create);
938 /* lots of information can be parsed out of the cookie value */
940 ptr = data;
941 for (;;)
943 static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
944 static const WCHAR szPath[] = {'p','a','t','h','=',0};
945 static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','=',0};
946 static const WCHAR szSecure[] = {'s','e','c','u','r','e',0};
947 static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y',0};
948 static const WCHAR szVersion[] = {'v','e','r','s','i','o','n','=',0};
950 if (!(ptr = strchrW(ptr,';'))) break;
951 *ptr++ = 0;
953 if (value != data) heap_free(value);
954 value = heap_alloc((ptr - data) * sizeof(WCHAR));
955 if (value == NULL)
957 heap_free(data);
958 ERR("could not allocate the cookie value buffer\n");
959 return COOKIE_STATE_UNKNOWN;
961 strcpyW(value, data);
963 while (*ptr == ' ') ptr++; /* whitespace */
965 if (strncmpiW(ptr, szDomain, 7) == 0)
967 WCHAR *end_ptr;
969 ptr += sizeof(szDomain)/sizeof(szDomain[0])-1;
970 if(*ptr == '.')
971 ptr++;
972 end_ptr = strchrW(ptr, ';');
973 if(end_ptr)
974 *end_ptr = 0;
976 if(!IsDomainLegalCookieDomainW(ptr, domain))
978 if(value != data)
979 heap_free(value);
980 heap_free(data);
981 return COOKIE_STATE_UNKNOWN;
984 if(end_ptr)
985 *end_ptr = ';';
987 domain = ptr;
988 TRACE("Parsing new domain %s\n",debugstr_w(domain));
990 else if (strncmpiW(ptr, szPath, 5) == 0)
992 ptr+=strlenW(szPath);
993 path = ptr;
994 TRACE("Parsing new path %s\n",debugstr_w(path));
996 else if (strncmpiW(ptr, szExpires, 8) == 0)
998 SYSTEMTIME st;
999 ptr+=strlenW(szExpires);
1000 if (InternetTimeToSystemTimeW(ptr, &st, 0))
1002 SystemTimeToFileTime(&st, &expiry);
1004 if (CompareFileTime(&create,&expiry) > 0)
1006 TRACE("Cookie already expired.\n");
1007 expired = TRUE;
1011 else if (strncmpiW(ptr, szSecure, 6) == 0)
1013 FIXME("secure not handled (%s)\n",debugstr_w(ptr));
1014 ptr += strlenW(szSecure);
1016 else if (strncmpiW(ptr, szHttpOnly, 8) == 0)
1018 if(!(flags & INTERNET_COOKIE_HTTPONLY)) {
1019 WARN("HTTP only cookie added without INTERNET_COOKIE_HTTPONLY flag\n");
1020 heap_free(data);
1021 if (value != data) heap_free(value);
1022 SetLastError(ERROR_INVALID_OPERATION);
1023 return COOKIE_STATE_REJECT;
1026 cookie_flags |= INTERNET_COOKIE_HTTPONLY;
1027 ptr += strlenW(szHttpOnly);
1029 else if (strncmpiW(ptr, szVersion, 8) == 0)
1031 FIXME("version not handled (%s)\n",debugstr_w(ptr));
1032 ptr += strlenW(szVersion);
1034 else if (*ptr)
1036 FIXME("Unknown additional option %s\n",debugstr_w(ptr));
1037 break;
1041 EnterCriticalSection(&cookie_cs);
1043 load_persistent_cookie(domain, path);
1045 container = get_cookie_container(domain, path, !expired);
1046 if(!container) {
1047 heap_free(data);
1048 if (value != data) heap_free(value);
1049 LeaveCriticalSection(&cookie_cs);
1050 return COOKIE_STATE_ACCEPT;
1053 if(!expiry.dwLowDateTime && !expiry.dwHighDateTime)
1054 cookie_flags |= INTERNET_COOKIE_IS_SESSION;
1055 else
1056 update_persistent = TRUE;
1058 if ((thisCookie = find_cookie(container, cookie_name)))
1060 if ((thisCookie->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) {
1061 WARN("An attempt to override httponly cookie\n");
1062 SetLastError(ERROR_INVALID_OPERATION);
1063 heap_free(data);
1064 if (value != data) heap_free(value);
1065 return COOKIE_STATE_REJECT;
1068 if (!(thisCookie->flags & INTERNET_COOKIE_IS_SESSION))
1069 update_persistent = TRUE;
1070 delete_cookie(thisCookie);
1073 TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_w(cookie_name),
1074 debugstr_w(value), debugstr_w(container->domain->domain),debugstr_w(container->path));
1076 if (!expired) {
1077 cookie_t *new_cookie;
1079 new_cookie = alloc_cookie(cookie_name, value, expiry, create, cookie_flags);
1080 if(!new_cookie) {
1081 heap_free(data);
1082 if (value != data) heap_free(value);
1083 LeaveCriticalSection(&cookie_cs);
1084 return COOKIE_STATE_UNKNOWN;
1087 add_cookie(container, new_cookie);
1089 heap_free(data);
1090 if (value != data) heap_free(value);
1092 if (!update_persistent || save_persistent_cookie(container))
1094 LeaveCriticalSection(&cookie_cs);
1095 return COOKIE_STATE_ACCEPT;
1097 LeaveCriticalSection(&cookie_cs);
1098 return COOKIE_STATE_UNKNOWN;
1101 /***********************************************************************
1102 * InternetSetCookieExW (WININET.@)
1104 * Sets cookie for the specified url
1106 DWORD WINAPI InternetSetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
1107 LPCWSTR lpCookieData, DWORD flags, DWORD_PTR reserved)
1109 BOOL ret;
1110 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH];
1112 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_w(lpszUrl), debugstr_w(lpszCookieName),
1113 debugstr_w(lpCookieData), flags, reserved);
1115 if (flags & ~INTERNET_COOKIE_HTTPONLY)
1116 FIXME("flags %x not supported\n", flags);
1118 if (!lpszUrl || !lpCookieData)
1120 SetLastError(ERROR_INVALID_PARAMETER);
1121 return COOKIE_STATE_UNKNOWN;
1124 hostName[0] = 0;
1125 ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
1126 if (!ret || !hostName[0]) return COOKIE_STATE_UNKNOWN;
1128 if (!lpszCookieName)
1130 WCHAR *cookie, *data;
1131 DWORD res;
1133 cookie = heap_strdupW(lpCookieData);
1134 if (!cookie)
1136 SetLastError(ERROR_OUTOFMEMORY);
1137 return COOKIE_STATE_UNKNOWN;
1140 /* some apps (or is it us??) try to add a cookie with no cookie name, but
1141 * the cookie data in the form of name[=data].
1143 if (!(data = strchrW(cookie, '='))) data = cookie + strlenW(cookie);
1144 else *data++ = 0;
1146 res = set_cookie(hostName, path, cookie, data, flags);
1148 heap_free(cookie);
1149 return res;
1151 return set_cookie(hostName, path, lpszCookieName, lpCookieData, flags);
1154 /***********************************************************************
1155 * InternetSetCookieW (WININET.@)
1157 * Sets a cookie for the specified URL.
1159 BOOL WINAPI InternetSetCookieW(const WCHAR *url, const WCHAR *name, const WCHAR *data)
1161 TRACE("(%s, %s, %s)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data));
1163 return InternetSetCookieExW(url, name, data, 0, 0) == COOKIE_STATE_ACCEPT;
1166 /***********************************************************************
1167 * InternetSetCookieA (WININET.@)
1169 * Sets cookie for the specified url
1171 * RETURNS
1172 * TRUE on success
1173 * FALSE on failure
1176 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
1177 LPCSTR lpCookieData)
1179 LPWSTR data, url, name;
1180 BOOL r;
1182 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
1183 debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
1185 url = heap_strdupAtoW(lpszUrl);
1186 name = heap_strdupAtoW(lpszCookieName);
1187 data = heap_strdupAtoW(lpCookieData);
1189 r = InternetSetCookieW( url, name, data );
1191 heap_free( data );
1192 heap_free( name );
1193 heap_free( url );
1194 return r;
1197 /***********************************************************************
1198 * InternetSetCookieExA (WININET.@)
1200 * See InternetSetCookieExW.
1202 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData,
1203 DWORD dwFlags, DWORD_PTR dwReserved)
1205 WCHAR *data, *url, *name;
1206 DWORD r;
1208 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_a(lpszURL), debugstr_a(lpszCookieName),
1209 debugstr_a(lpszCookieData), dwFlags, dwReserved);
1211 url = heap_strdupAtoW(lpszURL);
1212 name = heap_strdupAtoW(lpszCookieName);
1213 data = heap_strdupAtoW(lpszCookieData);
1215 r = InternetSetCookieExW(url, name, data, dwFlags, dwReserved);
1217 heap_free( data );
1218 heap_free( name );
1219 heap_free( url );
1220 return r;
1223 /***********************************************************************
1224 * InternetClearAllPerSiteCookieDecisions (WININET.@)
1226 * Clears all per-site decisions about cookies.
1228 * RETURNS
1229 * TRUE on success
1230 * FALSE on failure
1233 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID )
1235 FIXME("stub\n");
1236 return TRUE;
1239 /***********************************************************************
1240 * InternetEnumPerSiteCookieDecisionA (WININET.@)
1242 * See InternetEnumPerSiteCookieDecisionW.
1244 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, ULONG *pcSiteNameSize,
1245 ULONG *pdwDecision, ULONG dwIndex )
1247 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1248 debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1249 return FALSE;
1252 /***********************************************************************
1253 * InternetEnumPerSiteCookieDecisionW (WININET.@)
1255 * Enumerates all per-site decisions about cookies.
1257 * RETURNS
1258 * TRUE on success
1259 * FALSE on failure
1262 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, ULONG *pcSiteNameSize,
1263 ULONG *pdwDecision, ULONG dwIndex )
1265 FIXME("(%s, %p, %p, 0x%08x) stub\n",
1266 debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
1267 return FALSE;
1270 /***********************************************************************
1271 * InternetGetPerSiteCookieDecisionA (WININET.@)
1273 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, ULONG *pResult )
1275 FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult);
1276 return FALSE;
1279 /***********************************************************************
1280 * InternetGetPerSiteCookieDecisionW (WININET.@)
1282 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, ULONG *pResult )
1284 FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult);
1285 return FALSE;
1288 /***********************************************************************
1289 * InternetSetPerSiteCookieDecisionA (WININET.@)
1291 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision )
1293 FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision);
1294 return FALSE;
1297 /***********************************************************************
1298 * InternetSetPerSiteCookieDecisionW (WININET.@)
1300 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision )
1302 FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision);
1303 return FALSE;
1306 void free_cookie(void)
1308 DeleteCriticalSection(&cookie_cs);