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