| /* |
| * Wininet - cookie handling stuff |
| * |
| * Copyright 2002 TransGaming Technologies Inc. |
| * |
| * David Hammerton |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "ws2tcpip.h" |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wininet.h" |
| #include "winerror.h" |
| |
| #include "wine/debug.h" |
| #include "internet.h" |
| |
| #define RESPONSE_TIMEOUT 30 /* FROM internet.c */ |
| |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(wininet); |
| |
| /* FIXME |
| * Cookies could use A LOT OF MEMORY. We need some kind of memory management here! |
| */ |
| |
| struct _cookie_domain_t; |
| struct _cookie_container_t; |
| |
| typedef struct _cookie_t { |
| struct list entry; |
| |
| struct _cookie_container_t *container; |
| |
| WCHAR *name; |
| WCHAR *data; |
| DWORD flags; |
| FILETIME expiry; |
| FILETIME create; |
| } cookie_t; |
| |
| typedef struct _cookie_container_t { |
| struct list entry; |
| |
| WCHAR *path; |
| struct _cookie_domain_t *domain; |
| |
| struct list cookie_list; |
| } cookie_container_t; |
| |
| typedef struct _cookie_domain_t { |
| struct list entry; |
| |
| WCHAR *domain; |
| unsigned subdomain_len; |
| |
| struct _cookie_domain_t *parent; |
| struct list subdomain_list; |
| |
| /* List of stored paths sorted by length of the path. */ |
| struct list path_list; |
| } cookie_domain_t; |
| |
| static CRITICAL_SECTION cookie_cs; |
| static CRITICAL_SECTION_DEBUG cookie_cs_debug = |
| { |
| 0, 0, &cookie_cs, |
| { &cookie_cs_debug.ProcessLocksList, &cookie_cs_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": cookie_cs") } |
| }; |
| static CRITICAL_SECTION cookie_cs = { &cookie_cs_debug, -1, 0, 0, 0, 0 }; |
| static struct list domain_list = LIST_INIT(domain_list); |
| |
| static cookie_domain_t *get_cookie_domain(const WCHAR *domain, BOOL create) |
| { |
| const WCHAR *ptr = domain + strlenW(domain), *ptr_end, *subdomain_ptr; |
| cookie_domain_t *iter, *current_domain, *prev_domain = NULL; |
| struct list *current_list = &domain_list; |
| |
| while(1) { |
| for(ptr_end = ptr--; ptr > domain && *ptr != '.'; ptr--); |
| subdomain_ptr = *ptr == '.' ? ptr+1 : ptr; |
| |
| current_domain = NULL; |
| LIST_FOR_EACH_ENTRY(iter, current_list, cookie_domain_t, entry) { |
| if(ptr_end-subdomain_ptr == iter->subdomain_len && !memcmp(subdomain_ptr, iter->domain, iter->subdomain_len)) { |
| current_domain = iter; |
| break; |
| } |
| } |
| |
| if(!current_domain) { |
| if(!create) |
| return prev_domain; |
| |
| current_domain = heap_alloc(sizeof(*current_domain)); |
| if(!current_domain) |
| return NULL; |
| |
| current_domain->domain = heap_strdupW(subdomain_ptr); |
| if(!current_domain->domain) { |
| heap_free(current_domain); |
| return NULL; |
| } |
| |
| current_domain->subdomain_len = ptr_end-subdomain_ptr; |
| |
| current_domain->parent = prev_domain; |
| list_init(¤t_domain->path_list); |
| list_init(¤t_domain->subdomain_list); |
| |
| list_add_tail(current_list, ¤t_domain->entry); |
| } |
| |
| if(ptr == domain) |
| return current_domain; |
| |
| prev_domain = current_domain; |
| current_list = ¤t_domain->subdomain_list; |
| } |
| } |
| |
| static cookie_container_t *get_cookie_container(const WCHAR *domain, const WCHAR *path, BOOL create) |
| { |
| cookie_domain_t *cookie_domain; |
| cookie_container_t *cookie_container, *iter; |
| size_t path_len, len; |
| |
| cookie_domain = get_cookie_domain(domain, create); |
| if(!cookie_domain) |
| return NULL; |
| |
| path_len = strlenW(path); |
| |
| LIST_FOR_EACH_ENTRY(cookie_container, &cookie_domain->path_list, cookie_container_t, entry) { |
| len = strlenW(cookie_container->path); |
| if(len < path_len) |
| break; |
| |
| if(!strcmpiW(cookie_container->path, path)) |
| return cookie_container; |
| } |
| |
| if(!create) |
| return NULL; |
| |
| cookie_container = heap_alloc(sizeof(*cookie_container)); |
| if(!cookie_container) |
| return NULL; |
| |
| cookie_container->path = heap_strdupW(path); |
| if(!cookie_container->path) { |
| heap_free(cookie_container); |
| return NULL; |
| } |
| |
| cookie_container->domain = cookie_domain; |
| list_init(&cookie_container->cookie_list); |
| |
| |
| LIST_FOR_EACH_ENTRY(iter, &cookie_domain->path_list, cookie_container_t, entry) { |
| if(strlenW(iter->path) <= path_len) { |
| list_add_before(&iter->entry, &cookie_container->entry); |
| return cookie_container; |
| } |
| } |
| |
| list_add_tail(&cookie_domain->path_list, &cookie_container->entry); |
| return cookie_container; |
| } |
| |
| static void delete_cookie(cookie_t *cookie) |
| { |
| list_remove(&cookie->entry); |
| |
| heap_free(cookie->name); |
| heap_free(cookie->data); |
| heap_free(cookie); |
| } |
| |
| static cookie_t *alloc_cookie(const WCHAR *name, const WCHAR *data, FILETIME expiry, FILETIME create_time, DWORD flags) |
| { |
| cookie_t *new_cookie; |
| |
| new_cookie = heap_alloc(sizeof(*new_cookie)); |
| if(!new_cookie) |
| return NULL; |
| |
| new_cookie->expiry = expiry; |
| new_cookie->create = create_time; |
| new_cookie->flags = flags; |
| list_init(&new_cookie->entry); |
| |
| new_cookie->name = heap_strdupW(name); |
| new_cookie->data = heap_strdupW(data); |
| if((name && !new_cookie->name) || (data && !new_cookie->data)) { |
| delete_cookie(new_cookie); |
| return NULL; |
| } |
| |
| return new_cookie; |
| } |
| |
| static cookie_t *find_cookie(cookie_container_t *container, const WCHAR *name) |
| { |
| cookie_t *iter; |
| |
| LIST_FOR_EACH_ENTRY(iter, &container->cookie_list, cookie_t, entry) { |
| if(!strcmpiW(iter->name, name)) |
| return iter; |
| } |
| |
| return NULL; |
| } |
| |
| static void add_cookie(cookie_container_t *container, cookie_t *new_cookie) |
| { |
| TRACE("Adding %s=%s to %s %s\n", debugstr_w(new_cookie->name), debugstr_w(new_cookie->data), |
| debugstr_w(container->domain->domain), debugstr_w(container->path)); |
| |
| list_add_tail(&container->cookie_list, &new_cookie->entry); |
| new_cookie->container = container; |
| } |
| |
| static void replace_cookie(cookie_container_t *container, cookie_t *new_cookie) |
| { |
| cookie_t *old_cookie; |
| |
| old_cookie = find_cookie(container, new_cookie->name); |
| if(old_cookie) |
| delete_cookie(old_cookie); |
| |
| add_cookie(container, new_cookie); |
| } |
| |
| static BOOL cookie_match_path(cookie_container_t *container, const WCHAR *path) |
| { |
| return !strncmpiW(container->path, path, strlenW(container->path)); |
| } |
| |
| static BOOL create_cookie_url(LPCWSTR domain, LPCWSTR path, WCHAR *buf, DWORD buf_len) |
| { |
| static const WCHAR cookie_prefix[] = {'C','o','o','k','i','e',':'}; |
| |
| WCHAR *p; |
| DWORD len; |
| |
| if(buf_len < sizeof(cookie_prefix)/sizeof(WCHAR)) |
| return FALSE; |
| memcpy(buf, cookie_prefix, sizeof(cookie_prefix)); |
| buf += sizeof(cookie_prefix)/sizeof(WCHAR); |
| buf_len -= sizeof(cookie_prefix)/sizeof(WCHAR); |
| p = buf; |
| |
| len = buf_len; |
| if(!GetUserNameW(buf, &len)) |
| return FALSE; |
| buf += len-1; |
| buf_len -= len-1; |
| |
| if(!buf_len) |
| return FALSE; |
| *(buf++) = '@'; |
| buf_len--; |
| |
| len = strlenW(domain); |
| if(len >= buf_len) |
| return FALSE; |
| memcpy(buf, domain, len*sizeof(WCHAR)); |
| buf += len; |
| buf_len -= len; |
| |
| len = strlenW(path); |
| if(len >= buf_len) |
| return FALSE; |
| memcpy(buf, path, len*sizeof(WCHAR)); |
| buf += len; |
| |
| *buf = 0; |
| |
| for(; *p; p++) |
| *p = tolowerW(*p); |
| return TRUE; |
| } |
| |
| static BOOL load_persistent_cookie(LPCWSTR domain, LPCWSTR path) |
| { |
| INTERNET_CACHE_ENTRY_INFOW *info; |
| cookie_container_t *cookie_container; |
| cookie_t *new_cookie; |
| WCHAR cookie_url[MAX_PATH]; |
| HANDLE cookie; |
| char *str = NULL, *pbeg, *pend; |
| DWORD size, flags; |
| WCHAR *name, *data; |
| FILETIME expiry, create, time; |
| |
| if (!create_cookie_url(domain, path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0]))) |
| return FALSE; |
| |
| size = 0; |
| RetrieveUrlCacheEntryStreamW(cookie_url, NULL, &size, FALSE, 0); |
| if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) |
| return TRUE; |
| info = heap_alloc(size); |
| if(!info) |
| return FALSE; |
| cookie = RetrieveUrlCacheEntryStreamW(cookie_url, info, &size, FALSE, 0); |
| size = info->dwSizeLow; |
| heap_free(info); |
| if(!cookie) |
| return FALSE; |
| |
| if(!(str = heap_alloc(size+1)) || !ReadUrlCacheEntryStream(cookie, 0, str, &size, 0)) { |
| UnlockUrlCacheEntryStream(cookie, 0); |
| heap_free(str); |
| return FALSE; |
| } |
| str[size] = 0; |
| UnlockUrlCacheEntryStream(cookie, 0); |
| |
| cookie_container = get_cookie_container(domain, path, TRUE); |
| if(!cookie_container) { |
| heap_free(str); |
| return FALSE; |
| } |
| |
| GetSystemTimeAsFileTime(&time); |
| for(pbeg=str; pbeg && *pbeg; name=data=NULL) { |
| pend = strchr(pbeg, '\n'); |
| if(!pend) |
| break; |
| *pend = 0; |
| name = heap_strdupAtoW(pbeg); |
| |
| pbeg = pend+1; |
| pend = strchr(pbeg, '\n'); |
| if(!pend) |
| break; |
| *pend = 0; |
| data = heap_strdupAtoW(pbeg); |
| |
| pbeg = strchr(pend+1, '\n'); |
| if(!pbeg) |
| break; |
| sscanf(pbeg, "%u %u %u %u %u", &flags, &expiry.dwLowDateTime, &expiry.dwHighDateTime, |
| &create.dwLowDateTime, &create.dwHighDateTime); |
| |
| /* skip "*\n" */ |
| pbeg = strchr(pbeg, '*'); |
| if(pbeg) { |
| pbeg++; |
| if(*pbeg) |
| pbeg++; |
| } |
| |
| if(!name || !data) |
| break; |
| |
| if(CompareFileTime(&time, &expiry) <= 0) { |
| new_cookie = alloc_cookie(NULL, NULL, expiry, create, flags); |
| if(!new_cookie) |
| break; |
| |
| new_cookie->name = name; |
| new_cookie->data = data; |
| |
| replace_cookie(cookie_container, new_cookie); |
| }else { |
| heap_free(name); |
| heap_free(data); |
| } |
| } |
| heap_free(str); |
| heap_free(name); |
| heap_free(data); |
| |
| return TRUE; |
| } |
| |
| static BOOL save_persistent_cookie(cookie_container_t *container) |
| { |
| static const WCHAR txtW[] = {'t','x','t',0}; |
| |
| WCHAR cookie_url[MAX_PATH], cookie_file[MAX_PATH]; |
| HANDLE cookie_handle; |
| cookie_t *cookie_container = NULL, *cookie_iter; |
| BOOL do_save = FALSE; |
| char buf[64], *dyn_buf; |
| FILETIME time; |
| DWORD bytes_written; |
| |
| if (!create_cookie_url(container->domain->domain, container->path, cookie_url, sizeof(cookie_url)/sizeof(cookie_url[0]))) |
| return FALSE; |
| |
| /* check if there's anything to save */ |
| GetSystemTimeAsFileTime(&time); |
| LIST_FOR_EACH_ENTRY_SAFE(cookie_container, cookie_iter, &container->cookie_list, cookie_t, entry) |
| { |
| if((cookie_container->expiry.dwLowDateTime || cookie_container->expiry.dwHighDateTime) |
| && CompareFileTime(&time, &cookie_container->expiry) > 0) { |
| delete_cookie(cookie_container); |
| continue; |
| } |
| |
| if(!(cookie_container->flags & INTERNET_COOKIE_IS_SESSION)) { |
| do_save = TRUE; |
| break; |
| } |
| } |
| if(!do_save) { |
| DeleteUrlCacheEntryW(cookie_url); |
| return TRUE; |
| } |
| |
| if(!CreateUrlCacheEntryW(cookie_url, 0, txtW, cookie_file, 0)) |
| return FALSE; |
| cookie_handle = CreateFileW(cookie_file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); |
| if(cookie_handle == INVALID_HANDLE_VALUE) { |
| DeleteFileW(cookie_file); |
| return FALSE; |
| } |
| |
| LIST_FOR_EACH_ENTRY(cookie_container, &container->cookie_list, cookie_t, entry) |
| { |
| if(cookie_container->flags & INTERNET_COOKIE_IS_SESSION) |
| continue; |
| |
| dyn_buf = heap_strdupWtoA(cookie_container->name); |
| if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { |
| heap_free(dyn_buf); |
| do_save = FALSE; |
| break; |
| } |
| heap_free(dyn_buf); |
| if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) { |
| do_save = FALSE; |
| break; |
| } |
| |
| dyn_buf = heap_strdupWtoA(cookie_container->data); |
| if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { |
| heap_free(dyn_buf); |
| do_save = FALSE; |
| break; |
| } |
| heap_free(dyn_buf); |
| if(!WriteFile(cookie_handle, "\n", 1, &bytes_written, NULL)) { |
| do_save = FALSE; |
| break; |
| } |
| |
| dyn_buf = heap_strdupWtoA(container->domain->domain); |
| if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { |
| heap_free(dyn_buf); |
| do_save = FALSE; |
| break; |
| } |
| heap_free(dyn_buf); |
| |
| dyn_buf = heap_strdupWtoA(container->path); |
| if(!dyn_buf || !WriteFile(cookie_handle, dyn_buf, strlen(dyn_buf), &bytes_written, NULL)) { |
| heap_free(dyn_buf); |
| do_save = FALSE; |
| break; |
| } |
| heap_free(dyn_buf); |
| |
| sprintf(buf, "\n%u\n%u\n%u\n%u\n%u\n*\n", cookie_container->flags, |
| cookie_container->expiry.dwLowDateTime, cookie_container->expiry.dwHighDateTime, |
| cookie_container->create.dwLowDateTime, cookie_container->create.dwHighDateTime); |
| if(!WriteFile(cookie_handle, buf, strlen(buf), &bytes_written, NULL)) { |
| do_save = FALSE; |
| break; |
| } |
| } |
| |
| CloseHandle(cookie_handle); |
| if(!do_save) { |
| ERR("error saving cookie file\n"); |
| DeleteFileW(cookie_file); |
| return FALSE; |
| } |
| |
| memset(&time, 0, sizeof(time)); |
| return CommitUrlCacheEntryW(cookie_url, cookie_file, time, time, 0, NULL, 0, txtW, 0); |
| } |
| |
| static BOOL COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen) |
| { |
| URL_COMPONENTSW UrlComponents; |
| |
| UrlComponents.lpszExtraInfo = NULL; |
| UrlComponents.lpszPassword = NULL; |
| UrlComponents.lpszScheme = NULL; |
| UrlComponents.lpszUrlPath = path; |
| UrlComponents.lpszUserName = NULL; |
| UrlComponents.lpszHostName = hostName; |
| UrlComponents.dwExtraInfoLength = 0; |
| UrlComponents.dwPasswordLength = 0; |
| UrlComponents.dwSchemeLength = 0; |
| UrlComponents.dwUserNameLength = 0; |
| UrlComponents.dwHostNameLength = hostNameLen; |
| UrlComponents.dwUrlPathLength = pathLen; |
| |
| if (!InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents)) return FALSE; |
| |
| /* discard the webpage off the end of the path */ |
| if (UrlComponents.dwUrlPathLength) |
| { |
| if (path[UrlComponents.dwUrlPathLength - 1] != '/') |
| { |
| WCHAR *ptr; |
| if ((ptr = strrchrW(path, '/'))) *(++ptr) = 0; |
| else |
| { |
| path[0] = '/'; |
| path[1] = 0; |
| } |
| } |
| } |
| else if (pathLen >= 2) |
| { |
| path[0] = '/'; |
| path[1] = 0; |
| } |
| return TRUE; |
| } |
| |
| typedef struct { |
| cookie_t **cookies; |
| unsigned cnt; |
| unsigned size; |
| |
| unsigned string_len; |
| } cookie_set_t; |
| |
| static DWORD get_cookie(const WCHAR *host, const WCHAR *path, DWORD flags, cookie_set_t *res) |
| { |
| static const WCHAR empty_path[] = { '/',0 }; |
| |
| WCHAR *ptr, subpath[INTERNET_MAX_PATH_LENGTH]; |
| const WCHAR *p; |
| cookie_domain_t *domain; |
| cookie_container_t *container; |
| unsigned len; |
| FILETIME tm; |
| |
| GetSystemTimeAsFileTime(&tm); |
| |
| len = strlenW(host); |
| p = host+len; |
| while(p>host && p[-1]!='.') p--; |
| while(p != host) { |
| p--; |
| while(p>host && p[-1]!='.') p--; |
| if(p == host) break; |
| |
| load_persistent_cookie(p, empty_path); |
| } |
| |
| len = strlenW(path); |
| assert(len+1 < INTERNET_MAX_PATH_LENGTH); |
| memcpy(subpath, path, (len+1)*sizeof(WCHAR)); |
| ptr = subpath+len; |
| do { |
| *ptr = 0; |
| load_persistent_cookie(host, subpath); |
| |
| ptr--; |
| while(ptr>subpath && ptr[-1]!='/') ptr--; |
| }while(ptr != subpath); |
| |
| domain = get_cookie_domain(host, FALSE); |
| if(!domain) { |
| TRACE("Unknown host %s\n", debugstr_w(host)); |
| return ERROR_NO_MORE_ITEMS; |
| } |
| |
| for(domain = get_cookie_domain(host, FALSE); domain; domain = domain->parent) { |
| TRACE("Trying %s domain...\n", debugstr_w(domain->domain)); |
| |
| LIST_FOR_EACH_ENTRY(container, &domain->path_list, cookie_container_t, entry) { |
| struct list *cursor, *cursor2; |
| |
| TRACE("path %s\n", debugstr_w(container->path)); |
| |
| if(!cookie_match_path(container, path)) |
| continue; |
| |
| TRACE("found domain %p\n", domain->domain); |
| |
| LIST_FOR_EACH_SAFE(cursor, cursor2, &container->cookie_list) { |
| cookie_t *cookie_iter = LIST_ENTRY(cursor, cookie_t, entry); |
| |
| /* check for expiry */ |
| if((cookie_iter->expiry.dwLowDateTime != 0 || cookie_iter->expiry.dwHighDateTime != 0) |
| && CompareFileTime(&tm, &cookie_iter->expiry) > 0) { |
| TRACE("Found expired cookie. deleting\n"); |
| delete_cookie(cookie_iter); |
| continue; |
| } |
| |
| if((cookie_iter->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) |
| continue; |
| |
| |
| if(!res->size) { |
| res->cookies = heap_alloc(4*sizeof(*res->cookies)); |
| if(!res->cookies) |
| continue; |
| res->size = 4; |
| }else if(res->cnt == res->size) { |
| cookie_t **new_cookies = heap_realloc(res->cookies, res->size*2*sizeof(*res->cookies)); |
| if(!new_cookies) |
| continue; |
| res->cookies = new_cookies; |
| res->size *= 2; |
| } |
| |
| if(res->cnt) |
| res->string_len += 2; /* '; ' */ |
| res->cookies[res->cnt++] = cookie_iter; |
| |
| res->string_len += strlenW(cookie_iter->name); |
| if(*cookie_iter->data) |
| res->string_len += 1 /* = */ + strlenW(cookie_iter->data); |
| } |
| } |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| static void cookie_set_to_string(const cookie_set_t *cookie_set, WCHAR *str) |
| { |
| WCHAR *ptr = str; |
| unsigned i, len; |
| |
| for(i=0; i<cookie_set->cnt; i++) { |
| if(i) { |
| *ptr++ = ';'; |
| *ptr++ = ' '; |
| } |
| |
| len = strlenW(cookie_set->cookies[i]->name); |
| memcpy(ptr, cookie_set->cookies[i]->name, len*sizeof(WCHAR)); |
| ptr += len; |
| |
| if(*cookie_set->cookies[i]->data) { |
| *ptr++ = '='; |
| len = strlenW(cookie_set->cookies[i]->data); |
| memcpy(ptr, cookie_set->cookies[i]->data, len*sizeof(WCHAR)); |
| ptr += len; |
| } |
| } |
| |
| assert(ptr-str == cookie_set->string_len); |
| TRACE("%s\n", debugstr_wn(str, ptr-str)); |
| } |
| |
| DWORD get_cookie_header(const WCHAR *host, const WCHAR *path, WCHAR **ret) |
| { |
| cookie_set_t cookie_set = {0}; |
| DWORD res; |
| |
| static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' '}; |
| |
| EnterCriticalSection(&cookie_cs); |
| |
| res = get_cookie(host, path, INTERNET_COOKIE_HTTPONLY, &cookie_set); |
| if(res != ERROR_SUCCESS) { |
| LeaveCriticalSection(&cookie_cs); |
| return res; |
| } |
| |
| if(cookie_set.cnt) { |
| WCHAR *header, *ptr; |
| |
| ptr = header = heap_alloc(sizeof(cookieW) + (cookie_set.string_len + 3 /* crlf0 */) * sizeof(WCHAR)); |
| if(header) { |
| memcpy(ptr, cookieW, sizeof(cookieW)); |
| ptr += sizeof(cookieW)/sizeof(*cookieW); |
| |
| cookie_set_to_string(&cookie_set, ptr); |
| heap_free(cookie_set.cookies); |
| ptr += cookie_set.string_len; |
| |
| *ptr++ = '\r'; |
| *ptr++ = '\n'; |
| *ptr++ = 0; |
| |
| *ret = header; |
| }else { |
| res = ERROR_NOT_ENOUGH_MEMORY; |
| } |
| }else { |
| *ret = NULL; |
| } |
| |
| LeaveCriticalSection(&cookie_cs); |
| return res; |
| } |
| |
| /*********************************************************************** |
| * InternetGetCookieExW (WININET.@) |
| * |
| * Retrieve cookie from the specified url |
| * |
| * It should be noted that on windows the lpszCookieName parameter is "not implemented". |
| * So it won't be implemented here. |
| * |
| * RETURNS |
| * TRUE on success |
| * FALSE on failure |
| * |
| */ |
| BOOL WINAPI InternetGetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName, |
| LPWSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved) |
| { |
| WCHAR host[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH]; |
| cookie_set_t cookie_set = {0}; |
| DWORD res; |
| BOOL ret; |
| |
| TRACE("(%s, %s, %p, %p, %x, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), lpCookieData, lpdwSize, flags, reserved); |
| |
| if (flags) |
| FIXME("flags 0x%08x not supported\n", flags); |
| |
| if (!lpszUrl) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| host[0] = 0; |
| ret = COOKIE_crackUrlSimple(lpszUrl, host, sizeof(host)/sizeof(host[0]), path, sizeof(path)/sizeof(path[0])); |
| if (!ret || !host[0]) { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| EnterCriticalSection(&cookie_cs); |
| |
| res = get_cookie(host, path, flags, &cookie_set); |
| if(res != ERROR_SUCCESS) { |
| LeaveCriticalSection(&cookie_cs); |
| SetLastError(res); |
| return FALSE; |
| } |
| |
| if(cookie_set.cnt) { |
| if(!lpCookieData || cookie_set.string_len+1 > *lpdwSize) { |
| *lpdwSize = (cookie_set.string_len + 1) * sizeof(WCHAR); |
| TRACE("returning %u\n", *lpdwSize); |
| if(lpCookieData) { |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); |
| ret = FALSE; |
| } |
| }else { |
| *lpdwSize = cookie_set.string_len + 1; |
| cookie_set_to_string(&cookie_set, lpCookieData); |
| lpCookieData[cookie_set.string_len] = 0; |
| } |
| }else { |
| TRACE("no cookies found for %s\n", debugstr_w(host)); |
| SetLastError(ERROR_NO_MORE_ITEMS); |
| ret = FALSE; |
| } |
| |
| heap_free(cookie_set.cookies); |
| LeaveCriticalSection(&cookie_cs); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * InternetGetCookieW (WININET.@) |
| * |
| * Retrieve cookie for the specified URL. |
| */ |
| BOOL WINAPI InternetGetCookieW(const WCHAR *url, const WCHAR *name, WCHAR *data, DWORD *size) |
| { |
| TRACE("(%s, %s, %s, %p)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data), size); |
| |
| return InternetGetCookieExW(url, name, data, size, 0, NULL); |
| } |
| |
| /*********************************************************************** |
| * InternetGetCookieExA (WININET.@) |
| * |
| * Retrieve cookie from the specified url |
| * |
| * RETURNS |
| * TRUE on success |
| * FALSE on failure |
| * |
| */ |
| BOOL WINAPI InternetGetCookieExA(LPCSTR lpszUrl, LPCSTR lpszCookieName, |
| LPSTR lpCookieData, LPDWORD lpdwSize, DWORD flags, void *reserved) |
| { |
| WCHAR *url, *name; |
| DWORD len, size; |
| BOOL r; |
| |
| TRACE("(%s %s %p %p(%u) %x %p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName), |
| lpCookieData, lpdwSize, lpdwSize ? *lpdwSize : 0, flags, reserved); |
| |
| url = heap_strdupAtoW(lpszUrl); |
| name = heap_strdupAtoW(lpszCookieName); |
| |
| r = InternetGetCookieExW( url, name, NULL, &len, flags, reserved ); |
| if( r ) |
| { |
| WCHAR *szCookieData; |
| |
| szCookieData = heap_alloc(len * sizeof(WCHAR)); |
| if( !szCookieData ) |
| { |
| r = FALSE; |
| } |
| else |
| { |
| r = InternetGetCookieExW( url, name, szCookieData, &len, flags, reserved ); |
| |
| if(r) { |
| size = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, NULL, 0, NULL, NULL); |
| if(lpCookieData) { |
| if(*lpdwSize >= size) { |
| WideCharToMultiByte( CP_ACP, 0, szCookieData, len, lpCookieData, *lpdwSize, NULL, NULL); |
| }else { |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); |
| r = FALSE; |
| } |
| } |
| *lpdwSize = size; |
| } |
| |
| heap_free( szCookieData ); |
| } |
| } |
| heap_free( name ); |
| heap_free( url ); |
| return r; |
| } |
| |
| /*********************************************************************** |
| * InternetGetCookieA (WININET.@) |
| * |
| * See InternetGetCookieW. |
| */ |
| BOOL WINAPI InternetGetCookieA(const char *url, const char *name, char *data, DWORD *size) |
| { |
| TRACE("(%s, %s, %s, %p)\n", debugstr_a(url), debugstr_a(name), debugstr_a(data), size); |
| |
| return InternetGetCookieExA(url, name, data, size, 0, NULL); |
| } |
| |
| /*********************************************************************** |
| * IsDomainLegalCookieDomainW (WININET.@) |
| */ |
| BOOL WINAPI IsDomainLegalCookieDomainW( LPCWSTR s1, LPCWSTR s2 ) |
| { |
| DWORD s1_len, s2_len; |
| |
| FIXME("(%s, %s) semi-stub\n", debugstr_w(s1), debugstr_w(s2)); |
| |
| if (!s1 || !s2) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| if (s1[0] == '.' || !s1[0] || s2[0] == '.' || !s2[0]) |
| { |
| SetLastError(ERROR_INVALID_NAME); |
| return FALSE; |
| } |
| if(!strchrW(s1, '.') || !strchrW(s2, '.')) |
| return FALSE; |
| |
| s1_len = strlenW(s1); |
| s2_len = strlenW(s2); |
| if (s1_len > s2_len) |
| return FALSE; |
| |
| if (strncmpiW(s1, s2+s2_len-s1_len, s1_len) || (s2_len>s1_len && s2[s2_len-s1_len-1]!='.')) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| DWORD set_cookie(const WCHAR *domain, const WCHAR *path, const WCHAR *cookie_name, const WCHAR *cookie_data, DWORD flags) |
| { |
| cookie_container_t *container; |
| cookie_t *thisCookie; |
| LPWSTR data, value; |
| WCHAR *ptr; |
| FILETIME expiry, create; |
| BOOL expired = FALSE, update_persistent = FALSE; |
| DWORD cookie_flags = 0; |
| |
| TRACE("%s %s %s=%s %x\n", debugstr_w(domain), debugstr_w(path), debugstr_w(cookie_name), debugstr_w(cookie_data), flags); |
| |
| value = data = heap_strdupW(cookie_data); |
| if (!data) |
| { |
| ERR("could not allocate the cookie data buffer\n"); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| |
| memset(&expiry,0,sizeof(expiry)); |
| GetSystemTimeAsFileTime(&create); |
| |
| /* lots of information can be parsed out of the cookie value */ |
| |
| ptr = data; |
| for (;;) |
| { |
| static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0}; |
| static const WCHAR szPath[] = {'p','a','t','h','=',0}; |
| static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','=',0}; |
| static const WCHAR szSecure[] = {'s','e','c','u','r','e',0}; |
| static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y',0}; |
| static const WCHAR szVersion[] = {'v','e','r','s','i','o','n','=',0}; |
| |
| if (!(ptr = strchrW(ptr,';'))) break; |
| *ptr++ = 0; |
| |
| if (value != data) heap_free(value); |
| value = heap_alloc((ptr - data) * sizeof(WCHAR)); |
| if (value == NULL) |
| { |
| heap_free(data); |
| ERR("could not allocate the cookie value buffer\n"); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| strcpyW(value, data); |
| |
| while (*ptr == ' ') ptr++; /* whitespace */ |
| |
| if (strncmpiW(ptr, szDomain, 7) == 0) |
| { |
| WCHAR *end_ptr; |
| |
| ptr += sizeof(szDomain)/sizeof(szDomain[0])-1; |
| if(*ptr == '.') |
| ptr++; |
| end_ptr = strchrW(ptr, ';'); |
| if(end_ptr) |
| *end_ptr = 0; |
| |
| if(!IsDomainLegalCookieDomainW(ptr, domain)) |
| { |
| if(value != data) |
| heap_free(value); |
| heap_free(data); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| |
| if(end_ptr) |
| *end_ptr = ';'; |
| |
| domain = ptr; |
| TRACE("Parsing new domain %s\n",debugstr_w(domain)); |
| } |
| else if (strncmpiW(ptr, szPath, 5) == 0) |
| { |
| ptr+=strlenW(szPath); |
| path = ptr; |
| TRACE("Parsing new path %s\n",debugstr_w(path)); |
| } |
| else if (strncmpiW(ptr, szExpires, 8) == 0) |
| { |
| SYSTEMTIME st; |
| ptr+=strlenW(szExpires); |
| if (InternetTimeToSystemTimeW(ptr, &st, 0)) |
| { |
| SystemTimeToFileTime(&st, &expiry); |
| |
| if (CompareFileTime(&create,&expiry) > 0) |
| { |
| TRACE("Cookie already expired.\n"); |
| expired = TRUE; |
| } |
| } |
| } |
| else if (strncmpiW(ptr, szSecure, 6) == 0) |
| { |
| FIXME("secure not handled (%s)\n",debugstr_w(ptr)); |
| ptr += strlenW(szSecure); |
| } |
| else if (strncmpiW(ptr, szHttpOnly, 8) == 0) |
| { |
| if(!(flags & INTERNET_COOKIE_HTTPONLY)) { |
| WARN("HTTP only cookie added without INTERNET_COOKIE_HTTPONLY flag\n"); |
| heap_free(data); |
| if (value != data) heap_free(value); |
| SetLastError(ERROR_INVALID_OPERATION); |
| return COOKIE_STATE_REJECT; |
| } |
| |
| cookie_flags |= INTERNET_COOKIE_HTTPONLY; |
| ptr += strlenW(szHttpOnly); |
| } |
| else if (strncmpiW(ptr, szVersion, 8) == 0) |
| { |
| FIXME("version not handled (%s)\n",debugstr_w(ptr)); |
| ptr += strlenW(szVersion); |
| } |
| else if (*ptr) |
| { |
| FIXME("Unknown additional option %s\n",debugstr_w(ptr)); |
| break; |
| } |
| } |
| |
| EnterCriticalSection(&cookie_cs); |
| |
| load_persistent_cookie(domain, path); |
| |
| container = get_cookie_container(domain, path, !expired); |
| if(!container) { |
| heap_free(data); |
| if (value != data) heap_free(value); |
| LeaveCriticalSection(&cookie_cs); |
| return COOKIE_STATE_ACCEPT; |
| } |
| |
| if(!expiry.dwLowDateTime && !expiry.dwHighDateTime) |
| cookie_flags |= INTERNET_COOKIE_IS_SESSION; |
| else |
| update_persistent = TRUE; |
| |
| if ((thisCookie = find_cookie(container, cookie_name))) |
| { |
| if ((thisCookie->flags & INTERNET_COOKIE_HTTPONLY) && !(flags & INTERNET_COOKIE_HTTPONLY)) { |
| WARN("An attempt to override httponly cookie\n"); |
| SetLastError(ERROR_INVALID_OPERATION); |
| heap_free(data); |
| if (value != data) heap_free(value); |
| return COOKIE_STATE_REJECT; |
| } |
| |
| if (!(thisCookie->flags & INTERNET_COOKIE_IS_SESSION)) |
| update_persistent = TRUE; |
| delete_cookie(thisCookie); |
| } |
| |
| TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_w(cookie_name), |
| debugstr_w(value), debugstr_w(container->domain->domain),debugstr_w(container->path)); |
| |
| if (!expired) { |
| cookie_t *new_cookie; |
| |
| new_cookie = alloc_cookie(cookie_name, value, expiry, create, cookie_flags); |
| if(!new_cookie) { |
| heap_free(data); |
| if (value != data) heap_free(value); |
| LeaveCriticalSection(&cookie_cs); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| |
| add_cookie(container, new_cookie); |
| } |
| heap_free(data); |
| if (value != data) heap_free(value); |
| |
| if (!update_persistent || save_persistent_cookie(container)) |
| { |
| LeaveCriticalSection(&cookie_cs); |
| return COOKIE_STATE_ACCEPT; |
| } |
| LeaveCriticalSection(&cookie_cs); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| |
| /*********************************************************************** |
| * InternetSetCookieExW (WININET.@) |
| * |
| * Sets cookie for the specified url |
| */ |
| DWORD WINAPI InternetSetCookieExW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName, |
| LPCWSTR lpCookieData, DWORD flags, DWORD_PTR reserved) |
| { |
| BOOL ret; |
| WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH], path[INTERNET_MAX_PATH_LENGTH]; |
| |
| TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_w(lpszUrl), debugstr_w(lpszCookieName), |
| debugstr_w(lpCookieData), flags, reserved); |
| |
| if (flags & ~INTERNET_COOKIE_HTTPONLY) |
| FIXME("flags %x not supported\n", flags); |
| |
| if (!lpszUrl || !lpCookieData) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| |
| hostName[0] = 0; |
| ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0])); |
| if (!ret || !hostName[0]) return COOKIE_STATE_UNKNOWN; |
| |
| if (!lpszCookieName) |
| { |
| WCHAR *cookie, *data; |
| DWORD res; |
| |
| cookie = heap_strdupW(lpCookieData); |
| if (!cookie) |
| { |
| SetLastError(ERROR_OUTOFMEMORY); |
| return COOKIE_STATE_UNKNOWN; |
| } |
| |
| /* some apps (or is it us??) try to add a cookie with no cookie name, but |
| * the cookie data in the form of name[=data]. |
| */ |
| if (!(data = strchrW(cookie, '='))) data = cookie + strlenW(cookie); |
| else *data++ = 0; |
| |
| res = set_cookie(hostName, path, cookie, data, flags); |
| |
| heap_free(cookie); |
| return res; |
| } |
| return set_cookie(hostName, path, lpszCookieName, lpCookieData, flags); |
| } |
| |
| /*********************************************************************** |
| * InternetSetCookieW (WININET.@) |
| * |
| * Sets a cookie for the specified URL. |
| */ |
| BOOL WINAPI InternetSetCookieW(const WCHAR *url, const WCHAR *name, const WCHAR *data) |
| { |
| TRACE("(%s, %s, %s)\n", debugstr_w(url), debugstr_w(name), debugstr_w(data)); |
| |
| return InternetSetCookieExW(url, name, data, 0, 0) == COOKIE_STATE_ACCEPT; |
| } |
| |
| /*********************************************************************** |
| * InternetSetCookieA (WININET.@) |
| * |
| * Sets cookie for the specified url |
| * |
| * RETURNS |
| * TRUE on success |
| * FALSE on failure |
| * |
| */ |
| BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName, |
| LPCSTR lpCookieData) |
| { |
| LPWSTR data, url, name; |
| BOOL r; |
| |
| TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl), |
| debugstr_a(lpszCookieName), debugstr_a(lpCookieData)); |
| |
| url = heap_strdupAtoW(lpszUrl); |
| name = heap_strdupAtoW(lpszCookieName); |
| data = heap_strdupAtoW(lpCookieData); |
| |
| r = InternetSetCookieW( url, name, data ); |
| |
| heap_free( data ); |
| heap_free( name ); |
| heap_free( url ); |
| return r; |
| } |
| |
| /*********************************************************************** |
| * InternetSetCookieExA (WININET.@) |
| * |
| * See InternetSetCookieExW. |
| */ |
| DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData, |
| DWORD dwFlags, DWORD_PTR dwReserved) |
| { |
| WCHAR *data, *url, *name; |
| DWORD r; |
| |
| TRACE("(%s, %s, %s, %x, %lx)\n", debugstr_a(lpszURL), debugstr_a(lpszCookieName), |
| debugstr_a(lpszCookieData), dwFlags, dwReserved); |
| |
| url = heap_strdupAtoW(lpszURL); |
| name = heap_strdupAtoW(lpszCookieName); |
| data = heap_strdupAtoW(lpszCookieData); |
| |
| r = InternetSetCookieExW(url, name, data, dwFlags, dwReserved); |
| |
| heap_free( data ); |
| heap_free( name ); |
| heap_free( url ); |
| return r; |
| } |
| |
| /*********************************************************************** |
| * InternetClearAllPerSiteCookieDecisions (WININET.@) |
| * |
| * Clears all per-site decisions about cookies. |
| * |
| * RETURNS |
| * TRUE on success |
| * FALSE on failure |
| * |
| */ |
| BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID ) |
| { |
| FIXME("stub\n"); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * InternetEnumPerSiteCookieDecisionA (WININET.@) |
| * |
| * See InternetEnumPerSiteCookieDecisionW. |
| */ |
| BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, ULONG *pcSiteNameSize, |
| ULONG *pdwDecision, ULONG dwIndex ) |
| { |
| FIXME("(%s, %p, %p, 0x%08x) stub\n", |
| debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex); |
| return FALSE; |
| } |
| |
| /*********************************************************************** |
| * InternetEnumPerSiteCookieDecisionW (WININET.@) |
| * |
| * Enumerates all per-site decisions about cookies. |
| * |
| * RETURNS |
| * TRUE on success |
| * FALSE on failure |
| * |
| */ |
| BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, ULONG *pcSiteNameSize, |
| ULONG *pdwDecision, ULONG dwIndex ) |
| { |
| FIXME("(%s, %p, %p, 0x%08x) stub\n", |
| debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex); |
| return FALSE; |
| } |
| |
| /*********************************************************************** |
| * InternetGetPerSiteCookieDecisionA (WININET.@) |
| */ |
| BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, ULONG *pResult ) |
| { |
| FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult); |
| return FALSE; |
| } |
| |
| /*********************************************************************** |
| * InternetGetPerSiteCookieDecisionW (WININET.@) |
| */ |
| BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, ULONG *pResult ) |
| { |
| FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult); |
| return FALSE; |
| } |
| |
| /*********************************************************************** |
| * InternetSetPerSiteCookieDecisionA (WININET.@) |
| */ |
| BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision ) |
| { |
| FIXME("(%s, 0x%08x) stub\n", debugstr_a(pchHostName), dwDecision); |
| return FALSE; |
| } |
| |
| /*********************************************************************** |
| * InternetSetPerSiteCookieDecisionW (WININET.@) |
| */ |
| BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision ) |
| { |
| FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision); |
| return FALSE; |
| } |
| |
| void free_cookie(void) |
| { |
| DeleteCriticalSection(&cookie_cs); |
| } |