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
37 #include "wine/debug.h"
40 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */
43 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
46 * Cookies are currently memory only.
47 * Cookies are NOT THREAD SAFE
48 * Cookies could use ALOT OF MEMORY. We need some kind of memory management here!
49 * Cookies should care about the expiry time
52 typedef struct _cookie_domain cookie_domain
;
53 typedef struct _cookie cookie
;
60 struct _cookie_domain
*parent
;
64 time_t expiry
; /* FIXME: not used */
69 struct _cookie_domain
*next
;
70 struct _cookie_domain
*prev
;
77 static cookie_domain
*cookieDomainTail
;
79 static cookie
*COOKIE_addCookie(cookie_domain
*domain
, LPCSTR name
, LPCSTR data
);
80 static cookie
*COOKIE_findCookie(cookie_domain
*domain
, LPCSTR lpszCookieName
);
81 static void COOKIE_deleteCookie(cookie
*deadCookie
, BOOL deleteDomain
);
82 static cookie_domain
*COOKIE_addDomain(LPCSTR domain
, LPCSTR path
);
83 static cookie_domain
*COOKIE_addDomainFromUrl(LPCSTR lpszUrl
);
84 static cookie_domain
*COOKIE_findNextDomain(LPCSTR lpszCookieDomain
, LPCSTR lpszCookiePath
,
85 cookie_domain
*prev_domain
, BOOL allow_partial
);
86 static cookie_domain
*COOKIE_findNextDomainFromUrl(LPCSTR lpszUrl
, cookie_domain
*prev_domain
,
88 static void COOKIE_deleteDomain(cookie_domain
*deadDomain
);
91 /* adds a cookie to the domain */
92 static cookie
*COOKIE_addCookie(cookie_domain
*domain
, LPCSTR name
, LPCSTR data
)
94 cookie
*newCookie
= HeapAlloc(GetProcessHeap(), 0, sizeof(cookie
));
96 newCookie
->next
= NULL
;
97 newCookie
->prev
= NULL
;
98 newCookie
->lpCookieName
= NULL
;
99 newCookie
->lpCookieData
= NULL
;
103 newCookie
->lpCookieName
= HeapAlloc(GetProcessHeap(), 0, strlen(name
) + 1);
104 strcpy(newCookie
->lpCookieName
, name
);
108 newCookie
->lpCookieData
= HeapAlloc(GetProcessHeap(), 0, strlen(data
) + 1);
109 strcpy(newCookie
->lpCookieData
, data
);
112 TRACE("added cookie %p (data is %s)\n", newCookie
, data
);
114 newCookie
->prev
= domain
->cookie_tail
;
115 newCookie
->parent
= domain
;
116 domain
->cookie_tail
= newCookie
;
121 /* finds a cookie in the domain matching the cookie name */
122 static cookie
*COOKIE_findCookie(cookie_domain
*domain
, LPCSTR lpszCookieName
)
124 cookie
*searchCookie
= domain
->cookie_tail
;
125 TRACE("(%p, %s)\n", domain
, debugstr_a(lpszCookieName
));
129 BOOL candidate
= TRUE
;
130 if (candidate
&& lpszCookieName
)
132 if (candidate
&& !searchCookie
->lpCookieName
)
134 if (candidate
&& strcmp(lpszCookieName
, searchCookie
->lpCookieName
) != 0)
139 searchCookie
= searchCookie
->prev
;
144 /* removes a cookie from the list, if its the last cookie we also remove the domain */
145 static void COOKIE_deleteCookie(cookie
*deadCookie
, BOOL deleteDomain
)
147 if (deadCookie
->lpCookieName
)
148 HeapFree(GetProcessHeap(), 0, deadCookie
->lpCookieName
);
149 if (deadCookie
->lpCookieData
)
150 HeapFree(GetProcessHeap(), 0, deadCookie
->lpCookieData
);
151 if (deadCookie
->prev
)
152 deadCookie
->prev
->next
= deadCookie
->next
;
153 if (deadCookie
->next
)
154 deadCookie
->next
->prev
= deadCookie
->prev
;
156 if (deadCookie
== deadCookie
->parent
->cookie_tail
)
158 /* special case: last cookie, lets remove the domain to save memory */
159 deadCookie
->parent
->cookie_tail
= deadCookie
->prev
;
160 if (!deadCookie
->parent
->cookie_tail
&& deleteDomain
)
161 COOKIE_deleteDomain(deadCookie
->parent
);
165 /* allocates a domain and adds it to the end */
166 static cookie_domain
*COOKIE_addDomain(LPCSTR domain
, LPCSTR path
)
168 cookie_domain
*newDomain
= HeapAlloc(GetProcessHeap(), 0, sizeof(cookie_domain
));
170 newDomain
->next
= NULL
;
171 newDomain
->prev
= NULL
;
172 newDomain
->cookie_tail
= NULL
;
173 newDomain
->lpCookieDomain
= NULL
;
174 newDomain
->lpCookiePath
= NULL
;
178 newDomain
->lpCookieDomain
= HeapAlloc(GetProcessHeap(), 0, strlen(domain
) + 1);
179 strcpy(newDomain
->lpCookieDomain
, domain
);
183 newDomain
->lpCookiePath
= HeapAlloc(GetProcessHeap(), 0, strlen(path
) + 1);
184 strcpy(newDomain
->lpCookiePath
, path
);
187 newDomain
->prev
= cookieDomainTail
;
188 cookieDomainTail
= newDomain
;
189 TRACE("Adding domain: %p\n", newDomain
);
193 static cookie_domain
*COOKIE_addDomainFromUrl(LPCSTR lpszUrl
)
195 char hostName
[2048], path
[2048];
196 URL_COMPONENTSA UrlComponents
;
198 UrlComponents
.lpszExtraInfo
= NULL
;
199 UrlComponents
.lpszPassword
= NULL
;
200 UrlComponents
.lpszScheme
= NULL
;
201 UrlComponents
.lpszUrlPath
= path
;
202 UrlComponents
.lpszUserName
= NULL
;
203 UrlComponents
.lpszHostName
= hostName
;
204 UrlComponents
.dwHostNameLength
= 2048;
205 UrlComponents
.dwUrlPathLength
= 2048;
207 InternetCrackUrlA(lpszUrl
, 0, 0, &UrlComponents
);
209 TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_a(UrlComponents
.lpszHostName
),
210 debugstr_a(UrlComponents
.lpszUrlPath
));
212 /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
213 UrlComponents
.lpszUrlPath
= NULL
;
215 return COOKIE_addDomain(UrlComponents
.lpszHostName
, UrlComponents
.lpszUrlPath
);
218 /* find a domain. domain must match if its not NULL. path must match if its not NULL */
219 static cookie_domain
*COOKIE_findNextDomain(LPCSTR lpszCookieDomain
, LPCSTR lpszCookiePath
,
220 cookie_domain
*prev_domain
, BOOL allow_partial
)
222 cookie_domain
*searchDomain
;
226 if(!prev_domain
->prev
)
228 TRACE("no more domains available, it would seem.\n");
231 searchDomain
= prev_domain
->prev
;
233 else searchDomain
= cookieDomainTail
;
237 BOOL candidate
= TRUE
;
238 TRACE("searching on domain %p\n", searchDomain
);
239 if (candidate
&& lpszCookieDomain
)
241 if (candidate
&& !searchDomain
->lpCookieDomain
)
243 TRACE("candidate! (%p)\n", searchDomain
->lpCookieDomain
);
244 TRACE("comparing domain %s with %s\n", lpszCookieDomain
, searchDomain
->lpCookieDomain
);
245 if (candidate
&& allow_partial
&& !strstr(lpszCookieDomain
, searchDomain
->lpCookieDomain
))
247 else if (candidate
&& !allow_partial
&&
248 strcmp(lpszCookieDomain
, searchDomain
->lpCookieDomain
) != 0)
251 if (candidate
&& lpszCookiePath
)
252 { TRACE("comparing paths\n");
253 if (candidate
&& !searchDomain
->lpCookiePath
)
255 if (candidate
&& strcmp(lpszCookiePath
, searchDomain
->lpCookiePath
) != 0)
260 TRACE("returning the domain %p\n", searchDomain
);
263 searchDomain
= searchDomain
->prev
;
265 TRACE("found no domain, returning NULL\n");
269 static cookie_domain
*COOKIE_findNextDomainFromUrl(LPCSTR lpszUrl
, cookie_domain
*previous_domain
,
272 char hostName
[2048], path
[2048];
273 URL_COMPONENTSA UrlComponents
;
275 UrlComponents
.lpszExtraInfo
= NULL
;
276 UrlComponents
.lpszPassword
= NULL
;
277 UrlComponents
.lpszScheme
= NULL
;
278 UrlComponents
.lpszUrlPath
= path
;
279 UrlComponents
.lpszUserName
= NULL
;
280 UrlComponents
.lpszHostName
= hostName
;
281 UrlComponents
.dwHostNameLength
= 2048;
282 UrlComponents
.dwUrlPathLength
= 2048;
284 InternetCrackUrlA(lpszUrl
, 0, 0, &UrlComponents
);
286 TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_a(UrlComponents
.lpszHostName
),
287 debugstr_a(UrlComponents
.lpszUrlPath
));
289 /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
290 UrlComponents
.lpszUrlPath
= NULL
;
292 return COOKIE_findNextDomain(UrlComponents
.lpszHostName
, UrlComponents
.lpszUrlPath
,
293 previous_domain
, allow_partial
);
296 /* remove a domain from the list and delete it */
297 static void COOKIE_deleteDomain(cookie_domain
*deadDomain
)
299 while (deadDomain
->cookie_tail
)
300 COOKIE_deleteCookie(deadDomain
->cookie_tail
, FALSE
);
301 if (deadDomain
->lpCookieDomain
)
302 HeapFree(GetProcessHeap(), 0, deadDomain
->lpCookieDomain
);
303 if (deadDomain
->lpCookiePath
)
304 HeapFree(GetProcessHeap(), 0, deadDomain
->lpCookiePath
);
305 if (deadDomain
->prev
)
306 deadDomain
->prev
->next
= deadDomain
->next
;
307 if (deadDomain
->next
)
308 deadDomain
->next
->prev
= deadDomain
->prev
;
310 if (cookieDomainTail
== deadDomain
)
311 cookieDomainTail
= deadDomain
->prev
;
312 HeapFree(GetProcessHeap(), 0, deadDomain
);
315 /***********************************************************************
316 * InternetGetCookieA (WININET.@)
318 * Retrieve cookie from the specified url
320 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
321 * So it won't be implemented here.
328 BOOL WINAPI
InternetGetCookieA(LPCSTR lpszUrl
, LPCSTR lpszCookieName
,
329 LPSTR lpCookieData
, LPDWORD lpdwSize
)
331 cookie_domain
*cookiesDomain
= NULL
;
333 int cnt
= 0, domain_count
= 0;
334 /* Ok, this is just ODD!. During my tests, it appears M$ like to send out
335 * a cookie called 'MtrxTracking' to some urls. Also returns it from InternetGetCookie.
336 * I'm not exactly sure what to make of this, so its here for now.
337 * It'd be nice to know what exactly is going on, M$ tracking users? Does this need
338 * to be unique? Should I generate a random number here? etc.
340 char *TrackingString
= "MtrxTrackingID=01234567890123456789012345678901";
342 TRACE("(%s, %s, %p, %p)\n", debugstr_a(lpszUrl
),debugstr_a(lpszCookieName
),
343 lpCookieData
, lpdwSize
);
346 cnt
+= snprintf(lpCookieData
+ cnt
, *lpdwSize
- cnt
, "%s", TrackingString
);
348 cnt
+= strlen(TrackingString
);
350 while ((cookiesDomain
= COOKIE_findNextDomainFromUrl(lpszUrl
, cookiesDomain
, TRUE
)))
353 TRACE("found domain %p\n", cookiesDomain
);
355 thisCookie
= cookiesDomain
->cookie_tail
;
356 if (lpCookieData
== NULL
) /* return the size of the buffer required to lpdwSize */
361 cnt
+= strlen(thisCookie
->lpCookieName
);
363 cnt
+= strlen(thisCookie
->lpCookieData
);
365 thisCookie
= thisCookie
->prev
;
370 cnt
+= snprintf(lpCookieData
+ cnt
, *lpdwSize
- cnt
, "; ");
371 cnt
+= snprintf(lpCookieData
+ cnt
, *lpdwSize
- cnt
, "%s=%s", thisCookie
->lpCookieName
,
372 thisCookie
->lpCookieData
);
374 thisCookie
= thisCookie
->prev
;
377 if (lpCookieData
== NULL
)
381 TRACE("returning\n");
390 TRACE("Returning %i (from %i domains): %s\n", cnt
, domain_count
, lpCookieData
);
392 return (cnt
? TRUE
: FALSE
);
396 /***********************************************************************
397 * InternetGetCookieW (WININET.@)
399 * Retrieve cookie from the specified url
406 BOOL WINAPI
InternetGetCookieW(LPCSTR lpszUrl
, LPCWSTR lpszCookieName
,
407 LPWSTR lpCookieData
, LPDWORD lpdwSize
)
410 TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl
), debugstr_w(lpszCookieName
),
416 /***********************************************************************
417 * InternetSetCookieA (WININET.@)
419 * Sets cookie for the specified url
426 BOOL WINAPI
InternetSetCookieA(LPCSTR lpszUrl
, LPCSTR lpszCookieName
,
430 cookie_domain
*thisCookieDomain
;
432 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl
),
433 debugstr_a(lpszCookieName
), lpCookieData
);
435 if (!lpCookieData
|| !strlen(lpCookieData
))
437 TRACE("no cookie data, not adding\n");
442 /* some apps (or is it us??) try to add a cookie with no cookie name, but
443 * the cookie data in the form of name=data. */
444 /* FIXME, probably a bug here, for now I don't care */
445 char *ourCookieName
, *ourCookieData
;
446 int ourCookieNameSize
;
448 if (!(ourCookieData
= strchr(lpCookieData
, '=')))
450 TRACE("something terribly wrong with cookie data %s\n", ourCookieData
);
453 ourCookieNameSize
= ourCookieData
- lpCookieData
;
455 ourCookieName
= HeapAlloc(GetProcessHeap(), 0, ourCookieNameSize
+ 1);
456 strncpy(ourCookieName
, ourCookieData
, ourCookieNameSize
);
457 ourCookieName
[ourCookieNameSize
] = '\0';
458 TRACE("setting (hacked) cookie of %s, %s\n", ourCookieName
, ourCookieData
);
459 ret
= InternetSetCookieA(lpszUrl
, ourCookieName
, ourCookieData
);
460 HeapFree(GetProcessHeap(), 0, ourCookieName
);
464 if (!(thisCookieDomain
= COOKIE_findNextDomainFromUrl(lpszUrl
, NULL
, FALSE
)))
465 thisCookieDomain
= COOKIE_addDomainFromUrl(lpszUrl
);
467 if ((thisCookie
= COOKIE_findCookie(thisCookieDomain
, lpszCookieName
)))
468 COOKIE_deleteCookie(thisCookie
, FALSE
);
470 thisCookie
= COOKIE_addCookie(thisCookieDomain
, lpszCookieName
, lpCookieData
);
475 /***********************************************************************
476 * InternetSetCookieW (WININET.@)
478 * Sets cookie for the specified url
485 BOOL WINAPI
InternetSetCookieW(LPCSTR lpszUrl
, LPCWSTR lpszCookieName
,
486 LPCWSTR lpCookieData
)
489 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl
),
490 debugstr_w(lpszCookieName
), debugstr_w(lpCookieData
));