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