2 * Wininet - cookie handling stuff
4 * Copyright 2002 TransGaming Technologies Inc.
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
38 #include "wine/debug.h"
41 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */
44 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
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;
61 struct _cookie_domain *parent;
65 time_t expiry; /* FIXME: not used */
70 struct _cookie_domain *next;
71 struct _cookie_domain *prev;
73 LPWSTR lpCookieDomain;
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,
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;
104 newCookie->lpCookieName = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1)*sizeof(WCHAR));
105 lstrcpyW(newCookie->lpCookieName, name);
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;
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));
130 BOOL candidate = TRUE;
131 if (candidate && lpszCookieName)
133 if (candidate && !searchCookie->lpCookieName)
135 if (candidate && strcmpW(lpszCookieName, searchCookie->lpCookieName) != 0)
140 searchCookie = searchCookie->prev;
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;
179 newDomain->lpCookieDomain = HeapAlloc(GetProcessHeap(), 0, (strlenW(domain) + 1)*sizeof(WCHAR));
180 strcpyW(newDomain->lpCookieDomain, domain);
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);
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;
227 if(!prev_domain->prev)
229 TRACE("no more domains available, it would seem.\n");
232 searchDomain = prev_domain->prev;
234 else searchDomain = cookieDomainTail;
238 BOOL candidate = TRUE;
239 TRACE("searching on domain %p\n", searchDomain);
240 if (candidate && lpszCookieDomain)
242 if (candidate && !searchDomain->lpCookieDomain)
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))
250 else if (candidate && !allow_partial &&
251 lstrcmpW(lpszCookieDomain, searchDomain->lpCookieDomain) != 0)
254 if (candidate && lpszCookiePath)
256 TRACE("comparing paths\n");
257 if (candidate && !searchDomain->lpCookiePath)
260 strcmpW(lpszCookiePath, searchDomain->lpCookiePath))
265 TRACE("returning the domain %p\n", searchDomain);
268 searchDomain = searchDomain->prev;
270 TRACE("found no domain, returning NULL\n");
274 static cookie_domain *COOKIE_findNextDomainFromUrl(LPCWSTR lpszUrl, cookie_domain *previous_domain,
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.
334 BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
335 LPWSTR lpCookieData, LPDWORD lpdwSize)
337 cookie_domain *cookiesDomain = NULL;
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 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 WCHAR szps[] = { '%','s',0 };
352 TRACE("(%s, %s, %p, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName),
353 lpCookieData, lpdwSize);
356 cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szps, TrackingString);
358 cnt += strlenW(TrackingString);
360 while ((cookiesDomain = COOKIE_findNextDomainFromUrl(lpszUrl, cookiesDomain, TRUE)))
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 */
371 cnt += strlenW(thisCookie->lpCookieName);
373 cnt += strlenW(thisCookie->lpCookieData);
375 thisCookie = thisCookie->prev;
380 WCHAR szsc[] = { ';',' ',0 };
381 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)
394 TRACE("returning\n");
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
420 BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
421 LPSTR lpCookieData, LPDWORD lpdwSize)
424 LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
427 TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
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 );
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 );
447 szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
451 r = InternetGetCookieW( szUrl, szCookieName, szCookieData, &len );
453 *lpdwSize = WideCharToMultiByte( CP_ACP, 0, szCookieData, len,
454 lpCookieData, *lpdwSize, NULL, NULL );
458 HeapFree( GetProcessHeap(), 0, szCookieData );
460 HeapFree( GetProcessHeap(), 0, szCookieName );
462 HeapFree( GetProcessHeap(), 0, szUrl );
468 /***********************************************************************
469 * InternetSetCookieW (WININET.@)
471 * Sets cookie for the specified url
478 BOOL WINAPI InternetSetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
479 LPCWSTR lpCookieData)
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");
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;
500 if (!(ourCookieData = strchrW(lpCookieData, '=')))
502 TRACE("something terribly wrong with cookie data %s\n",
503 debugstr_w(ourCookieData));
506 ourCookieNameSize = ourCookieData - lpCookieData;
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);
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);
530 /***********************************************************************
531 * InternetSetCookieA (WININET.@)
533 * Sets cookie for the specified url
540 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
544 LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
547 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
548 debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
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 );
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 );
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 );
574 HeapFree( GetProcessHeap(), 0, szCookieData );
576 HeapFree( GetProcessHeap(), 0, szCookieName );
578 HeapFree( GetProcessHeap(), 0, szUrl );