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
;
78 static cookie_domain
*cookieDomainTail
;
80 static cookie
*COOKIE_addCookie(cookie_domain
*domain
, LPCSTR name
, LPCSTR data
);
81 static cookie
*COOKIE_findCookie(cookie_domain
*domain
, LPCSTR lpszCookieName
);
82 static void COOKIE_deleteCookie(cookie
*deadCookie
, BOOL deleteDomain
);
83 static cookie_domain
*COOKIE_addDomain(LPCSTR domain
, LPCSTR path
);
84 static cookie_domain
*COOKIE_addDomainFromUrl(LPCSTR lpszUrl
);
85 static cookie_domain
*COOKIE_findNextDomain(LPCSTR lpszCookieDomain
, LPCSTR lpszCookiePath
,
86 cookie_domain
*prev_domain
, BOOL allow_partial
);
87 static cookie_domain
*COOKIE_findNextDomainFromUrl(LPCSTR 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
, LPCSTR name
, LPCSTR 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, strlen(name
) + 1);
105 strcpy(newCookie
->lpCookieName
, name
);
109 newCookie
->lpCookieData
= HeapAlloc(GetProcessHeap(), 0, strlen(data
) + 1);
110 strcpy(newCookie
->lpCookieData
, data
);
113 TRACE("added cookie %p (data is %s)\n", newCookie
, 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
, LPCSTR lpszCookieName
)
125 cookie
*searchCookie
= domain
->cookie_tail
;
126 TRACE("(%p, %s)\n", domain
, debugstr_a(lpszCookieName
));
130 BOOL candidate
= TRUE
;
131 if (candidate
&& lpszCookieName
)
133 if (candidate
&& !searchCookie
->lpCookieName
)
135 if (candidate
&& strcmp(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(LPCSTR domain
, LPCSTR 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, strlen(domain
) + 1);
180 strcpy(newDomain
->lpCookieDomain
, domain
);
184 newDomain
->lpCookiePath
= HeapAlloc(GetProcessHeap(), 0, strlen(path
) + 1);
185 strcpy(newDomain
->lpCookiePath
, path
);
188 newDomain
->prev
= cookieDomainTail
;
189 cookieDomainTail
= newDomain
;
190 TRACE("Adding domain: %p\n", newDomain
);
194 static cookie_domain
*COOKIE_addDomainFromUrl(LPCSTR lpszUrl
)
196 char hostName
[2048], path
[2048];
197 URL_COMPONENTSA 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 InternetCrackUrlA(lpszUrl
, 0, 0, &UrlComponents
);
210 TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_a(UrlComponents
.lpszHostName
),
211 debugstr_a(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(LPCSTR lpszCookieDomain
, LPCSTR 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", lpszCookieDomain
, searchDomain
->lpCookieDomain
);
246 if (candidate
&& allow_partial
&& !strstr(lpszCookieDomain
, searchDomain
->lpCookieDomain
))
248 else if (candidate
&& !allow_partial
&&
249 strcmp(lpszCookieDomain
, searchDomain
->lpCookieDomain
) != 0)
252 if (candidate
&& lpszCookiePath
)
253 { TRACE("comparing paths\n");
254 if (candidate
&& !searchDomain
->lpCookiePath
)
256 if (candidate
&& strcmp(lpszCookiePath
, searchDomain
->lpCookiePath
) != 0)
261 TRACE("returning the domain %p\n", searchDomain
);
264 searchDomain
= searchDomain
->prev
;
266 TRACE("found no domain, returning NULL\n");
270 static cookie_domain
*COOKIE_findNextDomainFromUrl(LPCSTR lpszUrl
, cookie_domain
*previous_domain
,
273 char hostName
[2048], path
[2048];
274 URL_COMPONENTSA UrlComponents
;
276 UrlComponents
.lpszExtraInfo
= NULL
;
277 UrlComponents
.lpszPassword
= NULL
;
278 UrlComponents
.lpszScheme
= NULL
;
279 UrlComponents
.lpszUrlPath
= path
;
280 UrlComponents
.lpszUserName
= NULL
;
281 UrlComponents
.lpszHostName
= hostName
;
282 UrlComponents
.dwHostNameLength
= 2048;
283 UrlComponents
.dwUrlPathLength
= 2048;
285 InternetCrackUrlA(lpszUrl
, 0, 0, &UrlComponents
);
287 TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_a(UrlComponents
.lpszHostName
),
288 debugstr_a(UrlComponents
.lpszUrlPath
));
290 /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
291 UrlComponents
.lpszUrlPath
= NULL
;
293 return COOKIE_findNextDomain(UrlComponents
.lpszHostName
, UrlComponents
.lpszUrlPath
,
294 previous_domain
, allow_partial
);
297 /* remove a domain from the list and delete it */
298 static void COOKIE_deleteDomain(cookie_domain
*deadDomain
)
300 while (deadDomain
->cookie_tail
)
301 COOKIE_deleteCookie(deadDomain
->cookie_tail
, FALSE
);
302 if (deadDomain
->lpCookieDomain
)
303 HeapFree(GetProcessHeap(), 0, deadDomain
->lpCookieDomain
);
304 if (deadDomain
->lpCookiePath
)
305 HeapFree(GetProcessHeap(), 0, deadDomain
->lpCookiePath
);
306 if (deadDomain
->prev
)
307 deadDomain
->prev
->next
= deadDomain
->next
;
308 if (deadDomain
->next
)
309 deadDomain
->next
->prev
= deadDomain
->prev
;
311 if (cookieDomainTail
== deadDomain
)
312 cookieDomainTail
= deadDomain
->prev
;
313 HeapFree(GetProcessHeap(), 0, deadDomain
);
316 /***********************************************************************
317 * InternetGetCookieA (WININET.@)
319 * Retrieve cookie from the specified url
321 * It should be noted that on windows the lpszCookieName parameter is "not implemented".
322 * So it won't be implemented here.
329 BOOL WINAPI
InternetGetCookieA(LPCSTR lpszUrl
, LPCSTR lpszCookieName
,
330 LPSTR lpCookieData
, LPDWORD lpdwSize
)
332 cookie_domain
*cookiesDomain
= NULL
;
334 int cnt
= 0, domain_count
= 0;
335 /* Ok, this is just ODD!. During my tests, it appears M$ like to send out
336 * a cookie called 'MtrxTracking' to some urls. Also returns it from InternetGetCookie.
337 * I'm not exactly sure what to make of this, so its here for now.
338 * It'd be nice to know what exactly is going on, M$ tracking users? Does this need
339 * to be unique? Should I generate a random number here? etc.
341 char *TrackingString
= "MtrxTrackingID=01234567890123456789012345678901";
343 TRACE("(%s, %s, %p, %p)\n", debugstr_a(lpszUrl
),debugstr_a(lpszCookieName
),
344 lpCookieData
, lpdwSize
);
347 cnt
+= snprintf(lpCookieData
+ cnt
, *lpdwSize
- cnt
, "%s", TrackingString
);
349 cnt
+= strlen(TrackingString
);
351 while ((cookiesDomain
= COOKIE_findNextDomainFromUrl(lpszUrl
, cookiesDomain
, TRUE
)))
354 TRACE("found domain %p\n", cookiesDomain
);
356 thisCookie
= cookiesDomain
->cookie_tail
;
357 if (lpCookieData
== NULL
) /* return the size of the buffer required to lpdwSize */
362 cnt
+= strlen(thisCookie
->lpCookieName
);
364 cnt
+= strlen(thisCookie
->lpCookieData
);
366 thisCookie
= thisCookie
->prev
;
371 cnt
+= snprintf(lpCookieData
+ cnt
, *lpdwSize
- cnt
, "; ");
372 cnt
+= snprintf(lpCookieData
+ cnt
, *lpdwSize
- cnt
, "%s=%s", thisCookie
->lpCookieName
,
373 thisCookie
->lpCookieData
);
375 thisCookie
= thisCookie
->prev
;
378 if (lpCookieData
== NULL
)
382 TRACE("returning\n");
391 TRACE("Returning %i (from %i domains): %s\n", cnt
, domain_count
, lpCookieData
);
393 return (cnt
? TRUE
: FALSE
);
397 /***********************************************************************
398 * InternetGetCookieW (WININET.@)
400 * Retrieve cookie from the specified url
407 BOOL WINAPI
InternetGetCookieW(LPCSTR lpszUrl
, LPCWSTR lpszCookieName
,
408 LPWSTR lpCookieData
, LPDWORD lpdwSize
)
411 TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl
), debugstr_w(lpszCookieName
),
417 /***********************************************************************
418 * InternetSetCookieA (WININET.@)
420 * Sets cookie for the specified url
427 BOOL WINAPI
InternetSetCookieA(LPCSTR lpszUrl
, LPCSTR lpszCookieName
,
431 cookie_domain
*thisCookieDomain
;
433 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl
),
434 debugstr_a(lpszCookieName
), lpCookieData
);
436 if (!lpCookieData
|| !strlen(lpCookieData
))
438 TRACE("no cookie data, not adding\n");
443 /* some apps (or is it us??) try to add a cookie with no cookie name, but
444 * the cookie data in the form of name=data. */
445 /* FIXME, probably a bug here, for now I don't care */
446 char *ourCookieName
, *ourCookieData
;
447 int ourCookieNameSize
;
449 if (!(ourCookieData
= strchr(lpCookieData
, '=')))
451 TRACE("something terribly wrong with cookie data %s\n", ourCookieData
);
454 ourCookieNameSize
= ourCookieData
- lpCookieData
;
456 ourCookieName
= HeapAlloc(GetProcessHeap(), 0, ourCookieNameSize
+ 1);
457 strncpy(ourCookieName
, ourCookieData
, ourCookieNameSize
);
458 ourCookieName
[ourCookieNameSize
] = '\0';
459 TRACE("setting (hacked) cookie of %s, %s\n", ourCookieName
, ourCookieData
);
460 ret
= InternetSetCookieA(lpszUrl
, ourCookieName
, ourCookieData
);
461 HeapFree(GetProcessHeap(), 0, ourCookieName
);
465 if (!(thisCookieDomain
= COOKIE_findNextDomainFromUrl(lpszUrl
, NULL
, FALSE
)))
466 thisCookieDomain
= COOKIE_addDomainFromUrl(lpszUrl
);
468 if ((thisCookie
= COOKIE_findCookie(thisCookieDomain
, lpszCookieName
)))
469 COOKIE_deleteCookie(thisCookie
, FALSE
);
471 thisCookie
= COOKIE_addCookie(thisCookieDomain
, lpszCookieName
, lpCookieData
);
476 /***********************************************************************
477 * InternetSetCookieW (WININET.@)
479 * Sets cookie for the specified url
486 BOOL WINAPI
InternetSetCookieW(LPCSTR lpszUrl
, LPCWSTR lpszCookieName
,
487 LPCWSTR lpCookieData
)
490 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl
),
491 debugstr_w(lpszCookieName
), debugstr_w(lpCookieData
));