xref: /reactos/dll/win32/winhttp/cookie.c (revision 595b846d)
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 "winhttp_private.h"
20 
21 static domain_t *add_domain( session_t *session, WCHAR *name )
22 {
23     domain_t *domain;
24 
25     if (!(domain = heap_alloc_zero( sizeof(domain_t) ))) return NULL;
26 
27     list_init( &domain->entry );
28     list_init( &domain->cookies );
29 
30     domain->name = strdupW( name );
31     list_add_tail( &session->cookie_cache, &domain->entry );
32 
33     TRACE("%s\n", debugstr_w(domain->name));
34     return domain;
35 }
36 
37 static cookie_t *find_cookie( domain_t *domain, const WCHAR *path, const WCHAR *name )
38 {
39     struct list *item;
40     cookie_t *cookie;
41 
42     LIST_FOR_EACH( item, &domain->cookies )
43     {
44         cookie = LIST_ENTRY( item, cookie_t, entry );
45         if (!strcmpW( cookie->path, path ) && !strcmpW( cookie->name, name ))
46         {
47             TRACE("found %s=%s\n", debugstr_w(cookie->name), debugstr_w(cookie->value));
48             return cookie;
49          }
50     }
51     return NULL;
52 }
53 
54 static BOOL domain_match( const WCHAR *name, domain_t *domain, BOOL partial )
55 {
56     TRACE("comparing %s with %s\n", debugstr_w(name), debugstr_w(domain->name));
57 
58     if (partial && !strstrW( name, domain->name )) return FALSE;
59     else if (!partial && strcmpW( name, domain->name )) return FALSE;
60     return TRUE;
61 }
62 
63 static void free_cookie( cookie_t *cookie )
64 {
65     heap_free( cookie->name );
66     heap_free( cookie->value );
67     heap_free( cookie->path );
68     heap_free( cookie );
69 }
70 
71 static void delete_cookie( cookie_t *cookie )
72 {
73     list_remove( &cookie->entry );
74     free_cookie( cookie );
75 }
76 
77 void delete_domain( domain_t *domain )
78 {
79     cookie_t *cookie;
80     struct list *item, *next;
81 
82     LIST_FOR_EACH_SAFE( item, next, &domain->cookies )
83     {
84         cookie = LIST_ENTRY( item, cookie_t, entry );
85         delete_cookie( cookie );
86     }
87 
88     list_remove( &domain->entry );
89     heap_free( domain->name );
90     heap_free( domain );
91 }
92 
93 static BOOL add_cookie( session_t *session, cookie_t *cookie, WCHAR *domain_name, WCHAR *path )
94 {
95     domain_t *domain = NULL;
96     cookie_t *old_cookie;
97     struct list *item;
98 
99     LIST_FOR_EACH( item, &session->cookie_cache )
100     {
101         domain = LIST_ENTRY( item, domain_t, entry );
102         if (domain_match( domain_name, domain, FALSE )) break;
103         domain = NULL;
104     }
105     if (!domain)
106     {
107         if (!(domain = add_domain( session, domain_name ))) return FALSE;
108     }
109     else if ((old_cookie = find_cookie( domain, path, cookie->name ))) delete_cookie( old_cookie );
110 
111     cookie->path = strdupW( path );
112     list_add_head( &domain->cookies, &cookie->entry );
113 
114     TRACE("domain %s path %s <- %s=%s\n", debugstr_w(domain_name), debugstr_w(cookie->path),
115           debugstr_w(cookie->name), debugstr_w(cookie->value));
116     return TRUE;
117 }
118 
119 static cookie_t *parse_cookie( const WCHAR *string )
120 {
121     cookie_t *cookie;
122     const WCHAR *p;
123     int len;
124 
125     if (!(p = strchrW( string, '=' ))) p = string + strlenW( string );
126     len = p - string;
127     while (len && string[len - 1] == ' ') len--;
128     if (!len) return NULL;
129 
130     if (!(cookie = heap_alloc_zero( sizeof(cookie_t) ))) return NULL;
131     list_init( &cookie->entry );
132 
133     if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) )))
134     {
135         heap_free( cookie );
136         return NULL;
137     }
138     memcpy( cookie->name, string, len * sizeof(WCHAR) );
139     cookie->name[len] = 0;
140 
141     if (*p++ == '=')
142     {
143         while (*p == ' ') p++;
144         len = strlenW( p );
145         while (len && p[len - 1] == ' ') len--;
146 
147         if (!(cookie->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
148         {
149             free_cookie( cookie );
150             return NULL;
151         }
152         memcpy( cookie->value, p, len * sizeof(WCHAR) );
153         cookie->value[len] = 0;
154     }
155     return cookie;
156 }
157 
158 struct attr
159 {
160     WCHAR *name;
161     WCHAR *value;
162 };
163 
164 static void free_attr( struct attr *attr )
165 {
166     if (!attr) return;
167     heap_free( attr->name );
168     heap_free( attr->value );
169     heap_free( attr );
170 }
171 
172 static struct attr *parse_attr( const WCHAR *str, int *used )
173 {
174     const WCHAR *p = str, *q;
175     struct attr *attr;
176     int len;
177 
178     while (*p == ' ') p++;
179     q = p;
180     while (*q && *q != ' ' && *q != '=' && *q != ';') q++;
181     len = q - p;
182     if (!len) return NULL;
183 
184     if (!(attr = heap_alloc( sizeof(struct attr) ))) return NULL;
185     if (!(attr->name = heap_alloc( (len + 1) * sizeof(WCHAR) )))
186     {
187         heap_free( attr );
188         return NULL;
189     }
190     memcpy( attr->name, p, len * sizeof(WCHAR) );
191     attr->name[len] = 0;
192     attr->value = NULL;
193 
194     p = q;
195     while (*p == ' ') p++;
196     if (*p++ == '=')
197     {
198         while (*p == ' ') p++;
199         q = p;
200         while (*q && *q != ';') q++;
201         len = q - p;
202         while (len && p[len - 1] == ' ') len--;
203 
204         if (!(attr->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
205         {
206             free_attr( attr );
207             return NULL;
208         }
209         memcpy( attr->value, p, len * sizeof(WCHAR) );
210         attr->value[len] = 0;
211     }
212 
213     while (*q == ' ') q++;
214     if (*q == ';') q++;
215     *used = q - str;
216 
217     return attr;
218 }
219 
220 BOOL set_cookies( request_t *request, const WCHAR *cookies )
221 {
222     static const WCHAR pathW[] = {'p','a','t','h',0};
223     static const WCHAR domainW[] = {'d','o','m','a','i','n',0};
224     BOOL ret = FALSE;
225     WCHAR *buffer, *p;
226     WCHAR *cookie_domain = NULL, *cookie_path = NULL;
227     struct attr *attr, *domain = NULL, *path = NULL;
228     session_t *session = request->connect->session;
229     cookie_t *cookie;
230     int len, used;
231 
232     len = strlenW( cookies );
233     if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
234     strcpyW( buffer, cookies );
235 
236     p = buffer;
237     while (*p && *p != ';') p++;
238     if (*p == ';') *p++ = 0;
239     if (!(cookie = parse_cookie( buffer )))
240     {
241         heap_free( buffer );
242         return FALSE;
243     }
244     len = strlenW( p );
245     while (len && (attr = parse_attr( p, &used )))
246     {
247         if (!strcmpiW( attr->name, domainW ))
248         {
249             domain = attr;
250             cookie_domain = attr->value;
251         }
252         else if (!strcmpiW( attr->name, pathW ))
253         {
254             path = attr;
255             cookie_path = attr->value;
256         }
257         else
258         {
259             FIXME( "unhandled attribute %s\n", debugstr_w(attr->name) );
260             free_attr( attr );
261         }
262         len -= used;
263         p += used;
264     }
265     if (!cookie_domain && !(cookie_domain = strdupW( request->connect->servername ))) goto end;
266     if (!cookie_path && !(cookie_path = strdupW( request->path ))) goto end;
267 
268     if ((p = strrchrW( cookie_path, '/' )) && p != cookie_path) *p = 0;
269     ret = add_cookie( session, cookie, cookie_domain, cookie_path );
270 
271 end:
272     if (!ret) free_cookie( cookie );
273     if (domain) free_attr( domain );
274     else heap_free( cookie_domain );
275     if (path) free_attr( path );
276     else heap_free( cookie_path );
277     heap_free( buffer );
278     return ret;
279 }
280 
281 BOOL add_cookie_headers( request_t *request )
282 {
283     struct list *domain_cursor;
284     session_t *session = request->connect->session;
285 
286     LIST_FOR_EACH( domain_cursor, &session->cookie_cache )
287     {
288         domain_t *domain = LIST_ENTRY( domain_cursor, domain_t, entry );
289         if (domain_match( request->connect->servername, domain, TRUE ))
290         {
291             struct list *cookie_cursor;
292             TRACE("found domain %s\n", debugstr_w(domain->name));
293 
294             LIST_FOR_EACH( cookie_cursor, &domain->cookies )
295             {
296                 cookie_t *cookie = LIST_ENTRY( cookie_cursor, cookie_t, entry );
297 
298                 TRACE("comparing path %s with %s\n", debugstr_w(request->path), debugstr_w(cookie->path));
299 
300                 if (strstrW( request->path, cookie->path ) == request->path)
301                 {
302                     const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '};
303                     int len, len_cookie = sizeof(cookieW) / sizeof(cookieW[0]), len_name = strlenW( cookie->name );
304                     WCHAR *header;
305 
306                     len = len_cookie + len_name;
307                     if (cookie->value) len += strlenW( cookie->value ) + 1;
308                     if (!(header = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
309 
310                     memcpy( header, cookieW, len_cookie * sizeof(WCHAR) );
311                     strcpyW( header + len_cookie, cookie->name );
312                     if (cookie->value)
313                     {
314                         header[len_cookie + len_name] = '=';
315                         strcpyW( header + len_cookie + len_name + 1, cookie->value );
316                     }
317 
318                     TRACE("%s\n", debugstr_w(header));
319                     add_request_headers( request, header, len,
320                                          WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
321                     heap_free( header );
322                 }
323             }
324         }
325     }
326     return TRUE;
327 }
328