Minor fixes.
[wine/testsucceed.git] / dlls / wininet / cookie.c
blobe7f20ca46395891716effb4f8d0c5f0defa40534
1 /*
2 * Wininet - cookie handling stuff
4 * Copyright 2002 TransGaming Technologies Inc.
6 * David Hammerton
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "config.h"
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wininet.h"
36 #include "winerror.h"
38 #include "wine/debug.h"
39 #include "internet.h"
41 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */
44 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
46 /* FIXME
47 * Cookies are currently memory only.
48 * Cookies are NOT THREAD SAFE
49 * Cookies could use ALOT OF MEMORY. We need some kind of memory management here!
50 * Cookies should care about the expiry time
53 typedef struct _cookie_domain cookie_domain;
54 typedef struct _cookie cookie;
56 struct _cookie
58 struct _cookie *next;
59 struct _cookie *prev;
61 struct _cookie_domain *parent;
63 LPWSTR lpCookieName;
64 LPWSTR lpCookieData;
65 time_t expiry; /* FIXME: not used */
68 struct _cookie_domain
70 struct _cookie_domain *next;
71 struct _cookie_domain *prev;
73 LPWSTR lpCookieDomain;
74 LPWSTR lpCookiePath;
75 cookie *cookie_tail;
78 static cookie_domain *cookieDomainTail;
80 static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data);
81 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName);
82 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain);
83 static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path);
84 static cookie_domain *COOKIE_addDomainFromUrl(LPCWSTR lpszUrl);
85 static cookie_domain *COOKIE_findNextDomain(LPCWSTR lpszCookieDomain, LPCWSTR lpszCookiePath,
86 cookie_domain *prev_domain, BOOL allow_partial);
87 static cookie_domain *COOKIE_findNextDomainFromUrl(LPCWSTR lpszUrl, cookie_domain *prev_domain,
88 BOOL allow_partial);
89 static void COOKIE_deleteDomain(cookie_domain *deadDomain);
92 /* adds a cookie to the domain */
93 static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data)
95 cookie *newCookie = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie));
97 newCookie->next = NULL;
98 newCookie->prev = NULL;
99 newCookie->lpCookieName = NULL;
100 newCookie->lpCookieData = NULL;
102 if (name)
104 newCookie->lpCookieName = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1)*sizeof(WCHAR));
105 lstrcpyW(newCookie->lpCookieName, name);
107 if (data)
109 newCookie->lpCookieData = HeapAlloc(GetProcessHeap(), 0, (strlenW(data) + 1)*sizeof(WCHAR));
110 lstrcpyW(newCookie->lpCookieData, data);
113 TRACE("added cookie %p (data is %s)\n", newCookie, debugstr_w(data) );
115 newCookie->prev = domain->cookie_tail;
116 newCookie->parent = domain;
117 domain->cookie_tail = newCookie;
118 return newCookie;
122 /* finds a cookie in the domain matching the cookie name */
123 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName)
125 cookie *searchCookie = domain->cookie_tail;
126 TRACE("(%p, %s)\n", domain, debugstr_w(lpszCookieName));
128 while (searchCookie)
130 BOOL candidate = TRUE;
131 if (candidate && lpszCookieName)
133 if (candidate && !searchCookie->lpCookieName)
134 candidate = FALSE;
135 if (candidate && strcmpW(lpszCookieName, searchCookie->lpCookieName) != 0)
136 candidate = FALSE;
138 if (candidate)
139 return searchCookie;
140 searchCookie = searchCookie->prev;
142 return NULL;
145 /* removes a cookie from the list, if its the last cookie we also remove the domain */
146 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain)
148 if (deadCookie->lpCookieName)
149 HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieName);
150 if (deadCookie->lpCookieData)
151 HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieData);
152 if (deadCookie->prev)
153 deadCookie->prev->next = deadCookie->next;
154 if (deadCookie->next)
155 deadCookie->next->prev = deadCookie->prev;
157 if (deadCookie == deadCookie->parent->cookie_tail)
159 /* special case: last cookie, lets remove the domain to save memory */
160 deadCookie->parent->cookie_tail = deadCookie->prev;
161 if (!deadCookie->parent->cookie_tail && deleteDomain)
162 COOKIE_deleteDomain(deadCookie->parent);
166 /* allocates a domain and adds it to the end */
167 static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path)
169 cookie_domain *newDomain = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie_domain));
171 newDomain->next = NULL;
172 newDomain->prev = NULL;
173 newDomain->cookie_tail = NULL;
174 newDomain->lpCookieDomain = NULL;
175 newDomain->lpCookiePath = NULL;
177 if (domain)
179 newDomain->lpCookieDomain = HeapAlloc(GetProcessHeap(), 0, (strlenW(domain) + 1)*sizeof(WCHAR));
180 strcpyW(newDomain->lpCookieDomain, domain);
182 if (path)
184 newDomain->lpCookiePath = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1)*sizeof(WCHAR));
185 lstrcpyW(newDomain->lpCookiePath, path);
188 newDomain->prev = cookieDomainTail;
189 cookieDomainTail = newDomain;
190 TRACE("Adding domain: %p\n", newDomain);
191 return newDomain;
194 static cookie_domain *COOKIE_addDomainFromUrl(LPCWSTR lpszUrl)
196 WCHAR hostName[2048], path[2048];
197 URL_COMPONENTSW UrlComponents;
199 UrlComponents.lpszExtraInfo = NULL;
200 UrlComponents.lpszPassword = NULL;
201 UrlComponents.lpszScheme = NULL;
202 UrlComponents.lpszUrlPath = path;
203 UrlComponents.lpszUserName = NULL;
204 UrlComponents.lpszHostName = hostName;
205 UrlComponents.dwHostNameLength = 2048;
206 UrlComponents.dwUrlPathLength = 2048;
208 InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents);
210 TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_w(UrlComponents.lpszHostName),
211 debugstr_w(UrlComponents.lpszUrlPath));
213 /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
214 UrlComponents.lpszUrlPath = NULL;
216 return COOKIE_addDomain(UrlComponents.lpszHostName, UrlComponents.lpszUrlPath);
219 /* find a domain. domain must match if its not NULL. path must match if its not NULL */
220 static cookie_domain *COOKIE_findNextDomain(LPCWSTR lpszCookieDomain, LPCWSTR lpszCookiePath,
221 cookie_domain *prev_domain, BOOL allow_partial)
223 cookie_domain *searchDomain;
225 if (prev_domain)
227 if(!prev_domain->prev)
229 TRACE("no more domains available, it would seem.\n");
230 return NULL;
232 searchDomain = prev_domain->prev;
234 else searchDomain = cookieDomainTail;
236 while (searchDomain)
238 BOOL candidate = TRUE;
239 TRACE("searching on domain %p\n", searchDomain);
240 if (candidate && lpszCookieDomain)
242 if (candidate && !searchDomain->lpCookieDomain)
243 candidate = FALSE;
244 TRACE("candidate! (%p)\n", searchDomain->lpCookieDomain);
245 TRACE("comparing domain %s with %s\n",
246 debugstr_w(lpszCookieDomain),
247 debugstr_w(searchDomain->lpCookieDomain));
248 if (candidate && allow_partial && !strstrW(lpszCookieDomain, searchDomain->lpCookieDomain))
249 candidate = FALSE;
250 else if (candidate && !allow_partial &&
251 lstrcmpW(lpszCookieDomain, searchDomain->lpCookieDomain) != 0)
252 candidate = FALSE;
254 if (candidate && lpszCookiePath)
256 TRACE("comparing paths\n");
257 if (candidate && !searchDomain->lpCookiePath)
258 candidate = FALSE;
259 if (candidate &&
260 strcmpW(lpszCookiePath, searchDomain->lpCookiePath))
261 candidate = FALSE;
263 if (candidate)
265 TRACE("returning the domain %p\n", searchDomain);
266 return searchDomain;
268 searchDomain = searchDomain->prev;
270 TRACE("found no domain, returning NULL\n");
271 return NULL;
274 static cookie_domain *COOKIE_findNextDomainFromUrl(LPCWSTR lpszUrl, cookie_domain *previous_domain,
275 BOOL allow_partial)
277 WCHAR hostName[2048], path[2048];
278 URL_COMPONENTSW UrlComponents;
280 UrlComponents.lpszExtraInfo = NULL;
281 UrlComponents.lpszPassword = NULL;
282 UrlComponents.lpszScheme = NULL;
283 UrlComponents.lpszUrlPath = path;
284 UrlComponents.lpszUserName = NULL;
285 UrlComponents.lpszHostName = hostName;
286 UrlComponents.dwHostNameLength = 2048;
287 UrlComponents.dwUrlPathLength = 2048;
289 InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents);
291 TRACE("Url cracked. Domain: %s, Path: %s.\n",
292 debugstr_w(UrlComponents.lpszHostName),
293 debugstr_w(UrlComponents.lpszUrlPath));
295 /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
296 UrlComponents.lpszUrlPath = NULL;
298 return COOKIE_findNextDomain(UrlComponents.lpszHostName, UrlComponents.lpszUrlPath,
299 previous_domain, allow_partial);
302 /* remove a domain from the list and delete it */
303 static void COOKIE_deleteDomain(cookie_domain *deadDomain)
305 while (deadDomain->cookie_tail)
306 COOKIE_deleteCookie(deadDomain->cookie_tail, FALSE);
307 if (deadDomain->lpCookieDomain)
308 HeapFree(GetProcessHeap(), 0, deadDomain->lpCookieDomain);
309 if (deadDomain->lpCookiePath)
310 HeapFree(GetProcessHeap(), 0, deadDomain->lpCookiePath);
311 if (deadDomain->prev)
312 deadDomain->prev->next = deadDomain->next;
313 if (deadDomain->next)
314 deadDomain->next->prev = deadDomain->prev;
316 if (cookieDomainTail == deadDomain)
317 cookieDomainTail = deadDomain->prev;
318 HeapFree(GetProcessHeap(), 0, deadDomain);
321 /***********************************************************************
322 * InternetGetCookieW (WININET.@)
324 * Retrieve cookie from the specified url
326 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
327 * So it won't be implemented here.
329 * RETURNS
330 * TRUE on success
331 * FALSE on failure
334 BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
335 LPWSTR lpCookieData, LPDWORD lpdwSize)
337 cookie_domain *cookiesDomain = NULL;
338 cookie *thisCookie;
339 int cnt = 0, domain_count = 0;
340 /* Ok, this is just ODD!. During my tests, it appears M$ like to send out
341 * a cookie called 'MtrxTracking' to some urls. Also returns it from InternetGetCookie.
342 * I'm not exactly sure what to make of this, so its here for now.
343 * It'd be nice to know what exactly is going on, M$ tracking users? Does this need
344 * to be unique? Should I generate a random number here? etc.
346 static const WCHAR TrackingString[] = {
347 'M','t','r','x','T','r','a','c','k','i','n','g','I','D','=',
348 '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5',
349 '6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1', 0 };
350 static const WCHAR szps[] = { '%','s',0 };
352 TRACE("(%s, %s, %p, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName),
353 lpCookieData, lpdwSize);
355 if (lpCookieData)
356 cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szps, TrackingString);
357 else
358 cnt += strlenW(TrackingString);
360 while ((cookiesDomain = COOKIE_findNextDomainFromUrl(lpszUrl, cookiesDomain, TRUE)))
362 domain_count++;
363 TRACE("found domain %p\n", cookiesDomain);
365 thisCookie = cookiesDomain->cookie_tail;
366 if (lpCookieData == NULL) /* return the size of the buffer required to lpdwSize */
368 while (thisCookie)
370 cnt += 2; /* '; ' */
371 cnt += strlenW(thisCookie->lpCookieName);
372 cnt += 1; /* = */
373 cnt += strlenW(thisCookie->lpCookieData);
375 thisCookie = thisCookie->prev;
378 while (thisCookie)
380 static const WCHAR szsc[] = { ';',' ',0 };
381 static const WCHAR szpseq[] = { '%','s','=','%','s',0 };
382 cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szsc);
383 cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szpseq,
384 thisCookie->lpCookieName,
385 thisCookie->lpCookieData);
387 thisCookie = thisCookie->prev;
390 if (lpCookieData == NULL)
392 cnt += 1; /* NULL */
393 *lpdwSize = cnt;
394 TRACE("returning\n");
395 return TRUE;
398 if (!domain_count)
399 return FALSE;
401 *lpdwSize = cnt + 1;
403 TRACE("Returning %i (from %i domains): %s\n", cnt, domain_count,
404 debugstr_w(lpCookieData));
406 return (cnt ? TRUE : FALSE);
410 /***********************************************************************
411 * InternetGetCookieA (WININET.@)
413 * Retrieve cookie from the specified url
415 * RETURNS
416 * TRUE on success
417 * FALSE on failure
420 BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
421 LPSTR lpCookieData, LPDWORD lpdwSize)
423 DWORD len;
424 LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
425 BOOL r;
427 TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
428 lpCookieData);
430 if( lpszUrl )
432 len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 );
433 szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
434 MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len );
437 if( lpszCookieName )
439 len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 );
440 szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
441 MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len );
444 r = InternetGetCookieW( szUrl, szCookieName, NULL, &len );
445 if( r )
447 szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
448 if( !szCookieData )
449 return FALSE;
451 r = InternetGetCookieW( szUrl, szCookieName, szCookieData, &len );
453 *lpdwSize = WideCharToMultiByte( CP_ACP, 0, szCookieData, len,
454 lpCookieData, *lpdwSize, NULL, NULL );
457 if( szCookieData )
458 HeapFree( GetProcessHeap(), 0, szCookieData );
459 if( szCookieName )
460 HeapFree( GetProcessHeap(), 0, szCookieName );
461 if( szUrl )
462 HeapFree( GetProcessHeap(), 0, szUrl );
464 return r;
468 /***********************************************************************
469 * InternetSetCookieW (WININET.@)
471 * Sets cookie for the specified url
473 * RETURNS
474 * TRUE on success
475 * FALSE on failure
478 BOOL WINAPI InternetSetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
479 LPCWSTR lpCookieData)
481 cookie *thisCookie;
482 cookie_domain *thisCookieDomain;
484 TRACE("(%s,%s,%s)\n", debugstr_w(lpszUrl),
485 debugstr_w(lpszCookieName), debugstr_w(lpCookieData));
487 if (!lpCookieData || !strlenW(lpCookieData))
489 TRACE("no cookie data, not adding\n");
490 return FALSE;
492 if (!lpszCookieName)
494 /* some apps (or is it us??) try to add a cookie with no cookie name, but
495 * the cookie data in the form of name=data. */
496 /* FIXME, probably a bug here, for now I don't care */
497 WCHAR *ourCookieName, *ourCookieData;
498 int ourCookieNameSize;
499 BOOL ret;
500 if (!(ourCookieData = strchrW(lpCookieData, '=')))
502 TRACE("something terribly wrong with cookie data %s\n",
503 debugstr_w(ourCookieData));
504 return FALSE;
506 ourCookieNameSize = ourCookieData - lpCookieData;
507 ourCookieData += 1;
508 ourCookieName = HeapAlloc(GetProcessHeap(), 0,
509 (ourCookieNameSize + 1)*sizeof(WCHAR));
510 strncpyW(ourCookieName, ourCookieData, ourCookieNameSize);
511 ourCookieName[ourCookieNameSize] = '\0';
512 TRACE("setting (hacked) cookie of %s, %s\n",
513 debugstr_w(ourCookieName), debugstr_w(ourCookieData));
514 ret = InternetSetCookieW(lpszUrl, ourCookieName, ourCookieData);
515 HeapFree(GetProcessHeap(), 0, ourCookieName);
516 return ret;
519 if (!(thisCookieDomain = COOKIE_findNextDomainFromUrl(lpszUrl, NULL, FALSE)))
520 thisCookieDomain = COOKIE_addDomainFromUrl(lpszUrl);
522 if ((thisCookie = COOKIE_findCookie(thisCookieDomain, lpszCookieName)))
523 COOKIE_deleteCookie(thisCookie, FALSE);
525 thisCookie = COOKIE_addCookie(thisCookieDomain, lpszCookieName, lpCookieData);
526 return TRUE;
530 /***********************************************************************
531 * InternetSetCookieA (WININET.@)
533 * Sets cookie for the specified url
535 * RETURNS
536 * TRUE on success
537 * FALSE on failure
540 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
541 LPCSTR lpCookieData)
543 DWORD len;
544 LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
545 BOOL r;
547 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
548 debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
550 if( lpszUrl )
552 len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 );
553 szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
554 MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len );
557 if( lpszCookieName )
559 len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 );
560 szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
561 MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len );
564 if( lpCookieData )
566 len = MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, NULL, 0 );
567 szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
568 MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, szCookieData, len );
571 r = InternetSetCookieW( szUrl, szCookieName, szCookieData );
573 if( szCookieData )
574 HeapFree( GetProcessHeap(), 0, szCookieData );
575 if( szCookieName )
576 HeapFree( GetProcessHeap(), 0, szCookieName );
577 if( szUrl )
578 HeapFree( GetProcessHeap(), 0, szUrl );
580 return r;