kernel32: FindExSearchLimitToDirectories has no effect on FindFirstFileEx.
[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 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wininet.h"
37 #include "winerror.h"
38
39 #include "wine/debug.h"
40 #include "internet.h"
41
42 #define RESPONSE_TIMEOUT        30            /* FROM internet.c */
43
44
45 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
46
47 /* FIXME
48  *     Cookies are currently memory only.
49  *     Cookies are NOT THREAD SAFE
50  *     Cookies could use A LOT OF MEMORY. We need some kind of memory management here!
51  *     Cookies should care about the expiry time
52  */
53
54 typedef struct _cookie_domain cookie_domain;
55 typedef struct _cookie cookie;
56
57 struct _cookie
58 {
59     struct list entry;
60
61     struct _cookie_domain *parent;
62
63     LPWSTR lpCookieName;
64     LPWSTR lpCookieData;
65     time_t expiry; /* FIXME: not used */
66 };
67
68 struct _cookie_domain
69 {
70     struct list entry;
71
72     LPWSTR lpCookieDomain;
73     LPWSTR lpCookiePath;
74     struct list cookie_list;
75 };
76
77 static struct list domain_list = LIST_INIT(domain_list);
78
79 static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data);
80 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName);
81 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain);
82 static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path);
83 static void COOKIE_deleteDomain(cookie_domain *deadDomain);
84
85
86 /* adds a cookie to the domain */
87 static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data)
88 {
89     cookie *newCookie = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie));
90
91     list_init(&newCookie->entry);
92     newCookie->lpCookieName = NULL;
93     newCookie->lpCookieData = NULL;
94
95     if (name)
96     {
97         newCookie->lpCookieName = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1)*sizeof(WCHAR));
98         lstrcpyW(newCookie->lpCookieName, name);
99     }
100     if (data)
101     {
102         newCookie->lpCookieData = HeapAlloc(GetProcessHeap(), 0, (strlenW(data) + 1)*sizeof(WCHAR));
103         lstrcpyW(newCookie->lpCookieData, data);
104     }
105
106     TRACE("added cookie %p (data is %s)\n", newCookie, debugstr_w(data) );
107
108     list_add_tail(&domain->cookie_list, &newCookie->entry);
109     newCookie->parent = domain;
110     return newCookie;
111 }
112
113
114 /* finds a cookie in the domain matching the cookie name */
115 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName)
116 {
117     struct list * cursor;
118     TRACE("(%p, %s)\n", domain, debugstr_w(lpszCookieName));
119
120     LIST_FOR_EACH(cursor, &domain->cookie_list)
121     {
122         cookie *searchCookie = LIST_ENTRY(cursor, cookie, entry);
123         BOOL candidate = TRUE;
124         if (candidate && lpszCookieName)
125         {
126             if (candidate && !searchCookie->lpCookieName)
127                 candidate = FALSE;
128             if (candidate && strcmpW(lpszCookieName, searchCookie->lpCookieName) != 0)
129                 candidate = FALSE;
130         }
131         if (candidate)
132             return searchCookie;
133     }
134     return NULL;
135 }
136
137 /* removes a cookie from the list, if its the last cookie we also remove the domain */
138 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain)
139 {
140     HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieName);
141     HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieData);
142     list_remove(&deadCookie->entry);
143
144     /* special case: last cookie, lets remove the domain to save memory */
145     if (list_empty(&deadCookie->parent->cookie_list) && deleteDomain)
146         COOKIE_deleteDomain(deadCookie->parent);
147     HeapFree(GetProcessHeap(), 0, deadCookie);
148 }
149
150 /* allocates a domain and adds it to the end */
151 static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path)
152 {
153     cookie_domain *newDomain = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie_domain));
154
155     list_init(&newDomain->entry);
156     list_init(&newDomain->cookie_list);
157     newDomain->lpCookieDomain = NULL;
158     newDomain->lpCookiePath = NULL;
159
160     if (domain)
161     {
162         newDomain->lpCookieDomain = HeapAlloc(GetProcessHeap(), 0, (strlenW(domain) + 1)*sizeof(WCHAR));
163         strcpyW(newDomain->lpCookieDomain, domain);
164     }
165     if (path)
166     {
167         newDomain->lpCookiePath = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1)*sizeof(WCHAR));
168         lstrcpyW(newDomain->lpCookiePath, path);
169     }
170
171     list_add_tail(&domain_list, &newDomain->entry);
172
173     TRACE("Adding domain: %p\n", newDomain);
174     return newDomain;
175 }
176
177 static void COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen)
178 {
179     URL_COMPONENTSW UrlComponents;
180
181     UrlComponents.lpszExtraInfo = NULL;
182     UrlComponents.lpszPassword = NULL;
183     UrlComponents.lpszScheme = NULL;
184     UrlComponents.lpszUrlPath = path;
185     UrlComponents.lpszUserName = NULL;
186     UrlComponents.lpszHostName = hostName;
187     UrlComponents.dwHostNameLength = hostNameLen;
188     UrlComponents.dwUrlPathLength = pathLen;
189
190     InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents);
191 }
192
193 /* match a domain. domain must match if the domain is not NULL. path must match if the path is not NULL */
194 static BOOL COOKIE_matchDomain(LPCWSTR lpszCookieDomain, LPCWSTR lpszCookiePath,
195                                cookie_domain *searchDomain, BOOL allow_partial)
196 {
197     TRACE("searching on domain %p\n", searchDomain);
198         if (lpszCookieDomain)
199         {
200             if (!searchDomain->lpCookieDomain)
201             return FALSE;
202
203             TRACE("comparing domain %s with %s\n", 
204             debugstr_w(lpszCookieDomain), 
205             debugstr_w(searchDomain->lpCookieDomain));
206
207         if (allow_partial && !strstrW(lpszCookieDomain, searchDomain->lpCookieDomain))
208             return FALSE;
209         else if (!allow_partial && lstrcmpW(lpszCookieDomain, searchDomain->lpCookieDomain) != 0)
210             return FALSE;
211         }
212     if (lpszCookiePath)
213     {
214         TRACE("comparing paths: %s with %s\n", debugstr_w(lpszCookiePath), debugstr_w(searchDomain->lpCookiePath));
215         if (!searchDomain->lpCookiePath)
216             return FALSE;
217         if (strcmpW(lpszCookiePath, searchDomain->lpCookiePath))
218             return FALSE;
219         }
220         return TRUE;
221 }
222
223 /* remove a domain from the list and delete it */
224 static void COOKIE_deleteDomain(cookie_domain *deadDomain)
225 {
226     struct list * cursor;
227     while ((cursor = list_tail(&deadDomain->cookie_list)))
228     {
229         COOKIE_deleteCookie(LIST_ENTRY(cursor, cookie, entry), FALSE);
230         list_remove(cursor);
231     }
232
233     HeapFree(GetProcessHeap(), 0, deadDomain->lpCookieDomain);
234     HeapFree(GetProcessHeap(), 0, deadDomain->lpCookiePath);
235
236     list_remove(&deadDomain->entry);
237
238     HeapFree(GetProcessHeap(), 0, deadDomain);
239 }
240
241 /***********************************************************************
242  *           InternetGetCookieW (WININET.@)
243  *
244  * Retrieve cookie from the specified url
245  *
246  *  It should be noted that on windows the lpszCookieName parameter is "not implemented".
247  *    So it won't be implemented here.
248  *
249  * RETURNS
250  *    TRUE  on success
251  *    FALSE on failure
252  *
253  */
254 BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
255     LPWSTR lpCookieData, LPDWORD lpdwSize)
256 {
257     struct list * cursor;
258     int cnt = 0, domain_count = 0;
259     int cookie_count = 0;
260     WCHAR hostName[2048], path[2048];
261
262     TRACE("(%s, %s, %p, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName),
263           lpCookieData, lpdwSize);
264
265     if (!lpszUrl)
266     {
267         SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
268         return FALSE;
269     }
270
271     COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
272
273     LIST_FOR_EACH(cursor, &domain_list)
274     {
275         cookie_domain *cookiesDomain = LIST_ENTRY(cursor, cookie_domain, entry);
276         if (COOKIE_matchDomain(hostName, NULL /* FIXME: path */, cookiesDomain, TRUE))
277         {
278             struct list * cursor;
279             domain_count++;
280             TRACE("found domain %p\n", cookiesDomain);
281     
282             LIST_FOR_EACH(cursor, &cookiesDomain->cookie_list)
283             {
284                 cookie *thisCookie = LIST_ENTRY(cursor, cookie, entry);
285                 if (lpCookieData == NULL) /* return the size of the buffer required to lpdwSize */
286                 {
287                     if (cookie_count != 0)
288                         cnt += 2; /* '; ' */
289                     cnt += strlenW(thisCookie->lpCookieName);
290                     cnt += 1; /* = */
291                     cnt += strlenW(thisCookie->lpCookieData);
292                 }
293                 else
294                 {
295                     static const WCHAR szsc[] = { ';',' ',0 };
296                     static const WCHAR szpseq[] = { '%','s','=','%','s',0 };
297                     if (cookie_count != 0)
298                         cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szsc);
299                     cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szpseq,
300                                     thisCookie->lpCookieName,
301                                     thisCookie->lpCookieData);
302                     TRACE("Cookie: %s=%s\n", debugstr_w(thisCookie->lpCookieName), debugstr_w(thisCookie->lpCookieData));
303                 }
304                 cookie_count++;
305             }
306         }
307     }
308
309     if (!domain_count)
310     {
311         TRACE("no cookies found for %s\n", debugstr_w(hostName));
312         SetLastError(ERROR_NO_MORE_ITEMS);
313         return FALSE;
314     }
315
316     if (lpCookieData == NULL)
317     {
318         cnt += 1; /* NULL */
319         *lpdwSize = cnt*sizeof(WCHAR);
320         TRACE("returning\n");
321         return TRUE;
322     }
323
324     *lpdwSize = (cnt + 1)*sizeof(WCHAR);
325
326     TRACE("Returning %i (from %i domains): %s\n", cnt, domain_count,
327            debugstr_w(lpCookieData));
328
329     return (cnt ? TRUE : FALSE);
330 }
331
332
333 /***********************************************************************
334  *           InternetGetCookieA (WININET.@)
335  *
336  * Retrieve cookie from the specified url
337  *
338  * RETURNS
339  *    TRUE  on success
340  *    FALSE on failure
341  *
342  */
343 BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
344     LPSTR lpCookieData, LPDWORD lpdwSize)
345 {
346     DWORD len;
347     LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
348     BOOL r;
349
350     TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
351         lpCookieData);
352
353     if( lpszUrl )
354     {
355         len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 );
356         szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
357         MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len );
358     }
359
360     if( lpszCookieName )
361     {
362         len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 );
363         szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
364         MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len );
365     }
366
367     r = InternetGetCookieW( szUrl, szCookieName, NULL, &len );
368     if( r )
369     {
370         szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
371         if( !szCookieData )
372         {
373             r = FALSE;
374         }
375         else
376         {
377             r = InternetGetCookieW( szUrl, szCookieName, szCookieData, &len );
378
379             *lpdwSize = WideCharToMultiByte( CP_ACP, 0, szCookieData, len,
380                                     lpCookieData, *lpdwSize, NULL, NULL );
381         }
382     }
383
384     HeapFree( GetProcessHeap(), 0, szCookieData );
385     HeapFree( GetProcessHeap(), 0, szCookieName );
386     HeapFree( GetProcessHeap(), 0, szUrl );
387
388     return r;
389 }
390
391
392 /***********************************************************************
393  *           InternetSetCookieW (WININET.@)
394  *
395  * Sets cookie for the specified url
396  *
397  * RETURNS
398  *    TRUE  on success
399  *    FALSE on failure
400  *
401  */
402 BOOL WINAPI InternetSetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
403     LPCWSTR lpCookieData)
404 {
405     cookie_domain *thisCookieDomain = NULL;
406     cookie *thisCookie;
407     WCHAR hostName[2048], path[2048];
408     struct list * cursor;
409
410     TRACE("(%s,%s,%s)\n", debugstr_w(lpszUrl),
411         debugstr_w(lpszCookieName), debugstr_w(lpCookieData));
412
413     if (!lpCookieData)
414     {
415         SetLastError(ERROR_INVALID_PARAMETER);
416         return FALSE;
417     }
418     if (!lpszCookieName)
419     {
420         /* some apps (or is it us??) try to add a cookie with no cookie name, but
421          * the cookie data in the form of name=data. */
422         /* FIXME, probably a bug here, for now I don't care */
423         WCHAR *ourCookieName, *ourCookieData;
424         int ourCookieNameSize;
425         BOOL ret;
426
427         if (!(ourCookieData = strchrW(lpCookieData, '=')))
428         {
429             TRACE("something terribly wrong with cookie data %s\n", 
430                    debugstr_w(ourCookieData));
431             return FALSE;
432         }
433         ourCookieNameSize = ourCookieData - lpCookieData;
434         ourCookieData += 1;
435         ourCookieName = HeapAlloc(GetProcessHeap(), 0, 
436                               (ourCookieNameSize + 1)*sizeof(WCHAR));
437         memcpy(ourCookieName, lpCookieData, ourCookieNameSize * sizeof(WCHAR));
438         ourCookieName[ourCookieNameSize] = '\0';
439         TRACE("setting (hacked) cookie of %s, %s\n",
440                debugstr_w(ourCookieName), debugstr_w(ourCookieData));
441         ret = InternetSetCookieW(lpszUrl, ourCookieName, ourCookieData);
442         HeapFree(GetProcessHeap(), 0, ourCookieName);
443         return ret;
444     }
445
446     COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
447
448     LIST_FOR_EACH(cursor, &domain_list)
449     {
450         thisCookieDomain = LIST_ENTRY(cursor, cookie_domain, entry);
451         if (COOKIE_matchDomain(hostName, NULL /* FIXME: path */, thisCookieDomain, FALSE))
452             break;
453         thisCookieDomain = NULL;
454     }
455     if (!thisCookieDomain)
456         thisCookieDomain = COOKIE_addDomain(hostName, path);
457
458     if ((thisCookie = COOKIE_findCookie(thisCookieDomain, lpszCookieName)))
459         COOKIE_deleteCookie(thisCookie, FALSE);
460
461     thisCookie = COOKIE_addCookie(thisCookieDomain, lpszCookieName, lpCookieData);
462     return TRUE;
463 }
464
465
466 /***********************************************************************
467  *           InternetSetCookieA (WININET.@)
468  *
469  * Sets cookie for the specified url
470  *
471  * RETURNS
472  *    TRUE  on success
473  *    FALSE on failure
474  *
475  */
476 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
477     LPCSTR lpCookieData)
478 {
479     DWORD len;
480     LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
481     BOOL r;
482
483     TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
484         debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
485
486     if( lpszUrl )
487     {
488         len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 );
489         szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
490         MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len );
491     }
492
493     if( lpszCookieName )
494     {
495         len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 );
496         szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
497         MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len );
498     }
499
500     if( lpCookieData )
501     {
502         len = MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, NULL, 0 );
503         szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
504         MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, szCookieData, len );
505     }
506
507     r = InternetSetCookieW( szUrl, szCookieName, szCookieData );
508
509     HeapFree( GetProcessHeap(), 0, szCookieData );
510     HeapFree( GetProcessHeap(), 0, szCookieName );
511     HeapFree( GetProcessHeap(), 0, szUrl );
512
513     return r;
514 }
515
516 /***********************************************************************
517  *           InternetSetCookieExA (WININET.@)
518  *
519  * See InternetSetCookieExW.
520  */
521 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData,
522                                    DWORD dwFlags, DWORD_PTR dwReserved)
523 {
524     FIXME("(%s, %s, %s, 0x%08x, 0x%08lx) stub\n",
525           debugstr_a(lpszURL), debugstr_a(lpszCookieName), debugstr_a(lpszCookieData),
526           dwFlags, dwReserved);
527     return TRUE;
528 }
529
530 /***********************************************************************
531  *           InternetSetCookieExW (WININET.@)
532  *
533  * Sets a cookie for the specified URL.
534  *
535  * RETURNS
536  *    TRUE  on success
537  *    FALSE on failure
538  *
539  */
540 DWORD WINAPI InternetSetCookieExW( LPCWSTR lpszURL, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData,
541                                    DWORD dwFlags, DWORD_PTR dwReserved)
542 {
543     FIXME("(%s, %s, %s, 0x%08x, 0x%08lx) stub\n",
544           debugstr_w(lpszURL), debugstr_w(lpszCookieName), debugstr_w(lpszCookieData),
545           dwFlags, dwReserved);
546     return TRUE;
547 }
548
549 /***********************************************************************
550  *           InternetGetCookieExA (WININET.@)
551  *
552  * See InternetGetCookieExW.
553  */
554 BOOL WINAPI InternetGetCookieExA( LPCSTR pchURL, LPCSTR pchCookieName, LPSTR pchCookieData,
555                                   LPDWORD pcchCookieData, DWORD dwFlags, LPVOID lpReserved)
556 {
557     FIXME("(%s, %s, %s, %p, 0x%08x, %p) stub\n",
558           debugstr_a(pchURL), debugstr_a(pchCookieName), debugstr_a(pchCookieData),
559           pcchCookieData, dwFlags, lpReserved);
560     return FALSE;
561 }
562
563 /***********************************************************************
564  *           InternetGetCookieExW (WININET.@)
565  *
566  * Retrieve cookie for the specified URL.
567  *
568  * RETURNS
569  *    TRUE  on success
570  *    FALSE on failure
571  *
572  */
573 BOOL WINAPI InternetGetCookieExW( LPCWSTR pchURL, LPCWSTR pchCookieName, LPWSTR pchCookieData,
574                                   LPDWORD pcchCookieData, DWORD dwFlags, LPVOID lpReserved)
575 {
576     FIXME("(%s, %s, %s, %p, 0x%08x, %p) stub\n",
577           debugstr_w(pchURL), debugstr_w(pchCookieName), debugstr_w(pchCookieData),
578           pcchCookieData, dwFlags, lpReserved);
579     return FALSE;
580 }
581
582 /***********************************************************************
583  *           InternetClearAllPerSiteCookieDecisions (WININET.@)
584  *
585  * Clears all per-site decisions about cookies.
586  *
587  * RETURNS
588  *    TRUE  on success
589  *    FALSE on failure
590  *
591  */
592 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID )
593 {
594     FIXME("stub\n");
595     return TRUE;
596 }
597
598 /***********************************************************************
599  *           InternetEnumPerSiteCookieDecisionA (WININET.@)
600  *
601  * See InternetEnumPerSiteCookieDecisionW.
602  */
603 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, unsigned long *pcSiteNameSize,
604                                                 unsigned long *pdwDecision, unsigned long dwIndex )
605 {
606     FIXME("(%s, %p, %p, 0x%08lx) stub\n",
607           debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
608     return FALSE;
609 }
610
611 /***********************************************************************
612  *           InternetEnumPerSiteCookieDecisionW (WININET.@)
613  *
614  * Enumerates all per-site decisions about cookies.
615  *
616  * RETURNS
617  *    TRUE  on success
618  *    FALSE on failure
619  *
620  */
621 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, unsigned long *pcSiteNameSize,
622                                                 unsigned long *pdwDecision, unsigned long dwIndex )
623 {
624     FIXME("(%s, %p, %p, 0x%08lx) stub\n",
625           debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
626     return FALSE;
627 }
628
629 /***********************************************************************
630  *           InternetGetPerSiteCookieDecisionA (WININET.@)
631  */
632 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, unsigned long *pResult )
633 {
634     FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult);
635     return FALSE;
636 }
637
638 /***********************************************************************
639  *           InternetGetPerSiteCookieDecisionW (WININET.@)
640  */
641 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, unsigned long *pResult )
642 {
643     FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult);
644     return FALSE;
645 }
646
647 /***********************************************************************
648  *           InternetSetPerSiteCookieDecisionA (WININET.@)
649  */
650 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision )
651 {
652     FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision);
653     return FALSE;
654 }
655
656 /***********************************************************************
657  *           InternetSetPerSiteCookieDecisionW (WININET.@)
658  */
659 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision )
660 {
661     FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision);
662     return FALSE;
663 }