1 #ifdef __REACTOS__ 2 #include "precomp.h" 3 #else 4 /* 5 * Wininet - cookie handling stuff 6 * 7 * Copyright 2002 TransGaming Technologies Inc. 8 * 9 * David Hammerton 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24 */ 25 26 #include "ws2tcpip.h" 27 28 #include <stdarg.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <assert.h> 33 #include <wchar.h> 34 35 #include "windef.h" 36 #include "winbase.h" 37 #include "wininet.h" 38 #include "lmcons.h" 39 #include "winerror.h" 40 41 #include "wine/debug.h" 42 #include "internet.h" 43 #endif /* defined(__REACTOS__) */ 44 45 #define RESPONSE_TIMEOUT 30 /* FROM internet.c */ 46 47 48 WINE_DEFAULT_DEBUG_CHANNEL(wininet); 49 50 /* FIXME 51 * Cookies could use A LOT OF MEMORY. We need some kind of memory management here! 52 */ 53 54 struct _cookie_domain_t; 55 struct _cookie_container_t; 56 57 typedef struct _cookie_t { 58 struct list entry; 59 60 struct _cookie_container_t *container; 61 62 WCHAR *name; 63 WCHAR *data; 64 DWORD flags; 65 FILETIME expiry; 66 FILETIME create; 67 } cookie_t; 68 69 typedef struct _cookie_container_t { 70 struct list entry; 71 72 WCHAR *cookie_url; 73 substr_t path; 74 struct _cookie_domain_t *domain; 75 76 struct list cookie_list; 77 } cookie_container_t; 78 79 typedef struct _cookie_domain_t { 80 struct list entry; 81 82 WCHAR *domain; 83 unsigned subdomain_len; 84 85 struct _cookie_domain_t *parent; 86 struct list subdomain_list; 87 88 /* List of stored paths sorted by length of the path. */ 89 struct list path_list; 90 } cookie_domain_t; 91 92 static CRITICAL_SECTION cookie_cs; 93 static CRITICAL_SECTION_DEBUG cookie_cs_debug = 94 { 95 0, 0, &cookie_cs, 96 { &cookie_cs_debug.ProcessLocksList, &cookie_cs_debug.ProcessLocksList }, 97 0, 0, { (DWORD_PTR)(__FILE__ ": cookie_cs") } 98 }; 99 static CRITICAL_SECTION cookie_cs = { &cookie_cs_debug, -1, 0, 0, 0, 0 }; 100 static struct list domain_list = LIST_INIT(domain_list); 101 102 static cookie_domain_t *get_cookie_domain(substr_t domain, BOOL create) 103 { 104 const WCHAR *ptr = domain.str + domain.len, *ptr_end, *subdomain_ptr; 105 cookie_domain_t *iter, *current_domain, *prev_domain = NULL; 106 struct list *current_list = &domain_list; 107 108 while(1) { 109 for(ptr_end = ptr--; ptr > domain.str && *ptr != '.'; ptr--); 110 subdomain_ptr = *ptr == '.' ? ptr+1 : ptr; 111 112 current_domain = NULL; 113 LIST_FOR_EACH_ENTRY(iter, current_list, cookie_domain_t, entry) { 114 if(ptr_end-subdomain_ptr == iter->subdomain_len 115 && !memcmp(subdomain_ptr, iter->domain, iter->subdomain_len*sizeof(WCHAR))) { 116 current_domain = iter; 117 break; 118 } 119 } 120 121 if(!current_domain) { 122 if(!create) 123 return prev_domain; 124 125 current_domain = heap_alloc(sizeof(*current_domain)); 126 if(!current_domain) 127 return NULL; 128 129 current_domain->domain = heap_strndupW(subdomain_ptr, domain.str + domain.len - subdomain_ptr); 130 if(!current_domain->domain) { 131 heap_free(current_domain); 132 return NULL; 133 } 134 135 current_domain->subdomain_len = ptr_end-subdomain_ptr; 136 137 current_domain->parent = prev_domain; 138 list_init(¤t_domain->path_list); 139 list_init(¤t_domain->subdomain_list); 140 141 list_add_tail(current_list, ¤t_domain->entry); 142 } 143 144 if(ptr == domain.str) 145 return current_domain; 146 147 prev_domain = current_domain; 148 current_list = ¤t_domain->subdomain_list; 149 } 150 } 151 152 static WCHAR *create_cookie_url(substr_t domain, substr_t path, substr_t *ret_path) 153 { 154 WCHAR *p, *url; 155 DWORD len, user_len, i; 156 157 static const WCHAR cookie_prefix[] = {'C','o','o','k','i','e',':'}; 158 159 user_len = 0; 160 if(GetUserNameW(NULL, &user_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) 161 return NULL; 162 163 /* user_len already accounts for terminating NULL */ 164 len = ARRAY_SIZE(cookie_prefix) + user_len + 1 /* @ */ + domain.len + path.len; 165 url = heap_alloc(len * sizeof(WCHAR)); 166 if(!url) 167 return NULL; 168 169 memcpy(url, cookie_prefix, sizeof(cookie_prefix)); 170 p = url + ARRAY_SIZE(cookie_prefix); 171 172 if(!GetUserNameW(p, &user_len)) { 173 heap_free(url); 174 return NULL; 175 } 176 p += user_len; 177 178 *(p - 1) = '@'; 179 180 memcpy(p, domain.str, domain.len*sizeof(WCHAR)); 181 p += domain.len; 182 183 for(i=0; i < path.len; i++) 184 p[i] = towlower(path.str[i]); 185 p[path.len] = 0; 186 187 ret_path->str = p; 188 ret_path->len = path.len; 189 return url; 190 } 191 192 static cookie_container_t *get_cookie_container(substr_t domain, substr_t path, BOOL create) 193 { 194 cookie_domain_t *cookie_domain; 195 cookie_container_t *cookie_container, *iter; 196 197 cookie_domain = get_cookie_domain(domain, create); 198 if(!cookie_domain) 199 return NULL; 200 201 LIST_FOR_EACH_ENTRY(cookie_container, &cookie_domain->path_list, cookie_container_t, entry) { 202 if(cookie_container->path.len < path.len) 203 break; 204 205 if(path.len == cookie_container->path.len && !wcsnicmp(cookie_container->path.str, path.str, path.len)) 206 return cookie_container; 207 } 208 209 if(!create) 210 return NULL; 211 212 cookie_container = heap_alloc(sizeof(*cookie_container)); 213 if(!cookie_container) 214 return NULL; 215 216 cookie_container->cookie_url = create_cookie_url(substrz(cookie_domain->domain), path, &cookie_container->path); 217 if(!cookie_container->cookie_url) { 218 heap_free(cookie_container); 219 return NULL; 220 } 221 222 cookie_container->domain = cookie_domain; 223 list_init(&cookie_container->cookie_list); 224 225 LIST_FOR_EACH_ENTRY(iter, &cookie_domain->path_list, cookie_container_t, entry) { 226 if(iter->path.len <= path.len) { 227 list_add_before(&iter->entry, &cookie_container->entry); 228 return cookie_container; 229 } 230 } 231 232 list_add_tail(&cookie_domain->path_list, &cookie_container->entry); 233 return cookie_container; 234 } 235 236 static void delete_cookie(cookie_t *cookie) 237 { 238 list_remove(&cookie->entry); 239 240 heap_free(cookie->name); 241 heap_free(cookie->data); 242 heap_free(cookie); 243 } 244 245 static cookie_t *alloc_cookie(substr_t name, substr_t data, FILETIME expiry, FILETIME create_time, DWORD flags) 246 { 247 cookie_t *new_cookie; 248 249 new_cookie = heap_alloc_zero(sizeof(*new_cookie)); 250 if(!new_cookie) 251 return NULL; 252 253 new_cookie->expiry = expiry; 254 new_cookie->create = create_time; 255 new_cookie->flags = flags; 256 list_init(&new_cookie->entry); 257 258 if(name.str && !(new_cookie->name = heap_strndupW(name.str, name.len))) { 259 delete_cookie(new_cookie); 260 return NULL; 261 } 262 263 if(data.str && !(new_cookie->data = heap_strndupW(data.str, data.len))) { 264 delete_cookie(new_cookie); 265 return NULL; 266 } 267 268 return new_cookie; 269 } 270 271 static cookie_t *find_cookie(cookie_container_t *container, substr_t name) 272 { 273 cookie_t *iter; 274 275 LIST_FOR_EACH_ENTRY(iter, &container->cookie_list, cookie_t, entry) { 276 if(lstrlenW(iter->name) == name.len && !wcsnicmp(iter->name, name.str, name.len)) 277 return iter; 278 } 279 280 return NULL; 281 } 282 283 static void add_cookie(cookie_container_t *container, cookie_t *new_cookie) 284 { 285 TRACE("Adding %s=%s to %s\n", debugstr_w(new_cookie->name), debugstr_w(new_cookie->data), 286 debugstr_w(container->cookie_url)); 287 288 list_add_tail(&container->cookie_list, &new_cookie->entry); 289 new_cookie->container = container; 290 } 291 292 static void replace_cookie(cookie_container_t *container, cookie_t *new_cookie) 293 { 294 cookie_t *old_cookie; 295 296 old_cookie = find_cookie(container, substrz(new_cookie->name)); 297 if(old_cookie) 298 delete_cookie(old_cookie); 299 300 add_cookie(container, new_cookie); 301 } 302 303 static BOOL cookie_match_path(cookie_container_t *container, substr_t path) 304 { 305 return path.len >= container->path.len && !wcsnicmp(container->path.str, path.str, container->path.len); 306 } 307 308 static BOOL load_persistent_cookie(substr_t domain, substr_t path) 309 { 310 INTERNET_CACHE_ENTRY_INFOW *info; 311 cookie_container_t *cookie_container; 312 cookie_t *new_cookie; 313 HANDLE cookie; 314 char *str = NULL, *pbeg, *pend; 315 DWORD size, flags; 316 WCHAR *name, *data; 317 FILETIME expiry, create, time; 318 319 cookie_container = get_cookie_container(domain, path, TRUE); 320 if(!cookie_container) 321 return FALSE; 322 323 size = 0; 324 RetrieveUrlCacheEntryStreamW(cookie_container->cookie_url, NULL, &size, FALSE, 0); 325 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) 326 return TRUE; 327 info = heap_alloc(size); 328 if(!info) 329 return FALSE; 330 cookie = RetrieveUrlCacheEntryStreamW(cookie_container->cookie_url, info, &size, FALSE, 0); 331 size = info->dwSizeLow; 332 heap_free(info); 333 if(!cookie) 334 return FALSE; 335 336 if(!(str = heap_alloc(size+1)) || !ReadUrlCacheEntryStream(cookie, 0, str, &size, 0)) { 337 UnlockUrlCacheEntryStream(cookie, 0); 338 heap_free(str); 339 return FALSE; 340 } 341 str[size] = 0; 342 UnlockUrlCacheEntryStream(cookie, 0); 343 344 GetSystemTimeAsFileTime(&time); 345 for(pbeg=str; pbeg && *pbeg; name=data=NULL) { 346 pend = strchr(pbeg, '\n'); 347 if(!pend) 348 break; 349 *pend = 0; 350 name = heap_strdupAtoW(pbeg); 351 352 pbeg = pend+1; 353 pend = strchr(pbeg, '\n'); 354 if(!pend) 355 break; 356 *pend = 0; 357 data = heap_strdupAtoW(pbeg); 358 359 pbeg = strchr(pend+1, '\n'); 360 if(!pbeg) 361 break; 362 sscanf(pbeg, "%u %u %u %u %u", &flags, &expiry.dwLowDateTime, &expiry.dwHighDateTime, 363 &create.dwLowDateTime, &create.dwHighDateTime); 364 365 /* skip "*\n" */ 366 pbeg = strchr(pbeg, '*'); 367 if(pbeg) { 368 pbeg++; 369 if(*pbeg) 370 pbeg++; 371 } 372 373 if(!name || !data) 374 break; 375 376 if(CompareFileTime(&time, &expiry) <= 0) { 377 new_cookie = alloc_cookie(substr(NULL, 0), substr(NULL, 0), expiry, create, flags); 378 if(!new_cookie) 379 break; 380 381 new_cookie->name = name; 382 new_cookie->data = data; 383 384 replace_cookie(cookie_container, new_cookie); 385 }else { 386 heap_free(name); 387 heap_free(data); 388 } 389 } 390 heap_free(str); 391 heap_free(name); 392 heap_free(data); 393 394 return TRUE; 395 } 396 397 static BOOL save_persistent_cookie(cookie_container_t *container) 398 { 399 WCHAR cookie_file[MAX_PATH]; 400 HANDLE cookie_handle; 401 cookie_t *cookie_container = NULL, *cookie_iter; 402 BOOL do_save = FALSE; 403 char buf[64], *dyn_buf; 404 FILETIME time; 405 DWORD bytes_written; 406 size_t len; 407 408 /* check if there's anything to save */ 409 GetSystemTimeAsFileTime(&time); 410 LIST_FOR_EACH_ENTRY_SAFE(cookie_container, cookie_iter, &container->cookie_list, cookie_t, entry) 411 { 412 if((cookie_container->expiry.dwLowDateTime || cookie_container->expiry.dwHighDateTime) 413 && CompareFileTime(&time, &cookie_container->expiry) > 0) { 414 delete_cookie(cookie_container); 415 continue; 416 } 417 418 if(!(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)) { 419 do_save = TRUE; 420 break; 421 } 422 } 423 424 if(!do_save) { 425 DeleteUrlCacheEntryW(container->cookie_url); 426 return TRUE; 427 } 428 429 if(!CreateUrlCacheEntryW(container->cookie_url, 0, L"txt", cookie_file, 0)) 430 return FALSE; 431 432 cookie_handle = CreateFileW(cookie_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 433 if(cookie_handle == INVALID_HANDLE_VALUE) { 434 DeleteFileW(cookie_file); 435 return FALSE; 436 } 437 438 LIST_FOR_EACH_ENTRY(cookie_container, &container->cookie_list, cookie_t, entry) 439 { 440 if(cookie_container->flags & INTERNET_COOKIE_IS_SESSION) 441 continue; 442 443 dyn_buf = heap_strdupWtoA(cookie_container->name); 444 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { 445 heap_free(dyn_buf); 446 do_save = FALSE; 447 break; 448 } 449 heap_free(dyn_buf); 450 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) { 451 do_save = FALSE; 452 break; 453 } 454 455 dyn_buf = heap_strdupWtoA(cookie_container->data); 456 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { 457 heap_free(dyn_buf); 458 do_save = FALSE; 459 break; 460 } 461 heap_free(dyn_buf); 462 if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) { 463 do_save = FALSE; 464 break; 465 } 466 467 dyn_buf = heap_strdupWtoA(container->domain->domain); 468 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { 469 heap_free(dyn_buf); 470 do_save = FALSE; 471 break; 472 } 473 heap_free(dyn_buf); 474 475 len = WideCharToMultiByte(CP_ACP, 0, container->path.str, container->path.len, NULL, 0, NULL, NULL); 476 dyn_buf = heap_alloc(len+1); 477 if(dyn_buf) { 478 WideCharToMultiByte(CP_ACP, 0, container->path.str, container->path.len, dyn_buf, len, NULL, NULL); 479 dyn_buf[len] = 0; 480 } 481 if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { 482 heap_free(dyn_buf); 483 do_save = FALSE; 484 break; 485 } 486 heap_free(dyn_buf); 487 488 sprintf(buf, "\n%u\n%u\n%u\n%u\n%u\n*\n", cookie_container->flags, 489 cookie_container->expiry.dwLowDateTime, cookie_container->expiry.dwHighDateTime, 490 cookie_container->create.dwLowDateTime, cookie_container->create.dwHighDateTime); 491 if(!WriteFile(cookie_handle, buf, strlen(buf), &bytes_written, NULL)) { 492 do_save = FALSE; 493 break; 494 } 495 } 496 497 CloseHandle(cookie_handle); 498 if(!do_save) { 499 ERR("error saving cookie file\n"); 500 DeleteFileW(cookie_file); 501 return FALSE; 502 } 503 504 memset(&time, 0, sizeof(time)); 505 return CommitUrlCacheEntryW(container->cookie_url, cookie_file, time, time, 0, NULL, 0, L"txt", 0); 506 } 507 508 static BOOL cookie_parse_url(const WCHAR *url, substr_t *host, substr_t *path) 509 { 510 URL_COMPONENTSW comp = { sizeof(comp) }; 511 512 comp.dwHostNameLength = 1; 513 comp.dwUrlPathLength = 1; 514 515 if(!InternetCrackUrlW(url, 0, 0, &comp) || !comp.dwHostNameLength) 516 return FALSE; 517 518 /* discard the webpage off the end of the path */ 519 while(comp.dwUrlPathLength && comp.lpszUrlPath[comp.dwUrlPathLength-1] != '/') 520 comp.dwUrlPathLength--; 521 522 *host = substr(comp.lpszHostName, comp.dwHostNameLength); 523 *path = comp.dwUrlPathLength ? substr(comp.lpszUrlPath, comp.dwUrlPathLength) : substr(L"/", 1); 524 return TRUE; 525 } 526 527 typedef struct { 528 cookie_t **cookies; 529 unsigned cnt; 530 unsigned size; 531 532 unsigned string_len; 533 } cookie_set_t; 534 535 static DWORD get_cookie(substr_t host, substr_t path, DWORD flags, cookie_set_t *res) 536 { 537 const WCHAR *p; 538 cookie_domain_t *domain; 539 cookie_container_t *container; 540 FILETIME tm; 541 542 GetSystemTimeAsFileTime(&tm); 543 544 p = host.str + host.len; 545 while(p > host.str && p[-1] != '.') p--; 546 while(p != host.str) { 547 p--; 548 while(p > host.str && p[-1] != '.') p--; 549 if(p == host.str) break; 550 551 load_persistent_cookie(substr(p, host.str+host.len-p), substr(L"/", 1)); 552 } 553 554 p = path.str + path.len; 555 do { 556 load_persistent_cookie(host, substr(path.str, p-path.str)); 557 558 p--; 559 while(p > path.str && p[-1] != '/') p--; 560 }while(p != path.str); 561 562 domain = get_cookie_domain(host, FALSE); 563 if(!domain) { 564 TRACE("Unknown host %s\n", debugstr_wn(host.str, host.len)); 565 return ERROR_NO_MORE_ITEMS; 566 } 567 568 for(domain = get_cookie_domain(host, FALSE); domain; domain = domain->parent) { 569 LIST_FOR_EACH_ENTRY(container, &domain->path_list, cookie_container_t, entry) { 570 struct list *cursor, *cursor2; 571 572 if(!cookie_match_path(container, path)) 573 continue; 574 575 LIST_FOR_EACH_SAFE(cursor, cursor2, &container->cookie_list) { 576 cookie_t *cookie_iter = LIST_ENTRY(cursor, cookie_t, entry); 577 578 /* check for expiry */ 579 if((cookie_iter->expiry.dwLowDateTime != 0 || cookie_iter->expiry.dwHighDateTime != 0) 580 && CompareFileTime(&tm, &cookie_iter->expiry) > 0) { 581 TRACE("Found expired cookie. deleting\n"); 582 delete_cookie(cookie_iter); 583 continue; 584 } 585 586 if((cookie_iter->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) 587 continue; 588 589 if(!res->size) { 590 res->cookies = heap_alloc(4*sizeof(*res->cookies)); 591 if(!res->cookies) 592 continue; 593 res->size = 4; 594 }else if(res->cnt == res->size) { 595 cookie_t **new_cookies = heap_realloc(res->cookies, res->size*2*sizeof(*res->cookies)); 596 if(!new_cookies) 597 continue; 598 res->cookies = new_cookies; 599 res->size *= 2; 600 } 601 602 TRACE("%s = %s domain %s path %s\n", debugstr_w(cookie_iter->name), debugstr_w(cookie_iter->data), 603 debugstr_w(domain->domain), debugstr_wn(container->path.str, container->path.len)); 604 605 if(res->cnt) 606 res->string_len += 2; /* '; ' */ 607 res->cookies[res->cnt++] = cookie_iter; 608 609 res->string_len += lstrlenW(cookie_iter->name); 610 if(*cookie_iter->data) 611 res->string_len += 1 /* = */ + lstrlenW(cookie_iter->data); 612 } 613 } 614 } 615 616 return ERROR_SUCCESS; 617 } 618 619 static void cookie_set_to_string(const cookie_set_t *cookie_set, WCHAR *str) 620 { 621 WCHAR *ptr = str; 622 unsigned i, len; 623 624 for(i=0; i<cookie_set->cnt; i++) { 625 if(i) { 626 *ptr++ = ';'; 627 *ptr++ = ' '; 628 } 629 630 len = lstrlenW(cookie_set->cookies[i]->name); 631 memcpy(ptr, cookie_set->cookies[i]->name, len*sizeof(WCHAR)); 632 ptr += len; 633 634 if(*cookie_set->cookies[i]->data) { 635 *ptr++ = '='; 636 len = lstrlenW(cookie_set->cookies[i]->data); 637 memcpy(ptr, cookie_set->cookies[i]->data, len*sizeof(WCHAR)); 638 ptr += len; 639 } 640 } 641 642 assert(ptr-str == cookie_set->string_len); 643 TRACE("%s\n", debugstr_wn(str, ptr-str)); 644 } 645 646 DWORD get_cookie_header(const WCHAR *host, const WCHAR *path, WCHAR **ret) 647 { 648 cookie_set_t cookie_set = {0}; 649 DWORD res; 650 651 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '}; 652 653 EnterCriticalSection(&cookie_cs); 654 655 res = get_cookie(substrz(host), substrz(path), INTERNET_COOKIE_HTTPONLY, &cookie_set); 656 if(res != ERROR_SUCCESS) { 657 LeaveCriticalSection(&cookie_cs); 658 return res; 659 } 660 661 if(cookie_set.cnt) { 662 WCHAR *header, *ptr; 663 664 ptr = header = heap_alloc(sizeof(cookieW) + (cookie_set.string_len + 3 /* crlf0 */) * sizeof(WCHAR)); 665 if(header) { 666 memcpy(ptr, cookieW, sizeof(cookieW)); 667 ptr += ARRAY_SIZE(cookieW); 668 669 cookie_set_to_string(&cookie_set, ptr); 670 heap_free(cookie_set.cookies); 671 ptr += cookie_set.string_len; 672 673 *ptr++ = '\r'; 674 *ptr++ = '\n'; 675 *ptr++ = 0; 676 677 *ret = header; 678 }else { 679 res = ERROR_NOT_ENOUGH_MEMORY; 680 } 681 }else { 682 *ret = NULL; 683 } 684 685 LeaveCriticalSection(&cookie_cs); 686 return res; 687 } 688 689 static void free_cookie_domain_list(struct list *list) 690 { 691 cookie_container_t *container; 692 cookie_domain_t *domain; 693 694 while(!list_empty(list)) { 695 domain = LIST_ENTRY(list_head(list), cookie_domain_t, entry); 696 697 free_cookie_domain_list(&domain->subdomain_list); 698 699 while(!list_empty(&domain->path_list)) { 700 container = LIST_ENTRY(list_head(&domain->path_list), cookie_container_t, entry); 701 702 while(!list_empty(&container->cookie_list)) 703 delete_cookie(LIST_ENTRY(list_head(&container->cookie_list), cookie_t, entry)); 704 705 heap_free(container->cookie_url); 706 list_remove(&container->entry); 707 heap_free(container); 708 } 709 710 heap_free(domain->domain); 711 list_remove(&domain->entry); 712 heap_free(domain); 713 } 714 } 715 716 /*********************************************************************** 717 * InternetGetCookieExW (WININET.@) 718 * 719 * Retrieve cookie from the specified url 720 * 721 * It should be noted that on windows the lpszCookieName parameter is "not implemented". 722 * So it won't be implemented here. 723 * 724 * RETURNS 725 * TRUE on success 726 * FALSE on failure 727 * 728 */ 729 BOOL WINAPI InternetGetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName, 730 LPWSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved) 731 { 732 cookie_set_t cookie_set = {0}; 733 substr_t host, path; 734 DWORD res; 735 BOOL ret; 736 737 TRACE("(%s, %s, %p, %p, %x, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), lpCookieData, lpdwSize, flags, reserved); 738 739 if (flags & ~INTERNET_COOKIE_HTTPONLY) 740 FIXME("flags 0x%08x not supported\n", flags); 741 742 if (!lpszUrl) 743 { 744 SetLastError(ERROR_INVALID_PARAMETER); 745 return FALSE; 746 } 747 748 ret = cookie_parse_url(lpszUrl, &host, &path); 749 if (!ret) { 750 SetLastError(ERROR_INVALID_PARAMETER); 751 return FALSE; 752 } 753 754 EnterCriticalSection(&cookie_cs); 755 756 res = get_cookie(host, path, flags, &cookie_set); 757 if(res != ERROR_SUCCESS) { 758 LeaveCriticalSection(&cookie_cs); 759 SetLastError(res); 760 return FALSE; 761 } 762 763 if(cookie_set.cnt) { 764 if(!lpCookieData || cookie_set.string_len+1 > *lpdwSize) { 765 *lpdwSize = (cookie_set.string_len + 1) * sizeof(WCHAR); 766 TRACE("returning %u\n", *lpdwSize); 767 if(lpCookieData) { 768 SetLastError(ERROR_INSUFFICIENT_BUFFER); 769 ret = FALSE; 770 } 771 }else { 772 *lpdwSize = cookie_set.string_len + 1; 773 cookie_set_to_string(&cookie_set, lpCookieData); 774 lpCookieData[cookie_set.string_len] = 0; 775 } 776 }else { 777 TRACE("no cookies found for %s\n", debugstr_wn(host.str, host.len)); 778 SetLastError(ERROR_NO_MORE_ITEMS); 779 ret = FALSE; 780 } 781 782 heap_free(cookie_set.cookies); 783 LeaveCriticalSection(&cookie_cs); 784 return ret; 785 } 786 787 /*********************************************************************** 788 * InternetGetCookieW (WININET.@) 789 * 790 * Retrieve cookie for the specified URL. 791 */ 792 BOOL WINAPI InternetGetCookieW(const WCHAR *url, const WCHAR *name, WCHAR *data, DWORD *size) 793 { 794 TRACE("(%s, %s, %s, %p)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data), size); 795 796 return InternetGetCookieExW(url, name, data, size, 0, NULL); 797 } 798 799 /*********************************************************************** 800 * InternetGetCookieExA (WININET.@) 801 * 802 * Retrieve cookie from the specified url 803 * 804 * RETURNS 805 * TRUE on success 806 * FALSE on failure 807 * 808 */ 809 BOOL WINAPI InternetGetCookieExA(LPCSTR lpszUrl, LPCSTR lpszCookieName, 810 LPSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved) 811 { 812 WCHAR *url, *name; 813 DWORD len, size = 0; 814 BOOL r; 815 816 TRACE("(%s %s %p %p(%u) %x %p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName), 817 lpCookieData, lpdwSize, lpdwSize ? *lpdwSize : 0, flags, reserved); 818 819 url = heap_strdupAtoW(lpszUrl); 820 name = heap_strdupAtoW(lpszCookieName); 821 822 r = InternetGetCookieExW( url, name, NULL, &len, flags, reserved ); 823 if( r ) 824 { 825 WCHAR *szCookieData; 826 827 szCookieData = heap_alloc(len * sizeof(WCHAR)); 828 if( !szCookieData ) 829 { 830 r = FALSE; 831 } 832 else 833 { 834 r = InternetGetCookieExW( url, name, szCookieData, &len, flags, reserved ); 835 836 if(r) { 837 size = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, NULL, 0, NULL, NULL); 838 if(lpCookieData) { 839 if(*lpdwSize >= size) { 840 WideCharToMultiByte( CP_ACP, 0, szCookieData, len, lpCookieData, *lpdwSize, NULL, NULL); 841 }else { 842 SetLastError(ERROR_INSUFFICIENT_BUFFER); 843 r = FALSE; 844 } 845 } 846 } 847 848 heap_free( szCookieData ); 849 } 850 } 851 *lpdwSize = size; 852 heap_free( name ); 853 heap_free( url ); 854 return r; 855 } 856 857 /*********************************************************************** 858 * InternetGetCookieA (WININET.@) 859 * 860 * See InternetGetCookieW. 861 */ 862 BOOL WINAPI InternetGetCookieA(const char *url, const char *name, char *data, DWORD *size) 863 { 864 TRACE("(%s, %s, %p, %p)\n", debugstr_a(url), debugstr_a(name), data, size); 865 866 return InternetGetCookieExA(url, name, data, size, 0, NULL); 867 } 868 869 static BOOL is_domain_legal_for_cookie(substr_t domain, substr_t full_domain) 870 { 871 const WCHAR *ptr; 872 873 if(!domain.len || *domain.str == '.' || !full_domain.len || *full_domain.str == '.') { 874 SetLastError(ERROR_INVALID_NAME); 875 return FALSE; 876 } 877 878 if(domain.len > full_domain.len || !wmemchr(domain.str, '.', domain.len) || !wmemchr(full_domain.str, '.', full_domain.len)) 879 return FALSE; 880 881 ptr = full_domain.str + full_domain.len - domain.len; 882 if (wcsnicmp(domain.str, ptr, domain.len) || (full_domain.len > domain.len && ptr[-1] != '.')) { 883 SetLastError(ERROR_INVALID_PARAMETER); 884 return FALSE; 885 } 886 887 return TRUE; 888 } 889 890 /*********************************************************************** 891 * IsDomainLegalCookieDomainW (WININET.@) 892 */ 893 BOOL WINAPI IsDomainLegalCookieDomainW(const WCHAR *domain, const WCHAR *full_domain) 894 { 895 FIXME("(%s, %s) semi-stub\n", debugstr_w(domain), debugstr_w(full_domain)); 896 897 if (!domain || !full_domain) { 898 SetLastError(ERROR_INVALID_PARAMETER); 899 return FALSE; 900 } 901 902 return is_domain_legal_for_cookie(substrz(domain), substrz(full_domain)); 903 } 904 905 static void substr_skip(substr_t *str, size_t len) 906 { 907 assert(str->len >= len); 908 str->str += len; 909 str->len -= len; 910 } 911 912 DWORD set_cookie(substr_t domain, substr_t path, substr_t name, substr_t data, DWORD flags) 913 { 914 cookie_container_t *container; 915 cookie_t *thisCookie; 916 substr_t value; 917 const WCHAR *end_ptr; 918 FILETIME expiry, create; 919 BOOL expired = FALSE, update_persistent = FALSE; 920 DWORD cookie_flags = 0, len; 921 922 TRACE("%s %s %s=%s %x\n", debugstr_wn(domain.str, domain.len), debugstr_wn(path.str, path.len), 923 debugstr_wn(name.str, name.len), debugstr_wn(data.str, data.len), flags); 924 925 memset(&expiry,0,sizeof(expiry)); 926 GetSystemTimeAsFileTime(&create); 927 928 /* lots of information can be parsed out of the cookie value */ 929 930 if(!(end_ptr = wmemchr(data.str, ';', data.len))) 931 end_ptr = data.str + data.len; 932 value = substr(data.str, end_ptr-data.str); 933 data.str += value.len; 934 data.len -= value.len; 935 936 for(;;) { 937 static const WCHAR szDomain[] = {'d','o','m','a','i','n','='}; 938 static const WCHAR szPath[] = {'p','a','t','h','='}; 939 static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','='}; 940 static const WCHAR szSecure[] = {'s','e','c','u','r','e'}; 941 static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y'}; 942 static const WCHAR szVersion[] = {'v','e','r','s','i','o','n','='}; 943 static const WCHAR max_ageW[] = {'m','a','x','-','a','g','e','='}; 944 945 /* Skip ';' */ 946 if(data.len) 947 substr_skip(&data, 1); 948 949 while(data.len && *data.str == ' ') 950 substr_skip(&data, 1); 951 952 if(!data.len) 953 break; 954 955 if(!(end_ptr = wmemchr(data.str, ';', data.len))) 956 end_ptr = data.str + data.len; 957 958 if(data.len >= (len = ARRAY_SIZE(szDomain)) && !wcsnicmp(data.str, szDomain, len)) { 959 substr_skip(&data, len); 960 961 if(data.len && *data.str == '.') 962 substr_skip(&data, 1); 963 964 if(!is_domain_legal_for_cookie(substr(data.str, end_ptr-data.str), domain)) 965 return COOKIE_STATE_UNKNOWN; 966 967 domain = substr(data.str, end_ptr-data.str); 968 TRACE("Parsing new domain %s\n", debugstr_wn(domain.str, domain.len)); 969 }else if(data.len >= (len = ARRAY_SIZE(szPath)) && !wcsnicmp(data.str, szPath, len)) { 970 substr_skip(&data, len); 971 path = substr(data.str, end_ptr - data.str); 972 TRACE("Parsing new path %s\n", debugstr_wn(path.str, path.len)); 973 }else if(data.len >= (len = ARRAY_SIZE(szExpires)) && !wcsnicmp(data.str, szExpires, len)) { 974 SYSTEMTIME st; 975 WCHAR buf[128]; 976 977 substr_skip(&data, len); 978 979 if(end_ptr > data.str && (end_ptr - data.str < ARRAY_SIZE(buf) - 1)) { 980 memcpy(buf, data.str, data.len*sizeof(WCHAR)); 981 buf[data.len] = 0; 982 983 if (InternetTimeToSystemTimeW(data.str, &st, 0)) { 984 SystemTimeToFileTime(&st, &expiry); 985 986 if (CompareFileTime(&create,&expiry) > 0) { 987 TRACE("Cookie already expired.\n"); 988 expired = TRUE; 989 } 990 } 991 } 992 }else if(data.len >= (len = ARRAY_SIZE(szSecure)) && !wcsnicmp(data.str, szSecure, len)) { 993 substr_skip(&data, len); 994 FIXME("secure not handled\n"); 995 }else if(data.len >= (len = ARRAY_SIZE(szHttpOnly)) && !wcsnicmp(data.str, szHttpOnly, len)) { 996 substr_skip(&data, len); 997 998 if(!(flags & INTERNET_COOKIE_HTTPONLY)) { 999 WARN("HTTP only cookie added without INTERNET_COOKIE_HTTPONLY flag\n"); 1000 SetLastError(ERROR_INVALID_OPERATION); 1001 return COOKIE_STATE_REJECT; 1002 } 1003 1004 cookie_flags |= INTERNET_COOKIE_HTTPONLY; 1005 }else if(data.len >= (len = ARRAY_SIZE(szVersion)) && !wcsnicmp(data.str, szVersion, len)) { 1006 substr_skip(&data, len); 1007 1008 FIXME("version not handled (%s)\n",debugstr_wn(data.str, data.len)); 1009 }else if(data.len >= (len = ARRAY_SIZE(max_ageW)) && !wcsnicmp(data.str, max_ageW, len)) { 1010 /* Native doesn't support Max-Age attribute. */ 1011 WARN("Max-Age ignored\n"); 1012 }else if(data.len) { 1013 FIXME("Unknown additional option %s\n", debugstr_wn(data.str, data.len)); 1014 } 1015 1016 substr_skip(&data, end_ptr - data.str); 1017 } 1018 1019 EnterCriticalSection(&cookie_cs); 1020 1021 load_persistent_cookie(domain, path); 1022 1023 container = get_cookie_container(domain, path, !expired); 1024 if(!container) { 1025 LeaveCriticalSection(&cookie_cs); 1026 return COOKIE_STATE_ACCEPT; 1027 } 1028 1029 if(!expiry.dwLowDateTime && !expiry.dwHighDateTime) 1030 cookie_flags |= INTERNET_COOKIE_IS_SESSION; 1031 else 1032 update_persistent = TRUE; 1033 1034 if ((thisCookie = find_cookie(container, name))) { 1035 if ((thisCookie->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) { 1036 WARN("An attempt to override httponly cookie\n"); 1037 SetLastError(ERROR_INVALID_OPERATION); 1038 LeaveCriticalSection(&cookie_cs); 1039 return COOKIE_STATE_REJECT; 1040 } 1041 1042 if (!(thisCookie->flags & INTERNET_COOKIE_IS_SESSION)) 1043 update_persistent = TRUE; 1044 delete_cookie(thisCookie); 1045 } 1046 1047 TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_wn(name.str, name.len), 1048 debugstr_wn(value.str, value.len), debugstr_w(container->domain->domain), 1049 debugstr_wn(container->path.str, container->path.len)); 1050 1051 if (!expired) { 1052 cookie_t *new_cookie; 1053 1054 new_cookie = alloc_cookie(name, value, expiry, create, cookie_flags); 1055 if(!new_cookie) { 1056 LeaveCriticalSection(&cookie_cs); 1057 return COOKIE_STATE_UNKNOWN; 1058 } 1059 1060 add_cookie(container, new_cookie); 1061 } 1062 1063 if (!update_persistent || save_persistent_cookie(container)) 1064 { 1065 LeaveCriticalSection(&cookie_cs); 1066 return COOKIE_STATE_ACCEPT; 1067 } 1068 LeaveCriticalSection(&cookie_cs); 1069 return COOKIE_STATE_UNKNOWN; 1070 } 1071 1072 /*********************************************************************** 1073 * InternetSetCookieExW (WININET.@) 1074 * 1075 * Sets cookie for the specified url 1076 */ 1077 DWORD WINAPI InternetSetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName, 1078 LPCWSTR lpCookieData, DWORD flags, DWORD_PTR reserved) 1079 { 1080 substr_t host, path, name, data; 1081 BOOL ret; 1082 1083 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_w(lpszUrl), debugstr_w(lpszCookieName), 1084 debugstr_w(lpCookieData), flags, reserved); 1085 1086 if (flags & ~INTERNET_COOKIE_HTTPONLY) 1087 FIXME("flags %x not supported\n", flags); 1088 1089 if (!lpszUrl || !lpCookieData) 1090 { 1091 SetLastError(ERROR_INVALID_PARAMETER); 1092 return COOKIE_STATE_UNKNOWN; 1093 } 1094 1095 ret = cookie_parse_url(lpszUrl, &host, &path); 1096 if (!ret || !host.len) return COOKIE_STATE_UNKNOWN; 1097 1098 if (!lpszCookieName) { 1099 const WCHAR *ptr; 1100 1101 /* some apps (or is it us??) try to add a cookie with no cookie name, but 1102 * the cookie data in the form of name[=data]. 1103 */ 1104 if (!(ptr = wcschr(lpCookieData, '='))) 1105 ptr = lpCookieData + lstrlenW(lpCookieData); 1106 1107 name = substr(lpCookieData, ptr - lpCookieData); 1108 data = substrz(*ptr == '=' ? ptr+1 : ptr); 1109 }else { 1110 name = substrz(lpszCookieName); 1111 data = substrz(lpCookieData); 1112 } 1113 1114 return set_cookie(host, path, name, data, flags); 1115 } 1116 1117 /*********************************************************************** 1118 * InternetSetCookieW (WININET.@) 1119 * 1120 * Sets a cookie for the specified URL. 1121 */ 1122 BOOL WINAPI InternetSetCookieW(const WCHAR *url, const WCHAR *name, const WCHAR *data) 1123 { 1124 TRACE("(%s, %s, %s)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data)); 1125 1126 return InternetSetCookieExW(url, name, data, 0, 0) == COOKIE_STATE_ACCEPT; 1127 } 1128 1129 /*********************************************************************** 1130 * InternetSetCookieA (WININET.@) 1131 * 1132 * Sets cookie for the specified url 1133 * 1134 * RETURNS 1135 * TRUE on success 1136 * FALSE on failure 1137 * 1138 */ 1139 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName, 1140 LPCSTR lpCookieData) 1141 { 1142 LPWSTR data, url, name; 1143 BOOL r; 1144 1145 TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl), 1146 debugstr_a(lpszCookieName), debugstr_a(lpCookieData)); 1147 1148 url = heap_strdupAtoW(lpszUrl); 1149 name = heap_strdupAtoW(lpszCookieName); 1150 data = heap_strdupAtoW(lpCookieData); 1151 1152 r = InternetSetCookieW( url, name, data ); 1153 1154 heap_free( data ); 1155 heap_free( name ); 1156 heap_free( url ); 1157 return r; 1158 } 1159 1160 /*********************************************************************** 1161 * InternetSetCookieExA (WININET.@) 1162 * 1163 * See InternetSetCookieExW. 1164 */ 1165 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData, 1166 DWORD dwFlags, DWORD_PTR dwReserved) 1167 { 1168 WCHAR *data, *url, *name; 1169 DWORD r; 1170 1171 TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_a(lpszURL), debugstr_a(lpszCookieName), 1172 debugstr_a(lpszCookieData), dwFlags, dwReserved); 1173 1174 url = heap_strdupAtoW(lpszURL); 1175 name = heap_strdupAtoW(lpszCookieName); 1176 data = heap_strdupAtoW(lpszCookieData); 1177 1178 r = InternetSetCookieExW(url, name, data, dwFlags, dwReserved); 1179 1180 heap_free( data ); 1181 heap_free( name ); 1182 heap_free( url ); 1183 return r; 1184 } 1185 1186 /*********************************************************************** 1187 * InternetClearAllPerSiteCookieDecisions (WININET.@) 1188 * 1189 * Clears all per-site decisions about cookies. 1190 * 1191 * RETURNS 1192 * TRUE on success 1193 * FALSE on failure 1194 * 1195 */ 1196 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID ) 1197 { 1198 FIXME("stub\n"); 1199 return TRUE; 1200 } 1201 1202 /*********************************************************************** 1203 * InternetEnumPerSiteCookieDecisionA (WININET.@) 1204 * 1205 * See InternetEnumPerSiteCookieDecisionW. 1206 */ 1207 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, ULONG *pcSiteNameSize, 1208 ULONG *pdwDecision, ULONG dwIndex ) 1209 { 1210 FIXME("(%s, %p, %p, 0x%08x) stub\n", 1211 debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex); 1212 return FALSE; 1213 } 1214 1215 /*********************************************************************** 1216 * InternetEnumPerSiteCookieDecisionW (WININET.@) 1217 * 1218 * Enumerates all per-site decisions about cookies. 1219 * 1220 * RETURNS 1221 * TRUE on success 1222 * FALSE on failure 1223 * 1224 */ 1225 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, ULONG *pcSiteNameSize, 1226 ULONG *pdwDecision, ULONG dwIndex ) 1227 { 1228 FIXME("(%s, %p, %p, 0x%08x) stub\n", 1229 debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex); 1230 return FALSE; 1231 } 1232 1233 /*********************************************************************** 1234 * InternetGetPerSiteCookieDecisionA (WININET.@) 1235 */ 1236 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, ULONG *pResult ) 1237 { 1238 FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult); 1239 return FALSE; 1240 } 1241 1242 /*********************************************************************** 1243 * InternetGetPerSiteCookieDecisionW (WININET.@) 1244 */ 1245 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, ULONG *pResult ) 1246 { 1247 FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult); 1248 return FALSE; 1249 } 1250 1251 /*********************************************************************** 1252 * InternetSetPerSiteCookieDecisionA (WININET.@) 1253 */ 1254 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision ) 1255 { 1256 FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision); 1257 return FALSE; 1258 } 1259 1260 /*********************************************************************** 1261 * InternetSetPerSiteCookieDecisionW (WININET.@) 1262 */ 1263 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision ) 1264 { 1265 FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision); 1266 return FALSE; 1267 } 1268 1269 void free_cookie(void) 1270 { 1271 EnterCriticalSection(&cookie_cs); 1272 1273 free_cookie_domain_list(&domain_list); 1274 1275 LeaveCriticalSection(&cookie_cs); 1276 } 1277