// Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "CookieManager.h" #include #include #include "errorcodes.h" #include "logging.h" #include "BrowserCookie.h" #include "HookProcessor.h" #include "messages.h" #include "StringUtilities.h" #define TICKS_PER_SECOND 10000000 #define UNIX_TIME_OFFSET_SECONDS 11644473600L namespace webdriver { struct CookieSendMessageInfo { HWND window_handle; unsigned int message; }; CookieManager::CookieManager(void) { this->window_handle_ = NULL; } CookieManager::~CookieManager(void) { } void CookieManager::Initialize(HWND window_handle) { LOG(TRACE) << "Entering CookieManager::Initialize"; this->window_handle_ = window_handle; } bool CookieManager::IsAdvancedCookiesApi() { FARPROC address = NULL; HMODULE wininet_handle = ::GetModuleHandle(L"wininet"); if (wininet_handle) { address = ::GetProcAddress(wininet_handle, "InternetGetCookieEx2"); } return address != NULL; } int CookieManager::SetCookie(const std::string& url, const BrowserCookie& cookie) { std::string full_data = url + "|" + cookie.ToString(); WPARAM set_flags = 0; if (cookie.is_httponly()) { set_flags = INTERNET_COOKIE_HTTPONLY; } HookSettings hook_settings; hook_settings.hook_procedure_name = "CookieWndProc"; hook_settings.hook_procedure_type = WH_CALLWNDPROC; hook_settings.window_handle = this->window_handle_; hook_settings.communication_type = OneWay; HookProcessor hook; if (!hook.CanSetWindowsHook(this->window_handle_)) { LOG(WARN) << "Cannot set cookie because driver and browser are not the " << "same bit-ness."; return EUNHANDLEDERROR; } hook.Initialize(hook_settings); hook.PushData(StringUtilities::ToWString(full_data)); ::SendMessage(this->window_handle_, WD_SET_COOKIE, set_flags, NULL); int status = HookProcessor::GetDataBufferSize(); if (status != 0) { LOG(WARN) << "Setting cookie encountered error " << status; return EINVALIDCOOKIEDOMAIN; } return WD_SUCCESS; } int CookieManager::GetCookies(const std::string& url, std::vector* all_cookies) { LOG(TRACE) << "Entering CookieManager::GetCookies"; std::wstring wide_url = StringUtilities::ToWString(url); CComPtr parsed_url; ::CreateUri(wide_url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &parsed_url); DWORD url_scheme = 0; parsed_url->GetScheme(&url_scheme); bool is_secure_url = URL_SCHEME_HTTPS == url_scheme; HookSettings hook_settings; hook_settings.hook_procedure_name = "CookieWndProc"; hook_settings.hook_procedure_type = WH_CALLWNDPROC; hook_settings.window_handle = this->window_handle_; hook_settings.communication_type = TwoWay; HookProcessor hook; if (!hook.CanSetWindowsHook(this->window_handle_)) { LOG(WARN) << "Cannot get cookies because driver and browser are not the " << "same bit-ness."; return EUNHANDLEDERROR; } hook.Initialize(hook_settings); bool supports_advanced_api = this->IsAdvancedCookiesApi(); if (supports_advanced_api) { // The version of WinINet installed supports the InternetGetCookieEx2 // API, which gets all cookies (session and persistent) at once. std::wstring raw_cookie_data = this->SendGetCookieMessage(wide_url, WD_GET_ALL_COOKIES, &hook); std::string all_cookies_list = StringUtilities::ToString(raw_cookie_data); std::map cookies; this->ParseCookieList(all_cookies_list, is_secure_url, &cookies); std::map::const_iterator cookie_iterator; for (cookie_iterator = cookies.begin(); cookie_iterator != cookies.end(); ++cookie_iterator) { all_cookies->push_back(cookie_iterator->second); } } else { // Get all cookies for the current URL visible to JavaScript. std::wstring scriptable_cookie_string = this->SendGetCookieMessage(wide_url, WD_GET_SCRIPTABLE_COOKIES, &hook); std::map scriptable_cookies; this->ParseCookieString(scriptable_cookie_string, &scriptable_cookies); // Get all cookies for the insecure version of the current URL, // which will include HttpOnly cookies. std::wstring insecure_cookie_string = this->SendGetCookieMessage(wide_url, WD_GET_HTTPONLY_COOKIES, &hook); std::map insecure_cookies; this->ParseCookieString(insecure_cookie_string, &insecure_cookies); // Get all cookies for the current secure URL. This will include // HttpOnly cookies. std::wstring secure_cookie_string = this->SendGetCookieMessage(wide_url, WD_GET_SECURE_COOKIES, &hook); std::map secure_cookies; this->ParseCookieString(secure_cookie_string, &secure_cookies); // Get all of the persistent cookie files in the cache for the // URL currently being browsed. std::wstring file_list = this->SendGetCookieMessage(wide_url, WD_GET_COOKIE_CACHE_FILES, &hook); std::vector files; StringUtilities::Split(file_list, L"|", &files); // Parse the persistent cookie files to produce a list of // cookies. std::map persistent_cookies; std::vector::const_iterator file_iterator; for (file_iterator = files.begin(); file_iterator != files.end(); ++file_iterator) { std::string cookie_file_contents = this->ReadCookieFile(*file_iterator); this->ParseCookieList(cookie_file_contents, is_secure_url, &persistent_cookies); } // Loop through the entire list of cookies, including HttpOnly and secure // cookies. If the cookie exists as a persistent cookie, use its data from // the cache. If the cookie is found in the list of cookies visible to // JavaScript, set the HttpOnly property of the cookie to false. If the // cookie is found in the list of cookies set on the insecure version of // the URL, set the Secure property of the cookie to false. std::map::const_iterator it = secure_cookies.begin(); for (; it != secure_cookies.end(); ++it) { BrowserCookie browser_cookie; if (persistent_cookies.find(it->first) != persistent_cookies.end()) { browser_cookie = persistent_cookies[it->first]; } else { browser_cookie.set_name(it->first); browser_cookie.set_value(it->second); browser_cookie.set_is_httponly(scriptable_cookies.find(it->first) == scriptable_cookies.end()); browser_cookie.set_is_secure(insecure_cookies.find(it->first) == insecure_cookies.end()); } all_cookies->push_back(browser_cookie); } } return WD_SUCCESS; } bool CookieManager::DeleteCookie(const std::string& url, const BrowserCookie& cookie) { std::wstring wide_url = StringUtilities::ToWString(url); CComPtr uri_pointer; ::CreateUri(wide_url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer); CComBSTR host_bstr; uri_pointer->GetHost(&host_bstr); std::wstring wide_domain = host_bstr; CComBSTR path_bstr; uri_pointer->GetPath(&path_bstr); std::wstring wide_path = path_bstr; std::string domain = StringUtilities::ToString(wide_domain); std::string path = StringUtilities::ToString(wide_path); // N.B., We can hard-code the value and expiration time, since // we are deleting the cookie. So the value will be "deleted", // and the expiration time will be 1000 milliseconds after the // zero date (or Thu 1 Jan 1970 00:00:01 GMT). BrowserCookie recursive_cookie = cookie.Copy(); recursive_cookie.set_domain(domain); recursive_cookie.set_path(path); recursive_cookie.set_value("deleted"); recursive_cookie.set_expiration_time(1000); return this->RecursivelyDeleteCookie(url, recursive_cookie); } bool CookieManager::RecursivelyDeleteCookie(const std::string& url, const BrowserCookie& cookie) { // TODO: Optimize this path from the recursive to only // call setting the cookie as often as needed. BrowserCookie recursive_cookie = cookie.Copy(); recursive_cookie.set_domain("." + cookie.domain()); return this->RecurseCookiePath(url, recursive_cookie); } bool CookieManager::RecurseCookiePath(const std::string& url, const BrowserCookie& cookie) { size_t number_of_characters = 0; size_t slash_index = cookie.path().find_last_of('/'); size_t final_index = cookie.path().size() - 1; if (slash_index == final_index) { number_of_characters = slash_index; } else { number_of_characters = slash_index + 1; } if (slash_index != std::string::npos) { BrowserCookie path_cookie = cookie.Copy(); path_cookie.set_path(cookie.path().substr(0, number_of_characters)); bool deleted = this->RecurseCookiePath(url, path_cookie); } return this->RecurseCookieDomain(url, cookie); } bool CookieManager::RecurseCookieDomain(const std::string& url, const BrowserCookie& cookie) { int status = this->SetCookie(url, cookie); size_t dot_index = cookie.domain().find_first_of('.'); if (dot_index == 0) { BrowserCookie first_dot_cookie = cookie.Copy(); first_dot_cookie.set_domain(cookie.domain().substr(1)); return this->RecurseCookieDomain(url, first_dot_cookie); } else if (dot_index != std::string::npos) { BrowserCookie no_dot_cookie = cookie.Copy(); no_dot_cookie.set_domain(cookie.domain().substr(dot_index)); return this->RecurseCookieDomain(url, no_dot_cookie); } BrowserCookie no_domain_cookie = cookie.Copy(); no_domain_cookie.set_domain(""); status = this->SetCookie(url, no_domain_cookie); return status == WD_SUCCESS; } std::string CookieManager::ReadCookieFile(const std::wstring& file_name) { LOG(TRACE) << "Entering CookieManager::ReadCookieFile"; HANDLE file_handle = ::CreateFile(file_name.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); // Read the cookie file. Hopefully, we will never have a 2GB cookie file. DWORD file_size_high = 0; DWORD file_size_low = ::GetFileSize(file_handle, &file_size_high); std::vector file_content(file_size_low + 1); DWORD bytes_read = 0; ::ReadFile(file_handle, &file_content[0], file_size_low, &bytes_read, NULL); ::CloseHandle(file_handle); // Null-terminate and convert to a string for easier manipulation. file_content[bytes_read - 1] = '\0'; std::string cookie_file_contents = &file_content[0]; return cookie_file_contents; } void CookieManager::ParseCookieList(const std::string& cookie_file_contents, const bool include_secure_cookies, std::map* cookies) { LOG(TRACE) << "Entering CookieManager::ParseCookieList"; // Each cookie in the file is a record structure separated by // a line containing a single asterisk ('*'). Split the file // content on this delimiter, and parse each record. std::vector persistent_cookie_strings; StringUtilities::Split(cookie_file_contents, "\n*\n", &persistent_cookie_strings); std::vector::const_iterator cookie_string_iterator; for (cookie_string_iterator = persistent_cookie_strings.begin(); cookie_string_iterator != persistent_cookie_strings.end(); ++cookie_string_iterator) { BrowserCookie persistent_cookie = this->ParseSingleCookie(*cookie_string_iterator); if (include_secure_cookies || !persistent_cookie.is_secure()) { // Omit the cookie if it's 'secure' flag is set and we are *not* // browsing using SSL. cookies->insert( std::pair(persistent_cookie.name(), persistent_cookie)); } } } BrowserCookie CookieManager::ParseSingleCookie(const std::string& cookie) { LOG(TRACE) << "Entering CookieManager::ParsePersistentCookieInfo"; // Cookies represented by a structured string record type. // This structure is modeled after how some versions of IE // stored perisitent cookeis as files on disk. Each cookie // is represented by 8 lines in the file separated by line // feed (0xA) characters, with the following format: // // cookie_name // cookie_value // cookie.domain.value/cookie/path/value/ // // // // // // // Read each of these lines and set the appropriate values // in the resulting cookie object. std::vector cookie_parts; StringUtilities::Split(cookie, "\n", &cookie_parts); BrowserCookie cookie_to_return; cookie_to_return.set_name(cookie_parts[0]); cookie_to_return.set_value(cookie_parts[1]); size_t position = cookie_parts[2].find_first_of("/"); cookie_to_return.set_domain(cookie_parts[2].substr(0, position)); cookie_to_return.set_path(cookie_parts[2].substr(position)); int flags = atoi(cookie_parts[3].c_str()); cookie_to_return.set_is_secure(INTERNET_COOKIE_IS_SECURE == (INTERNET_COOKIE_IS_SECURE & flags)); cookie_to_return.set_is_httponly(INTERNET_COOKIE_HTTPONLY == (INTERNET_COOKIE_HTTPONLY & flags)); if (cookie_parts[4].size() > 0 && cookie_parts[5].size() > 0) { unsigned long expiry_time_low = strtoul(cookie_parts[4].c_str(), NULL, 10); unsigned long expiry_time_high = strtoul(cookie_parts[5].c_str(), NULL, 10); unsigned long long expiration_time = (expiry_time_high * static_cast(pow(2.0, 32))) + expiry_time_low; // Cookie expiration time is stored in the file as the number // of 100-nanosecond ticks since 1 January 1601 12:00:00 AM GMT. // We need the number of seconds since 1 January 1970 12:00:00 AM GMT. // This is the conversion. unsigned long cookie_expiration_time = static_cast((expiration_time / TICKS_PER_SECOND) - UNIX_TIME_OFFSET_SECONDS); cookie_to_return.set_expiration_time(cookie_expiration_time); } return cookie_to_return; } void CookieManager::ParseCookieString(const std::wstring& cookie_string, std::map* cookies) { LOG(TRACE) << "Entering CookieManager::ParseCookieString"; std::wstring cookie_string_copy = cookie_string; while (cookie_string_copy.size() > 0) { size_t cookie_delimiter_pos = cookie_string_copy.find(L"; "); std::wstring cookie = cookie_string_copy.substr(0, cookie_delimiter_pos); if (cookie_delimiter_pos == std::wstring::npos) { cookie_string_copy = L""; } else { cookie_string_copy = cookie_string_copy.substr(cookie_delimiter_pos + 2); } size_t cookie_separator_pos(cookie.find_first_of(L"=")); std::string cookie_name(StringUtilities::ToString(cookie.substr(0, cookie_separator_pos))); std::string cookie_value(StringUtilities::ToString(cookie.substr(cookie_separator_pos + 1))); cookies->insert(std::pair(cookie_name, cookie_value)); } } std::wstring CookieManager::SendGetCookieMessage(const std::wstring& url, const unsigned int message, HookProcessor* hook) { LOG(TRACE) << "Entering CookieManager::SendGetCookieMessage"; hook->PushData(url); // Since the named pipe server has to wait for the named pipe client // injected into the browser to connect to it before reading the data, // and since SendMessage is synchronous, we need to send the message // from a different thread to avoid a deadlock. CookieSendMessageInfo info; info.window_handle = this->window_handle_; info.message = message; unsigned int thread_id = 0; HANDLE thread_handle = reinterpret_cast(_beginthreadex(NULL, 0, &CookieManager::ThreadProc, reinterpret_cast(&info), 0, &thread_id)); if (thread_handle != NULL) { ::CloseHandle(thread_handle); } else { LOGERR(DEBUG) << "Unable to create thread"; } std::vector buffer(0); int bytes = hook->PullData(&buffer); std::wstring cookies = reinterpret_cast(&buffer[0]); return cookies; } unsigned int WINAPI CookieManager::ThreadProc(LPVOID lpParameter) { LOG(TRACE) << "Entering CookieManager::ThreadProc"; CookieSendMessageInfo* info = reinterpret_cast(lpParameter); DWORD process_id = ::GetCurrentProcessId(); LRESULT result = ::SendMessage(info->window_handle, info->message, process_id, NULL); return 0; } } // namespace webdriver #ifdef __cplusplus extern "C" { #endif // In order to run the IE driver against versions of IE that do not include // a version of WinINet.dll that supports the InternetGetCookiesEx2 API, // we must access the API in a way that does not import it into our DLL. // To that end, we duplicate the INTERNET_COOKIE2 structure here, and will // call the API (if it exists) via GetModuleHandle and GetProcAddress. typedef struct { PWSTR pwszName; PWSTR pwszValue; PWSTR pwszDomain; PWSTR pwszPath; DWORD dwFlags; FILETIME ftExpires; BOOL fExpiresSet; } INTERNETCOOKIE2; typedef void* (__stdcall *InternetFreeCookiesProc)(INTERNETCOOKIE2*, DWORD); typedef DWORD(__stdcall *InternetGetCookieEx2Proc)(PCWSTR, PCWSTR, DWORD, INTERNETCOOKIE2**, PDWORD); LRESULT CALLBACK CookieWndProc(int nCode, WPARAM wParam, LPARAM lParam) { CWPSTRUCT* call_window_proc_struct = reinterpret_cast(lParam); if (WM_COPYDATA == call_window_proc_struct->message) { COPYDATASTRUCT* data = reinterpret_cast(call_window_proc_struct->lParam); webdriver::HookProcessor::CopyDataToBuffer(data->cbData, data->lpData); } else if (WD_GET_ALL_COOKIES == call_window_proc_struct->message) { std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer(); int driver_process_id = static_cast(call_window_proc_struct->wParam); CComPtr uri_pointer; HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer); DWORD scheme = 0; uri_pointer->GetScheme(&scheme); CComBSTR scheme_bstr; uri_pointer->GetSchemeName(&scheme_bstr); CComBSTR host_bstr; uri_pointer->GetHost(&host_bstr); CComBSTR path_bstr; uri_pointer->GetPath(&path_bstr); std::wstring parsed_uri = scheme_bstr; parsed_uri.append(L"://"); parsed_uri.append(host_bstr); parsed_uri.append(path_bstr); InternetGetCookieEx2Proc get_cookie_proc = NULL; InternetFreeCookiesProc free_cookies_proc = NULL; HMODULE wininet_handle = ::GetModuleHandle(L"wininet"); if (wininet_handle) { get_cookie_proc = reinterpret_cast(::GetProcAddress(wininet_handle, "InternetGetCookieEx2")); free_cookies_proc = reinterpret_cast(::GetProcAddress(wininet_handle, "InternetFreeCookies")); } DWORD cookie_count = 0; INTERNETCOOKIE2* cookie_pointer = NULL; DWORD success = 1; if (get_cookie_proc) { success = get_cookie_proc(parsed_uri.c_str(), NULL, INTERNET_COOKIE_NON_SCRIPT, &cookie_pointer, &cookie_count); } if (success == 0) { // Mimic the format of the old persistent cookie files for ease of // transmission back to the driver and parsing. std::wstring all_cookies = L""; for (DWORD cookie_index = 0; cookie_index < cookie_count; ++cookie_index) { if (all_cookies.size() > 0) { all_cookies.append(L"\n*\n"); } INTERNETCOOKIE2* current_cookie = cookie_pointer + cookie_index; std::wstring cookie_name = L""; if (current_cookie->pwszName) { // Note that the spec appears to allow "nameless" cookies, // which clients like Selenium may not support. cookie_name = current_cookie->pwszName; } std::wstring cookie_value = L""; if (current_cookie->pwszValue) { cookie_value = current_cookie->pwszValue; } // TODO: The spec does not allow a cookie with an empty name // and value. It's unclear what the driver could do in this // case, but we should probably handle it somehow in the off // chance it ever comes up. std::wstring cookie_domain = L""; if (current_cookie->pwszDomain) { cookie_domain = current_cookie->pwszDomain; } std::wstring cookie_path = L""; if (current_cookie->pwszPath) { cookie_path = current_cookie->pwszPath; } DWORD flags = current_cookie->dwFlags; FILETIME expires = current_cookie->ftExpires; all_cookies.append(cookie_name).append(L"\n"); all_cookies.append(cookie_value).append(L"\n"); all_cookies.append(cookie_domain).append(L"/").append(cookie_path).append(L"\n"); all_cookies.append(std::to_wstring(flags)).append(L"\n"); // If the expiration time is set, add it to the string for the cookie. // If not, append empty fields to the record so subsequent parsing // of the string will still work. if (current_cookie->fExpiresSet) { all_cookies.append(std::to_wstring(expires.dwLowDateTime)).append(L"\n"); all_cookies.append(std::to_wstring(expires.dwHighDateTime)).append(L"\n"); } else { all_cookies.append(L"\n\n"); } } free_cookies_proc(cookie_pointer, cookie_count); webdriver::HookProcessor::CopyWStringToBuffer(all_cookies); } else { webdriver::HookProcessor::SetDataBufferSize(sizeof(wchar_t)); } webdriver::HookProcessor::WriteBufferToPipe(driver_process_id); } else if (WD_GET_HTTPONLY_COOKIES == call_window_proc_struct->message || WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message || WD_GET_SECURE_COOKIES == call_window_proc_struct->message) { std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer(); int driver_process_id = static_cast(call_window_proc_struct->wParam); DWORD get_cookie_flags = 0; if (WD_GET_HTTPONLY_COOKIES == call_window_proc_struct->message || WD_GET_SECURE_COOKIES == call_window_proc_struct->message) { get_cookie_flags = INTERNET_COOKIE_HTTPONLY; } CComPtr uri_pointer; HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer); DWORD scheme = 0; uri_pointer->GetScheme(&scheme); CComBSTR scheme_bstr; uri_pointer->GetSchemeName(&scheme_bstr); CComBSTR host_bstr; uri_pointer->GetHost(&host_bstr); CComBSTR path_bstr; uri_pointer->GetPath(&path_bstr); // Get only the cookies for the base URL, omitting port, if there is one. // N.B., we only return cookies secure cookies when browsing a site using // SSL. The browser won't see cookies with the 'secure' flag for sites // visited using plain http. std::wstring parsed_uri = L"http"; if ((WD_GET_SECURE_COOKIES == call_window_proc_struct->message || WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message) && URL_SCHEME_HTTPS == scheme) { parsed_uri.append(L"s"); } parsed_uri.append(L"://"); parsed_uri.append(host_bstr); parsed_uri.append(path_bstr); // Call InternetGetCookieEx once to get the size of the buffer needed, // then call again with the appropriately sized buffer allocated. DWORD buffer_size = 0; BOOL success = ::InternetGetCookieEx(parsed_uri.c_str(), NULL, NULL, &buffer_size, get_cookie_flags, NULL); if (success) { webdriver::HookProcessor::SetDataBufferSize(buffer_size); ::InternetGetCookieEx(parsed_uri.c_str(), NULL, reinterpret_cast(webdriver::HookProcessor::GetDataBufferAddress()), &buffer_size, get_cookie_flags, NULL); webdriver::HookProcessor::WriteBufferToPipe(driver_process_id); } else { if (ERROR_NO_MORE_ITEMS == ::GetLastError()) { webdriver::HookProcessor::SetDataBufferSize(sizeof(wchar_t)); webdriver::HookProcessor::WriteBufferToPipe(driver_process_id); } } } else if (WD_GET_COOKIE_CACHE_FILES == call_window_proc_struct->message) { int driver_process_id = static_cast(call_window_proc_struct->wParam); std::wstring file_list = L""; std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer(); // We need to remove the port to find the entry in the cache. CComPtr uri_pointer; HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer); CComBSTR host_bstr; uri_pointer->GetHost(&host_bstr); CComBSTR path_bstr; uri_pointer->GetPath(&path_bstr); std::wstring parsed_uri = host_bstr; parsed_uri.append(path_bstr); // A 2048-byte buffer should be large enough to handle cookie // cache entries in all but the most extreme cases. HANDLE cache_enum_handle = NULL; DWORD entry_size = 2048; LPINTERNET_CACHE_ENTRY_INFO entry = NULL; std::vector entry_buffer(entry_size); entry = reinterpret_cast(&entry_buffer[0]); cache_enum_handle = ::FindFirstUrlCacheEntry(L"cookie:", entry, &entry_size); if (cache_enum_handle == NULL && ERROR_INSUFFICIENT_BUFFER == ::GetLastError()) { entry_buffer.resize(entry_size); entry = reinterpret_cast(&entry_buffer[0]); cache_enum_handle = ::FindFirstUrlCacheEntry(L"cookie:", entry, &entry_size); } while (cache_enum_handle != NULL) { if (COOKIE_CACHE_ENTRY == (entry->CacheEntryType & COOKIE_CACHE_ENTRY)) { std::wstring name = entry->lpszSourceUrlName; size_t name_separator_pos(name.find_first_of(L"@")); std::wstring domain = name.substr(name_separator_pos + 1); if (parsed_uri.find(domain) != std::wstring::npos) { if (file_list.size() > 0) { file_list.append(L"|"); } file_list.append(entry->lpszLocalFileName); } } BOOL success = ::FindNextUrlCacheEntry(cache_enum_handle, entry, &entry_size); if (!success) { DWORD error = ::GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == error) { entry_buffer.resize(entry_size); BOOL other_success = ::FindNextUrlCacheEntry(cache_enum_handle, entry, &entry_size); } else if (ERROR_NO_MORE_ITEMS == error) { ::FindCloseUrlCache(cache_enum_handle); cache_enum_handle = NULL; } } } webdriver::HookProcessor::CopyWStringToBuffer(file_list); webdriver::HookProcessor::WriteBufferToPipe(driver_process_id); } else if (WD_SET_COOKIE == call_window_proc_struct->message) { DWORD set_cookie_flags = static_cast(call_window_proc_struct->wParam); std::wstring cookie_data = webdriver::HookProcessor::CopyWStringFromBuffer(); size_t url_separator_pos = cookie_data.find_first_of(L"|"); std::wstring url = cookie_data.substr(0, url_separator_pos); std::wstring cookie = cookie_data.substr(url_separator_pos + 1); CComPtr uri_pointer; HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer); CComBSTR scheme_bstr; uri_pointer->GetSchemeName(&scheme_bstr); CComBSTR host_bstr; uri_pointer->GetHost(&host_bstr); std::wstring parsed_uri = scheme_bstr; parsed_uri.append(L"://"); parsed_uri.append(host_bstr); // Leverage the shared data buffer size to return the error code // back to the driver, if necessary. DWORD cookie_set = ::InternetSetCookieEx(parsed_uri.c_str(), NULL, cookie.c_str(), set_cookie_flags, NULL); if (cookie_set) { webdriver::HookProcessor::SetDataBufferSize(0); } else { DWORD error = ::GetLastError(); webdriver::HookProcessor::SetDataBufferSize(error); } } return ::CallNextHookEx(NULL, nCode, wParam, lParam); } #ifdef __cplusplus } #endif