Added class factories for DirectSoundCapture, DirectSoundFullDuplex
[wine] / dlls / wininet / cookie.c
1 /*
2  * Wininet - cookie handling stuff
3  *
4  * Copyright 2002 TransGaming Technologies Inc.
5  *
6  * David Hammerton
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wininet.h"
33 #include "winerror.h"
34
35 #include "wine/debug.h"
36 #include "internet.h"
37
38 #define RESPONSE_TIMEOUT        30            /* FROM internet.c */
39
40
41 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
42
43 /* FIXME
44  *     Cookies are currently memory only.
45  *     Cookies are NOT THREAD SAFE
46  *     Cookies could use ALOT OF MEMORY. We need some kind of memory management here!
47  *     Cookies should care about the expiry time
48  */
49
50 typedef struct _cookie_domain cookie_domain;
51 typedef struct _cookie cookie;
52
53 struct _cookie
54 {
55     struct _cookie *next;
56     struct _cookie *prev;
57
58     struct _cookie_domain *parent;
59
60     LPSTR lpCookieName;
61     LPSTR lpCookieData;
62     time_t expiry; /* FIXME: not used */
63 };
64
65 struct _cookie_domain
66 {
67     struct _cookie_domain *next;
68     struct _cookie_domain *prev;
69
70     LPSTR lpCookieDomain;
71     LPSTR lpCookiePath;
72     cookie *cookie_tail;
73 };
74
75 static cookie_domain *cookieDomainTail;
76
77 static cookie *COOKIE_addCookie(cookie_domain *domain, LPCSTR name, LPCSTR data);
78 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCSTR lpszCookieName);
79 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain);
80 static cookie_domain *COOKIE_addDomain(LPCSTR domain, LPCSTR path);
81 static cookie_domain *COOKIE_addDomainFromUrl(LPCSTR lpszUrl);
82 static cookie_domain *COOKIE_findNextDomain(LPCSTR lpszCookieDomain, LPCSTR lpszCookiePath,
83                                             cookie_domain *prev_domain, BOOL allow_partial);
84 static cookie_domain *COOKIE_findNextDomainFromUrl(LPCSTR lpszUrl, cookie_domain *prev_domain,
85                                                    BOOL allow_partial);
86 static void COOKIE_deleteDomain(cookie_domain *deadDomain);
87
88
89 /* adds a cookie to the domain */
90 static cookie *COOKIE_addCookie(cookie_domain *domain, LPCSTR name, LPCSTR data)
91 {
92     cookie *newCookie = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie));
93
94     newCookie->next = NULL;
95     newCookie->prev = NULL;
96     newCookie->lpCookieName = NULL;
97     newCookie->lpCookieData = NULL;
98
99     if (name)
100     {
101         newCookie->lpCookieName = HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1);
102         strcpy(newCookie->lpCookieName, name);
103     }
104     if (data)
105     {
106         newCookie->lpCookieData = HeapAlloc(GetProcessHeap(), 0, strlen(data) + 1);
107         strcpy(newCookie->lpCookieData, data);
108     }
109
110     TRACE("added cookie %p (data is %s)\n", newCookie, data);
111
112     newCookie->prev = domain->cookie_tail;
113     newCookie->parent = domain;
114     domain->cookie_tail = newCookie;
115     return newCookie;
116 }
117
118
119 /* finds a cookie in the domain matching the cookie name */
120 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCSTR lpszCookieName)
121 {
122     cookie *searchCookie = domain->cookie_tail;
123     TRACE("(%p, %s)\n", domain, debugstr_a(lpszCookieName));
124
125     while (searchCookie)
126     {
127         BOOL candidate = TRUE;
128         if (candidate && lpszCookieName)
129         {
130             if (candidate && !searchCookie->lpCookieName)
131                 candidate = FALSE;
132             if (candidate && strcmp(lpszCookieName, searchCookie->lpCookieName) != 0)
133                 candidate = FALSE;
134         }
135         if (candidate)
136             return searchCookie;
137         searchCookie = searchCookie->prev;
138     }
139     return NULL;
140 }
141
142 /* removes a cookie from the list, if its the last cookie we also remove the domain */
143 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain)
144 {
145     if (deadCookie->lpCookieName)
146         HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieName);
147     if (deadCookie->lpCookieData)
148         HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieData);
149     if (deadCookie->prev)
150         deadCookie->prev->next = deadCookie->next;
151     if (deadCookie->next)
152         deadCookie->next->prev = deadCookie->prev;
153
154     if (deadCookie == deadCookie->parent->cookie_tail)
155     {
156         /* special case: last cookie, lets remove the domain to save memory */
157         deadCookie->parent->cookie_tail = deadCookie->prev;
158         if (!deadCookie->parent->cookie_tail && deleteDomain)
159             COOKIE_deleteDomain(deadCookie->parent);
160     }
161 }
162
163 /* allocates a domain and adds it to the end */
164 static cookie_domain *COOKIE_addDomain(LPCSTR domain, LPCSTR path)
165 {
166     cookie_domain *newDomain = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie_domain));
167
168     newDomain->next = NULL;
169     newDomain->prev = NULL;
170     newDomain->cookie_tail = NULL;
171     newDomain->lpCookieDomain = NULL;
172     newDomain->lpCookiePath = NULL;
173
174     if (domain)
175     {
176         newDomain->lpCookieDomain = HeapAlloc(GetProcessHeap(), 0, strlen(domain) + 1);
177         strcpy(newDomain->lpCookieDomain, domain);
178     }
179     if (path)
180     {
181         newDomain->lpCookiePath = HeapAlloc(GetProcessHeap(), 0, strlen(path) + 1);
182         strcpy(newDomain->lpCookiePath, path);
183     }
184
185     newDomain->prev = cookieDomainTail;
186     cookieDomainTail = newDomain;
187     TRACE("Adding domain: %p\n", newDomain);
188     return newDomain;
189 }
190
191 static cookie_domain *COOKIE_addDomainFromUrl(LPCSTR lpszUrl)
192 {
193     char hostName[2048], path[2048];
194     URL_COMPONENTSA UrlComponents;
195
196     UrlComponents.lpszExtraInfo = NULL;
197     UrlComponents.lpszPassword = NULL;
198     UrlComponents.lpszScheme = NULL;
199     UrlComponents.lpszUrlPath = path;
200     UrlComponents.lpszUserName = NULL;
201     UrlComponents.lpszHostName = hostName;
202     UrlComponents.dwHostNameLength = 2048;
203     UrlComponents.dwUrlPathLength = 2048;
204
205     InternetCrackUrlA(lpszUrl, 0, 0, &UrlComponents);
206
207     TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_a(UrlComponents.lpszHostName),
208           debugstr_a(UrlComponents.lpszUrlPath));
209
210     /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
211     UrlComponents.lpszUrlPath = NULL;
212
213     return COOKIE_addDomain(UrlComponents.lpszHostName, UrlComponents.lpszUrlPath);
214 }
215
216 /* find a domain. domain must match if its not NULL. path must match if its not NULL */
217 static cookie_domain *COOKIE_findNextDomain(LPCSTR lpszCookieDomain, LPCSTR lpszCookiePath,
218                                             cookie_domain *prev_domain, BOOL allow_partial)
219 {
220     cookie_domain *searchDomain;
221
222     if (prev_domain)
223     {
224         if(!prev_domain->prev)
225         {
226             TRACE("no more domains available, it would seem.\n");
227             return NULL;
228         }
229         searchDomain = prev_domain->prev;
230     }
231     else searchDomain = cookieDomainTail;
232
233     while (searchDomain)
234     {
235         BOOL candidate = TRUE;
236         TRACE("searching on domain %p\n", searchDomain);
237         if (candidate && lpszCookieDomain)
238         {
239             if (candidate && !searchDomain->lpCookieDomain)
240                 candidate = FALSE;
241             TRACE("candidate! (%p)\n", searchDomain->lpCookieDomain);
242             TRACE("comparing domain %s with %s\n", lpszCookieDomain, searchDomain->lpCookieDomain);
243             if (candidate && allow_partial && !strstr(lpszCookieDomain, searchDomain->lpCookieDomain))
244                 candidate = FALSE;
245             else if (candidate && !allow_partial &&
246                      strcmp(lpszCookieDomain, searchDomain->lpCookieDomain) != 0)
247                 candidate = FALSE;
248         }
249         if (candidate && lpszCookiePath)
250         {                           TRACE("comparing paths\n");
251             if (candidate && !searchDomain->lpCookiePath)
252                 candidate = FALSE;
253             if (candidate && strcmp(lpszCookiePath, searchDomain->lpCookiePath) != 0)
254                 candidate = FALSE;
255         }
256         if (candidate)
257         {
258             TRACE("returning the domain %p\n", searchDomain);
259             return searchDomain;
260         }
261         searchDomain = searchDomain->prev;
262     }
263     TRACE("found no domain, returning NULL\n");
264     return NULL;
265 }
266
267 static cookie_domain *COOKIE_findNextDomainFromUrl(LPCSTR lpszUrl, cookie_domain *previous_domain,
268                                                    BOOL allow_partial)
269 {
270     char hostName[2048], path[2048];
271     URL_COMPONENTSA UrlComponents;
272
273     UrlComponents.lpszExtraInfo = NULL;
274     UrlComponents.lpszPassword = NULL;
275     UrlComponents.lpszScheme = NULL;
276     UrlComponents.lpszUrlPath = path;
277     UrlComponents.lpszUserName = NULL;
278     UrlComponents.lpszHostName = hostName;
279     UrlComponents.dwHostNameLength = 2048;
280     UrlComponents.dwUrlPathLength = 2048;
281
282     InternetCrackUrlA(lpszUrl, 0, 0, &UrlComponents);
283
284     TRACE("Url cracked. Domain: %s, Path: %s.\n", debugstr_a(UrlComponents.lpszHostName),
285           debugstr_a(UrlComponents.lpszUrlPath));
286
287     /* hack for now - FIXME - There seems to be a bug in InternetCrackUrl?? */
288     UrlComponents.lpszUrlPath = NULL;
289
290     return COOKIE_findNextDomain(UrlComponents.lpszHostName, UrlComponents.lpszUrlPath,
291                                  previous_domain, allow_partial);
292 }
293
294 /* remove a domain from the list and delete it */
295 static void COOKIE_deleteDomain(cookie_domain *deadDomain)
296 {
297     while (deadDomain->cookie_tail)
298         COOKIE_deleteCookie(deadDomain->cookie_tail, FALSE);
299     if (deadDomain->lpCookieDomain)
300         HeapFree(GetProcessHeap(), 0, deadDomain->lpCookieDomain);
301     if (deadDomain->lpCookiePath)
302         HeapFree(GetProcessHeap(), 0, deadDomain->lpCookiePath);
303     if (deadDomain->prev)
304         deadDomain->prev->next = deadDomain->next;
305     if (deadDomain->next)
306         deadDomain->next->prev = deadDomain->prev;
307
308     if (cookieDomainTail == deadDomain)
309         cookieDomainTail = deadDomain->prev;
310     HeapFree(GetProcessHeap(), 0, deadDomain);
311 }
312
313 /***********************************************************************
314  *           InternetGetCookieA (WININET.@)
315  *
316  * Retrieve cookie from the specified url
317  *
318  *  It should be noted that on windows the lpszCookieName parameter is "not implemented".
319  *    So it won't be implemented here.
320  *
321  * RETURNS
322  *    TRUE  on success
323  *    FALSE on failure
324  *
325  */
326 BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
327     LPSTR lpCookieData, LPDWORD lpdwSize)
328 {
329     cookie_domain *cookiesDomain = NULL;
330     cookie *thisCookie;
331     int cnt = 0, domain_count = 0;
332     /* Ok, this is just ODD!. During my tests, it appears M$ like to send out
333      * a cookie called 'MtrxTracking' to some urls. Also returns it from InternetGetCookie.
334      * I'm not exactly sure what to make of this, so its here for now.
335      * It'd be nice to know what exactly is going on, M$ tracking users? Does this need
336      * to be unique? Should I generate a random number here? etc.
337      */
338     char *TrackingString = "MtrxTrackingID=01234567890123456789012345678901";
339
340     TRACE("(%s, %s, %p, %p)\n", debugstr_a(lpszUrl),debugstr_a(lpszCookieName),
341           lpCookieData, lpdwSize);
342
343     if (lpCookieData)
344         cnt += snprintf(lpCookieData + cnt, *lpdwSize - cnt, "%s", TrackingString);
345     else
346         cnt += strlen(TrackingString);
347
348     while ((cookiesDomain = COOKIE_findNextDomainFromUrl(lpszUrl, cookiesDomain, TRUE)))
349     {
350         domain_count++;
351         TRACE("found domain %p\n", cookiesDomain);
352
353         thisCookie = cookiesDomain->cookie_tail;
354         if (lpCookieData == NULL) /* return the size of the buffer required to lpdwSize */
355         {
356             while (thisCookie)
357             {
358                 cnt += 2; /* '; ' */
359                 cnt += strlen(thisCookie->lpCookieName);
360                 cnt += 1; /* = */
361                 cnt += strlen(thisCookie->lpCookieData);
362
363                 thisCookie = thisCookie->prev;
364             }
365         }
366         while (thisCookie)
367         {
368             cnt += snprintf(lpCookieData + cnt, *lpdwSize - cnt, "; ");
369             cnt += snprintf(lpCookieData + cnt, *lpdwSize - cnt, "%s=%s", thisCookie->lpCookieName,
370                             thisCookie->lpCookieData);
371
372             thisCookie = thisCookie->prev;
373         }
374     }
375     if (lpCookieData == NULL)
376     {
377         cnt += 1; /* NULL */
378         *lpdwSize = cnt;
379         TRACE("returning\n");
380         return TRUE;
381     }
382
383     if (!domain_count)
384         return FALSE;
385
386     *lpdwSize = cnt + 1;
387
388     TRACE("Returning %i (from %i domains): %s\n", cnt, domain_count, lpCookieData);
389
390     return (cnt ? TRUE : FALSE);
391 }
392
393
394 /***********************************************************************
395  *           InternetGetCookieW (WININET.@)
396  *
397  * Retrieve cookie from the specified url
398  *
399  * RETURNS
400  *    TRUE  on success
401  *    FALSE on failure
402  *
403  */
404 BOOL WINAPI InternetGetCookieW(LPCSTR lpszUrl, LPCWSTR lpszCookieName,
405     LPWSTR lpCookieData, LPDWORD lpdwSize)
406 {
407     FIXME("STUB\n");
408     TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl), debugstr_w(lpszCookieName),
409         lpCookieData);
410     return FALSE;
411 }
412
413
414 /***********************************************************************
415  *           InternetSetCookieA (WININET.@)
416  *
417  * Sets cookie for the specified url
418  *
419  * RETURNS
420  *    TRUE  on success
421  *    FALSE on failure
422  *
423  */
424 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
425     LPCSTR lpCookieData)
426 {
427     cookie *thisCookie;
428     cookie_domain *thisCookieDomain;
429
430     TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
431         debugstr_a(lpszCookieName), lpCookieData);
432
433     if (!lpCookieData || !strlen(lpCookieData))
434     {
435         TRACE("no cookie data, not adding\n");
436         return FALSE;
437     }
438     if (!lpszCookieName)
439     {
440         /* some apps (or is it us??) try to add a cookie with no cookie name, but
441          * the cookie data in the form of name=data. */
442         /* FIXME, probably a bug here, for now I don't care */
443         char *ourCookieName, *ourCookieData;
444         int ourCookieNameSize;
445         BOOL ret;
446         if (!(ourCookieData = strchr(lpCookieData, '=')))
447         {
448             TRACE("something terribly wrong with cookie data %s\n", ourCookieData);
449             return FALSE;
450         }
451         ourCookieNameSize = ourCookieData - lpCookieData;
452         ourCookieData += 1;
453         ourCookieName = HeapAlloc(GetProcessHeap(), 0, ourCookieNameSize + 1);
454         strncpy(ourCookieName, ourCookieData, ourCookieNameSize);
455         ourCookieName[ourCookieNameSize] = '\0';
456         TRACE("setting (hacked) cookie of %s, %s\n", ourCookieName, ourCookieData);
457         ret = InternetSetCookieA(lpszUrl, ourCookieName, ourCookieData);
458         HeapFree(GetProcessHeap(), 0, ourCookieName);
459         return ret;
460     }
461
462     if (!(thisCookieDomain = COOKIE_findNextDomainFromUrl(lpszUrl, NULL, FALSE)))
463         thisCookieDomain = COOKIE_addDomainFromUrl(lpszUrl);
464
465     if ((thisCookie = COOKIE_findCookie(thisCookieDomain, lpszCookieName)))
466         COOKIE_deleteCookie(thisCookie, FALSE);
467
468     thisCookie = COOKIE_addCookie(thisCookieDomain, lpszCookieName, lpCookieData);
469     return TRUE;
470 }
471
472
473 /***********************************************************************
474  *           InternetSetCookieW (WININET.@)
475  *
476  * Sets cookie for the specified url
477  *
478  * RETURNS
479  *    TRUE  on success
480  *    FALSE on failure
481  *
482  */
483 BOOL WINAPI InternetSetCookieW(LPCSTR lpszUrl, LPCWSTR lpszCookieName,
484     LPCWSTR lpCookieData)
485 {
486     FIXME("STUB\n");
487     TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
488         debugstr_w(lpszCookieName), debugstr_w(lpCookieData));
489     return FALSE;
490 }