2 * Copyright 2008 Hans Leidekker for CodeWeavers
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.
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.
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
27 #include "wine/debug.h"
28 #include "wine/list.h"
29 #include "winhttp_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(winhttp
);
48 static struct domain
*add_domain( struct session
*session
, WCHAR
*name
)
50 struct domain
*domain
;
52 if (!(domain
= heap_alloc_zero( sizeof(struct domain
) ))) return NULL
;
54 list_init( &domain
->entry
);
55 list_init( &domain
->cookies
);
57 domain
->name
= strdupW( name
);
58 list_add_tail( &session
->cookie_cache
, &domain
->entry
);
60 TRACE("%s\n", debugstr_w(domain
->name
));
64 static struct cookie
*find_cookie( struct domain
*domain
, const WCHAR
*path
, const WCHAR
*name
)
67 struct cookie
*cookie
;
69 LIST_FOR_EACH( item
, &domain
->cookies
)
71 cookie
= LIST_ENTRY( item
, struct cookie
, entry
);
72 if (!wcscmp( cookie
->path
, path
) && !wcscmp( cookie
->name
, name
))
74 TRACE("found %s=%s\n", debugstr_w(cookie
->name
), debugstr_w(cookie
->value
));
81 static BOOL
domain_match( const WCHAR
*name
, struct domain
*domain
, BOOL partial
)
83 TRACE("comparing %s with %s\n", debugstr_w(name
), debugstr_w(domain
->name
));
85 if (partial
&& !wcsstr( name
, domain
->name
)) return FALSE
;
86 else if (!partial
&& wcscmp( name
, domain
->name
)) return FALSE
;
90 static void free_cookie( struct cookie
*cookie
)
92 heap_free( cookie
->name
);
93 heap_free( cookie
->value
);
94 heap_free( cookie
->path
);
98 static void delete_cookie( struct cookie
*cookie
)
100 list_remove( &cookie
->entry
);
101 free_cookie( cookie
);
104 static void delete_domain( struct domain
*domain
)
106 struct cookie
*cookie
;
107 struct list
*item
, *next
;
109 LIST_FOR_EACH_SAFE( item
, next
, &domain
->cookies
)
111 cookie
= LIST_ENTRY( item
, struct cookie
, entry
);
112 delete_cookie( cookie
);
115 list_remove( &domain
->entry
);
116 heap_free( domain
->name
);
120 void destroy_cookies( struct session
*session
)
122 struct list
*item
, *next
;
123 struct domain
*domain
;
125 LIST_FOR_EACH_SAFE( item
, next
, &session
->cookie_cache
)
127 domain
= LIST_ENTRY( item
, struct domain
, entry
);
128 delete_domain( domain
);
132 static BOOL
add_cookie( struct session
*session
, struct cookie
*cookie
, WCHAR
*domain_name
, WCHAR
*path
)
134 struct domain
*domain
= NULL
;
135 struct cookie
*old_cookie
;
138 if (!(cookie
->path
= strdupW( path
))) return FALSE
;
140 EnterCriticalSection( &session
->cs
);
142 LIST_FOR_EACH( item
, &session
->cookie_cache
)
144 domain
= LIST_ENTRY( item
, struct domain
, entry
);
145 if (domain_match( domain_name
, domain
, FALSE
)) break;
148 if (!domain
) domain
= add_domain( session
, domain_name
);
149 else if ((old_cookie
= find_cookie( domain
, path
, cookie
->name
))) delete_cookie( old_cookie
);
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
));
158 LeaveCriticalSection( &session
->cs
);
159 return domain
!= NULL
;
162 static struct cookie
*parse_cookie( const WCHAR
*string
)
164 struct cookie
*cookie
;
168 if (!(p
= wcschr( string
, '=' ))) p
= string
+ lstrlenW( string
);
170 while (len
&& string
[len
- 1] == ' ') len
--;
171 if (!len
) return NULL
;
173 if (!(cookie
= heap_alloc_zero( sizeof(struct cookie
) ))) return NULL
;
174 list_init( &cookie
->entry
);
176 if (!(cookie
->name
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
181 memcpy( cookie
->name
, string
, len
* sizeof(WCHAR
) );
182 cookie
->name
[len
] = 0;
186 while (*p
== ' ') p
++;
188 while (len
&& p
[len
- 1] == ' ') len
--;
190 if (!(cookie
->value
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
192 free_cookie( cookie
);
195 memcpy( cookie
->value
, p
, len
* sizeof(WCHAR
) );
196 cookie
->value
[len
] = 0;
207 static void free_attr( struct attr
*attr
)
210 heap_free( attr
->name
);
211 heap_free( attr
->value
);
215 static struct attr
*parse_attr( const WCHAR
*str
, int *used
)
217 const WCHAR
*p
= str
, *q
;
221 while (*p
== ' ') p
++;
223 while (*q
&& *q
!= ' ' && *q
!= '=' && *q
!= ';') q
++;
225 if (!len
) return NULL
;
227 if (!(attr
= heap_alloc( sizeof(struct attr
) ))) return NULL
;
228 if (!(attr
->name
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
233 memcpy( attr
->name
, p
, len
* sizeof(WCHAR
) );
238 while (*p
== ' ') p
++;
241 while (*p
== ' ') p
++;
243 while (*q
&& *q
!= ';') q
++;
245 while (len
&& p
[len
- 1] == ' ') len
--;
247 if (!(attr
->value
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
252 memcpy( attr
->value
, p
, len
* sizeof(WCHAR
) );
253 attr
->value
[len
] = 0;
256 while (*q
== ' ') q
++;
263 BOOL
set_cookies( struct request
*request
, const WCHAR
*cookies
)
267 WCHAR
*cookie_domain
= NULL
, *cookie_path
= NULL
;
268 struct attr
*attr
, *domain
= NULL
, *path
= NULL
;
269 struct session
*session
= request
->connect
->session
;
270 struct cookie
*cookie
;
273 len
= lstrlenW( cookies
);
274 if (!(buffer
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) ))) return FALSE
;
275 lstrcpyW( buffer
, cookies
);
278 while (*p
&& *p
!= ';') p
++;
279 if (*p
== ';') *p
++ = 0;
280 if (!(cookie
= parse_cookie( buffer
)))
286 while (len
&& (attr
= parse_attr( p
, &used
)))
288 if (!wcsicmp( attr
->name
, L
"domain" ))
291 cookie_domain
= attr
->value
;
293 else if (!wcsicmp( attr
->name
, L
"path" ))
296 cookie_path
= attr
->value
;
300 FIXME( "unhandled attribute %s\n", debugstr_w(attr
->name
) );
306 if (!cookie_domain
&& !(cookie_domain
= strdupW( request
->connect
->servername
))) goto end
;
307 if (!cookie_path
&& !(cookie_path
= strdupW( request
->path
))) goto end
;
309 if ((p
= wcsrchr( cookie_path
, '/' )) && p
!= cookie_path
) *p
= 0;
310 ret
= add_cookie( session
, cookie
, cookie_domain
, cookie_path
);
313 if (!ret
) free_cookie( cookie
);
314 if (domain
) free_attr( domain
);
315 else heap_free( cookie_domain
);
316 if (path
) free_attr( path
);
317 else heap_free( cookie_path
);
322 DWORD
add_cookie_headers( struct request
*request
)
324 struct list
*domain_cursor
;
325 struct session
*session
= request
->connect
->session
;
326 DWORD ret
= ERROR_SUCCESS
;
328 EnterCriticalSection( &session
->cs
);
330 LIST_FOR_EACH( domain_cursor
, &session
->cookie_cache
)
332 struct domain
*domain
= LIST_ENTRY( domain_cursor
, struct domain
, entry
);
333 if (domain_match( request
->connect
->servername
, domain
, TRUE
))
335 struct list
*cookie_cursor
;
336 TRACE("found domain %s\n", debugstr_w(domain
->name
));
338 LIST_FOR_EACH( cookie_cursor
, &domain
->cookies
)
340 struct cookie
*cookie
= LIST_ENTRY( cookie_cursor
, struct cookie
, entry
);
342 TRACE("comparing path %s with %s\n", debugstr_w(request
->path
), debugstr_w(cookie
->path
));
344 if (wcsstr( request
->path
, cookie
->path
) == request
->path
)
346 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' '};
347 int len
, len_cookie
= ARRAY_SIZE( cookieW
), len_name
= lstrlenW( cookie
->name
);
350 len
= len_cookie
+ len_name
;
351 if (cookie
->value
) len
+= lstrlenW( cookie
->value
) + 1;
352 if (!(header
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) )))
354 LeaveCriticalSection( &session
->cs
);
355 return ERROR_OUTOFMEMORY
;
358 memcpy( header
, cookieW
, len_cookie
* sizeof(WCHAR
) );
359 lstrcpyW( header
+ len_cookie
, cookie
->name
);
362 header
[len_cookie
+ len_name
] = '=';
363 lstrcpyW( header
+ len_cookie
+ len_name
+ 1, cookie
->value
);
366 TRACE("%s\n", debugstr_w(header
));
367 ret
= add_request_headers( request
, header
, len
,
368 WINHTTP_ADDREQ_FLAG_ADD
| WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON
);
375 LeaveCriticalSection( &session
->cs
);