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