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
add_domain(struct session * session,WCHAR * name)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
find_cookie(struct domain * domain,const WCHAR * path,const WCHAR * name)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
domain_match(const WCHAR * name,struct domain * domain,BOOL partial)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
free_cookie(struct cookie * cookie)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
delete_cookie(struct cookie * cookie)98 static void delete_cookie( struct cookie *cookie )
99 {
100 list_remove( &cookie->entry );
101 free_cookie( cookie );
102 }
103
delete_domain(struct domain * domain)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
destroy_cookies(struct session * session)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
add_cookie(struct session * session,struct cookie * cookie,WCHAR * domain_name,WCHAR * path)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
parse_cookie(const WCHAR * string)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
free_attr(struct attr * attr)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
parse_attr(const WCHAR * str,int * used)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
set_cookies(struct request * request,const WCHAR * cookies)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
add_cookie_headers(struct request * request)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