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 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
);
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 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
)
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
);