xref: /reactos/dll/win32/winhttp/cookie.c (revision 2196a06f)
1 /*
2  * Copyright 2008 Hans Leidekker for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "config.h"
20 #include "ws2tcpip.h"
21 #include <stdarg.h>
22 
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winhttp.h"
26 
27 #include "wine/debug.h"
28 #include "wine/list.h"
29 #include "winhttp_private.h"
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
32 
33 struct cookie
34 {
35     struct list entry;
36     WCHAR *name;
37     WCHAR *value;
38     WCHAR *path;
39 };
40 
41 struct domain
42 {
43     struct list entry;
44     WCHAR *name;
45     struct list cookies;
46 };
47 
48 static struct domain *add_domain( struct session *session, WCHAR *name )
49 {
50     struct domain *domain;
51 
52     if (!(domain = heap_alloc_zero( sizeof(struct domain) ))) return NULL;
53 
54     list_init( &domain->entry );
55     list_init( &domain->cookies );
56 
57     domain->name = strdupW( name );
58     list_add_tail( &session->cookie_cache, &domain->entry );
59 
60     TRACE("%s\n", debugstr_w(domain->name));
61     return domain;
62 }
63 
64 static struct cookie *find_cookie( struct domain *domain, const WCHAR *path, const WCHAR *name )
65 {
66     struct list *item;
67     struct cookie *cookie;
68 
69     LIST_FOR_EACH( item, &domain->cookies )
70     {
71         cookie = LIST_ENTRY( item, struct cookie, entry );
72         if (!strcmpW( cookie->path, path ) && !strcmpW( cookie->name, name ))
73         {
74             TRACE("found %s=%s\n", debugstr_w(cookie->name), debugstr_w(cookie->value));
75             return cookie;
76          }
77     }
78     return NULL;
79 }
80 
81 static BOOL domain_match( const WCHAR *name, struct domain *domain, BOOL partial )
82 {
83     TRACE("comparing %s with %s\n", debugstr_w(name), debugstr_w(domain->name));
84 
85     if (partial && !strstrW( name, domain->name )) return FALSE;
86     else if (!partial && strcmpW( name, domain->name )) return FALSE;
87     return TRUE;
88 }
89 
90 static void free_cookie( struct cookie *cookie )
91 {
92     heap_free( cookie->name );
93     heap_free( cookie->value );
94     heap_free( cookie->path );
95     heap_free( cookie );
96 }
97 
98 static void delete_cookie( struct cookie *cookie )
99 {
100     list_remove( &cookie->entry );
101     free_cookie( cookie );
102 }
103 
104 static void delete_domain( struct domain *domain )
105 {
106     struct cookie *cookie;
107     struct list *item, *next;
108 
109     LIST_FOR_EACH_SAFE( item, next, &domain->cookies )
110     {
111         cookie = LIST_ENTRY( item, struct cookie, entry );
112         delete_cookie( cookie );
113     }
114 
115     list_remove( &domain->entry );
116     heap_free( domain->name );
117     heap_free( domain );
118 }
119 
120 void destroy_cookies( struct session *session )
121 {
122     struct list *item, *next;
123     struct domain *domain;
124 
125     LIST_FOR_EACH_SAFE( item, next, &session->cookie_cache )
126     {
127         domain = LIST_ENTRY( item, struct domain, entry );
128         delete_domain( domain );
129     }
130 }
131 
132 static BOOL add_cookie( struct session *session, struct cookie *cookie, WCHAR *domain_name, WCHAR *path )
133 {
134     struct domain *domain = NULL;
135     struct cookie *old_cookie;
136     struct list *item;
137 
138     if (!(cookie->path = strdupW( path ))) return FALSE;
139 
140     EnterCriticalSection( &session->cs );
141 
142     LIST_FOR_EACH( item, &session->cookie_cache )
143     {
144         domain = LIST_ENTRY( item, struct domain, entry );
145         if (domain_match( domain_name, domain, FALSE )) break;
146         domain = NULL;
147     }
148     if (!domain) domain = add_domain( session, domain_name );
149     else if ((old_cookie = find_cookie( domain, path, cookie->name ))) delete_cookie( old_cookie );
150 
151     if (domain)
152     {
153         list_add_head( &domain->cookies, &cookie->entry );
154         TRACE("domain %s path %s <- %s=%s\n", debugstr_w(domain_name), debugstr_w(cookie->path),
155               debugstr_w(cookie->name), debugstr_w(cookie->value));
156     }
157 
158     LeaveCriticalSection( &session->cs );
159     return domain != NULL;
160 }
161 
162 static struct cookie *parse_cookie( const WCHAR *string )
163 {
164     struct cookie *cookie;
165     const WCHAR *p;
166     int len;
167 
168     if (!(p = strchrW( string, '=' ))) p = string + strlenW( string );
169     len = p - string;
170     while (len && string[len - 1] == ' ') len--;
171     if (!len) return NULL;
172 
173     if (!(cookie = heap_alloc_zero( sizeof(struct cookie) ))) return NULL;
174     list_init( &cookie->entry );
175 
176     if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) )))
177     {
178         heap_free( cookie );
179         return NULL;
180     }
181     memcpy( cookie->name, string, len * sizeof(WCHAR) );
182     cookie->name[len] = 0;
183 
184     if (*p++ == '=')
185     {
186         while (*p == ' ') p++;
187         len = strlenW( p );
188         while (len && p[len - 1] == ' ') len--;
189 
190         if (!(cookie->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
191         {
192             free_cookie( cookie );
193             return NULL;
194         }
195         memcpy( cookie->value, p, len * sizeof(WCHAR) );
196         cookie->value[len] = 0;
197     }
198     return cookie;
199 }
200 
201 struct attr
202 {
203     WCHAR *name;
204     WCHAR *value;
205 };
206 
207 static void free_attr( struct attr *attr )
208 {
209     if (!attr) return;
210     heap_free( attr->name );
211     heap_free( attr->value );
212     heap_free( attr );
213 }
214 
215 static struct attr *parse_attr( const WCHAR *str, int *used )
216 {
217     const WCHAR *p = str, *q;
218     struct attr *attr;
219     int len;
220 
221     while (*p == ' ') p++;
222     q = p;
223     while (*q && *q != ' ' && *q != '=' && *q != ';') q++;
224     len = q - p;
225     if (!len) return NULL;
226 
227     if (!(attr = heap_alloc( sizeof(struct attr) ))) return NULL;
228     if (!(attr->name = heap_alloc( (len + 1) * sizeof(WCHAR) )))
229     {
230         heap_free( attr );
231         return NULL;
232     }
233     memcpy( attr->name, p, len * sizeof(WCHAR) );
234     attr->name[len] = 0;
235     attr->value = NULL;
236 
237     p = q;
238     while (*p == ' ') p++;
239     if (*p++ == '=')
240     {
241         while (*p == ' ') p++;
242         q = p;
243         while (*q && *q != ';') q++;
244         len = q - p;
245         while (len && p[len - 1] == ' ') len--;
246 
247         if (!(attr->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
248         {
249             free_attr( attr );
250             return NULL;
251         }
252         memcpy( attr->value, p, len * sizeof(WCHAR) );
253         attr->value[len] = 0;
254     }
255 
256     while (*q == ' ') q++;
257     if (*q == ';') q++;
258     *used = q - str;
259 
260     return attr;
261 }
262 
263 BOOL set_cookies( struct request *request, const WCHAR *cookies )
264 {
265     static const WCHAR pathW[] = {'p','a','t','h',0};
266     static const WCHAR domainW[] = {'d','o','m','a','i','n',0};
267     BOOL ret = FALSE;
268     WCHAR *buffer, *p;
269     WCHAR *cookie_domain = NULL, *cookie_path = NULL;
270     struct attr *attr, *domain = NULL, *path = NULL;
271     struct session *session = request->connect->session;
272     struct cookie *cookie;
273     int len, used;
274 
275     len = strlenW( cookies );
276     if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
277     strcpyW( buffer, cookies );
278 
279     p = buffer;
280     while (*p && *p != ';') p++;
281     if (*p == ';') *p++ = 0;
282     if (!(cookie = parse_cookie( buffer )))
283     {
284         heap_free( buffer );
285         return FALSE;
286     }
287     len = strlenW( p );
288     while (len && (attr = parse_attr( p, &used )))
289     {
290         if (!strcmpiW( attr->name, domainW ))
291         {
292             domain = attr;
293             cookie_domain = attr->value;
294         }
295         else if (!strcmpiW( attr->name, pathW ))
296         {
297             path = attr;
298             cookie_path = attr->value;
299         }
300         else
301         {
302             FIXME( "unhandled attribute %s\n", debugstr_w(attr->name) );
303             free_attr( attr );
304         }
305         len -= used;
306         p += used;
307     }
308     if (!cookie_domain && !(cookie_domain = strdupW( request->connect->servername ))) goto end;
309     if (!cookie_path && !(cookie_path = strdupW( request->path ))) goto end;
310 
311     if ((p = strrchrW( cookie_path, '/' )) && p != cookie_path) *p = 0;
312     ret = add_cookie( session, cookie, cookie_domain, cookie_path );
313 
314 end:
315     if (!ret) free_cookie( cookie );
316     if (domain) free_attr( domain );
317     else heap_free( cookie_domain );
318     if (path) free_attr( path );
319     else heap_free( cookie_path );
320     heap_free( buffer );
321     return ret;
322 }
323 
324 BOOL add_cookie_headers( struct request *request )
325 {
326     struct list *domain_cursor;
327     struct session *session = request->connect->session;
328 
329     EnterCriticalSection( &session->cs );
330 
331     LIST_FOR_EACH( domain_cursor, &session->cookie_cache )
332     {
333         struct domain *domain = LIST_ENTRY( domain_cursor, struct domain, entry );
334         if (domain_match( request->connect->servername, domain, TRUE ))
335         {
336             struct list *cookie_cursor;
337             TRACE("found domain %s\n", debugstr_w(domain->name));
338 
339             LIST_FOR_EACH( cookie_cursor, &domain->cookies )
340             {
341                 struct cookie *cookie = LIST_ENTRY( cookie_cursor, struct cookie, entry );
342 
343                 TRACE("comparing path %s with %s\n", debugstr_w(request->path), debugstr_w(cookie->path));
344 
345                 if (strstrW( request->path, cookie->path ) == request->path)
346                 {
347                     static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
348                     int len, len_cookie = ARRAY_SIZE( cookieW ), len_name = strlenW( cookie->name );
349                     WCHAR *header;
350 
351                     len = len_cookie + len_name;
352                     if (cookie->value) len += strlenW( cookie->value ) + 1;
353                     if (!(header = heap_alloc( (len + 1) * sizeof(WCHAR) )))
354                     {
355                         LeaveCriticalSection( &session->cs );
356                         return FALSE;
357                     }
358 
359                     memcpy( header, cookieW, len_cookie * sizeof(WCHAR) );
360                     strcpyW( header + len_cookie, cookie->name );
361                     if (cookie->value)
362                     {
363                         header[len_cookie + len_name] = '=';
364                         strcpyW( header + len_cookie + len_name + 1, cookie->value );
365                     }
366 
367                     TRACE("%s\n", debugstr_w(header));
368                     add_request_headers( request, header, len,
369                                          WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
370                     heap_free( header );
371                 }
372             }
373         }
374     }
375 
376     LeaveCriticalSection( &session->cs );
377     return TRUE;
378 }
379