Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/iedriver/CookieManager.cpp
2867 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
17
#include "CookieManager.h"
18
19
#include <UrlMon.h>
20
#include <wininet.h>
21
22
#include "errorcodes.h"
23
#include "logging.h"
24
25
#include "BrowserCookie.h"
26
#include "HookProcessor.h"
27
#include "messages.h"
28
#include "StringUtilities.h"
29
30
#define TICKS_PER_SECOND 10000000
31
#define UNIX_TIME_OFFSET_SECONDS 11644473600L
32
33
namespace webdriver {
34
35
struct CookieSendMessageInfo {
36
HWND window_handle;
37
unsigned int message;
38
};
39
40
CookieManager::CookieManager(void) {
41
this->window_handle_ = NULL;
42
}
43
44
CookieManager::~CookieManager(void) {
45
}
46
47
void CookieManager::Initialize(HWND window_handle) {
48
LOG(TRACE) << "Entering CookieManager::Initialize";
49
this->window_handle_ = window_handle;
50
}
51
52
bool CookieManager::IsAdvancedCookiesApi() {
53
FARPROC address = NULL;
54
HMODULE wininet_handle = ::GetModuleHandle(L"wininet");
55
if (wininet_handle) {
56
address = ::GetProcAddress(wininet_handle, "InternetGetCookieEx2");
57
}
58
return address != NULL;
59
}
60
61
int CookieManager::SetCookie(const std::string& url,
62
const BrowserCookie& cookie) {
63
std::string full_data = url + "|" + cookie.ToString();
64
WPARAM set_flags = 0;
65
if (cookie.is_httponly()) {
66
set_flags = INTERNET_COOKIE_HTTPONLY;
67
}
68
69
HookSettings hook_settings;
70
hook_settings.hook_procedure_name = "CookieWndProc";
71
hook_settings.hook_procedure_type = WH_CALLWNDPROC;
72
hook_settings.window_handle = this->window_handle_;
73
hook_settings.communication_type = OneWay;
74
75
HookProcessor hook;
76
if (!hook.CanSetWindowsHook(this->window_handle_)) {
77
LOG(WARN) << "Cannot set cookie because driver and browser are not the "
78
<< "same bit-ness.";
79
return EUNHANDLEDERROR;
80
}
81
hook.Initialize(hook_settings);
82
hook.PushData(StringUtilities::ToWString(full_data));
83
::SendMessage(this->window_handle_, WD_SET_COOKIE, set_flags, NULL);
84
int status = HookProcessor::GetDataBufferSize();
85
if (status != 0) {
86
LOG(WARN) << "Setting cookie encountered error " << status;
87
return EINVALIDCOOKIEDOMAIN;
88
}
89
return WD_SUCCESS;
90
}
91
92
int CookieManager::GetCookies(const std::string& url,
93
std::vector<BrowserCookie>* all_cookies) {
94
LOG(TRACE) << "Entering CookieManager::GetCookies";
95
std::wstring wide_url = StringUtilities::ToWString(url);
96
CComPtr<IUri> parsed_url;
97
::CreateUri(wide_url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &parsed_url);
98
DWORD url_scheme = 0;
99
parsed_url->GetScheme(&url_scheme);
100
bool is_secure_url = URL_SCHEME_HTTPS == url_scheme;
101
102
HookSettings hook_settings;
103
hook_settings.hook_procedure_name = "CookieWndProc";
104
hook_settings.hook_procedure_type = WH_CALLWNDPROC;
105
hook_settings.window_handle = this->window_handle_;
106
hook_settings.communication_type = TwoWay;
107
108
HookProcessor hook;
109
if (!hook.CanSetWindowsHook(this->window_handle_)) {
110
LOG(WARN) << "Cannot get cookies because driver and browser are not the "
111
<< "same bit-ness.";
112
return EUNHANDLEDERROR;
113
}
114
hook.Initialize(hook_settings);
115
116
bool supports_advanced_api = this->IsAdvancedCookiesApi();
117
if (supports_advanced_api) {
118
// The version of WinINet installed supports the InternetGetCookieEx2
119
// API, which gets all cookies (session and persistent) at once.
120
std::wstring raw_cookie_data =
121
this->SendGetCookieMessage(wide_url,
122
WD_GET_ALL_COOKIES,
123
&hook);
124
std::string all_cookies_list = StringUtilities::ToString(raw_cookie_data);
125
std::map<std::string, BrowserCookie> cookies;
126
this->ParseCookieList(all_cookies_list,
127
is_secure_url,
128
&cookies);
129
std::map<std::string, BrowserCookie>::const_iterator cookie_iterator;
130
for (cookie_iterator = cookies.begin();
131
cookie_iterator != cookies.end();
132
++cookie_iterator) {
133
all_cookies->push_back(cookie_iterator->second);
134
}
135
} else {
136
// Get all cookies for the current URL visible to JavaScript.
137
std::wstring scriptable_cookie_string =
138
this->SendGetCookieMessage(wide_url,
139
WD_GET_SCRIPTABLE_COOKIES,
140
&hook);
141
std::map<std::string, std::string> scriptable_cookies;
142
this->ParseCookieString(scriptable_cookie_string, &scriptable_cookies);
143
144
// Get all cookies for the insecure version of the current URL,
145
// which will include HttpOnly cookies.
146
std::wstring insecure_cookie_string =
147
this->SendGetCookieMessage(wide_url,
148
WD_GET_HTTPONLY_COOKIES,
149
&hook);
150
std::map<std::string, std::string> insecure_cookies;
151
this->ParseCookieString(insecure_cookie_string, &insecure_cookies);
152
153
// Get all cookies for the current secure URL. This will include
154
// HttpOnly cookies.
155
std::wstring secure_cookie_string =
156
this->SendGetCookieMessage(wide_url,
157
WD_GET_SECURE_COOKIES,
158
&hook);
159
std::map<std::string, std::string> secure_cookies;
160
this->ParseCookieString(secure_cookie_string, &secure_cookies);
161
162
// Get all of the persistent cookie files in the cache for the
163
// URL currently being browsed.
164
std::wstring file_list =
165
this->SendGetCookieMessage(wide_url,
166
WD_GET_COOKIE_CACHE_FILES,
167
&hook);
168
std::vector<std::wstring> files;
169
StringUtilities::Split(file_list, L"|", &files);
170
171
// Parse the persistent cookie files to produce a list of
172
// cookies.
173
std::map<std::string, BrowserCookie> persistent_cookies;
174
std::vector<std::wstring>::const_iterator file_iterator;
175
for (file_iterator = files.begin();
176
file_iterator != files.end();
177
++file_iterator) {
178
std::string cookie_file_contents = this->ReadCookieFile(*file_iterator);
179
this->ParseCookieList(cookie_file_contents,
180
is_secure_url,
181
&persistent_cookies);
182
}
183
184
// Loop through the entire list of cookies, including HttpOnly and secure
185
// cookies. If the cookie exists as a persistent cookie, use its data from
186
// the cache. If the cookie is found in the list of cookies visible to
187
// JavaScript, set the HttpOnly property of the cookie to false. If the
188
// cookie is found in the list of cookies set on the insecure version of
189
// the URL, set the Secure property of the cookie to false.
190
std::map<std::string, std::string>::const_iterator it = secure_cookies.begin();
191
for (; it != secure_cookies.end(); ++it) {
192
BrowserCookie browser_cookie;
193
if (persistent_cookies.find(it->first) != persistent_cookies.end()) {
194
browser_cookie = persistent_cookies[it->first];
195
} else {
196
browser_cookie.set_name(it->first);
197
browser_cookie.set_value(it->second);
198
browser_cookie.set_is_httponly(scriptable_cookies.find(it->first) == scriptable_cookies.end());
199
browser_cookie.set_is_secure(insecure_cookies.find(it->first) == insecure_cookies.end());
200
}
201
all_cookies->push_back(browser_cookie);
202
}
203
}
204
return WD_SUCCESS;
205
}
206
207
bool CookieManager::DeleteCookie(const std::string& url,
208
const BrowserCookie& cookie) {
209
std::wstring wide_url = StringUtilities::ToWString(url);
210
CComPtr<IUri> uri_pointer;
211
::CreateUri(wide_url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer);
212
213
CComBSTR host_bstr;
214
uri_pointer->GetHost(&host_bstr);
215
std::wstring wide_domain = host_bstr;
216
217
CComBSTR path_bstr;
218
uri_pointer->GetPath(&path_bstr);
219
std::wstring wide_path = path_bstr;
220
221
std::string domain = StringUtilities::ToString(wide_domain);
222
std::string path = StringUtilities::ToString(wide_path);
223
224
// N.B., We can hard-code the value and expiration time, since
225
// we are deleting the cookie. So the value will be "deleted",
226
// and the expiration time will be 1000 milliseconds after the
227
// zero date (or Thu 1 Jan 1970 00:00:01 GMT).
228
BrowserCookie recursive_cookie = cookie.Copy();
229
recursive_cookie.set_domain(domain);
230
recursive_cookie.set_path(path);
231
recursive_cookie.set_value("deleted");
232
recursive_cookie.set_expiration_time(1000);
233
return this->RecursivelyDeleteCookie(url, recursive_cookie);
234
}
235
236
bool CookieManager::RecursivelyDeleteCookie(const std::string& url,
237
const BrowserCookie& cookie) {
238
// TODO: Optimize this path from the recursive to only
239
// call setting the cookie as often as needed.
240
BrowserCookie recursive_cookie = cookie.Copy();
241
recursive_cookie.set_domain("." + cookie.domain());
242
return this->RecurseCookiePath(url, recursive_cookie);
243
}
244
245
bool CookieManager::RecurseCookiePath(const std::string& url,
246
const BrowserCookie& cookie) {
247
size_t number_of_characters = 0;
248
size_t slash_index = cookie.path().find_last_of('/');
249
size_t final_index = cookie.path().size() - 1;
250
if (slash_index == final_index) {
251
number_of_characters = slash_index;
252
}
253
else {
254
number_of_characters = slash_index + 1;
255
}
256
257
if (slash_index != std::string::npos) {
258
BrowserCookie path_cookie = cookie.Copy();
259
path_cookie.set_path(cookie.path().substr(0, number_of_characters));
260
bool deleted = this->RecurseCookiePath(url, path_cookie);
261
}
262
return this->RecurseCookieDomain(url, cookie);
263
}
264
265
bool CookieManager::RecurseCookieDomain(const std::string& url,
266
const BrowserCookie& cookie) {
267
int status = this->SetCookie(url, cookie);
268
269
size_t dot_index = cookie.domain().find_first_of('.');
270
if (dot_index == 0) {
271
BrowserCookie first_dot_cookie = cookie.Copy();
272
first_dot_cookie.set_domain(cookie.domain().substr(1));
273
return this->RecurseCookieDomain(url, first_dot_cookie);
274
} else if (dot_index != std::string::npos) {
275
BrowserCookie no_dot_cookie = cookie.Copy();
276
no_dot_cookie.set_domain(cookie.domain().substr(dot_index));
277
return this->RecurseCookieDomain(url, no_dot_cookie);
278
}
279
280
BrowserCookie no_domain_cookie = cookie.Copy();
281
no_domain_cookie.set_domain("");
282
status = this->SetCookie(url, no_domain_cookie);
283
return status == WD_SUCCESS;
284
}
285
286
std::string CookieManager::ReadCookieFile(const std::wstring& file_name) {
287
LOG(TRACE) << "Entering CookieManager::ReadCookieFile";
288
HANDLE file_handle = ::CreateFile(file_name.c_str(),
289
GENERIC_READ,
290
FILE_SHARE_READ | FILE_SHARE_WRITE,
291
NULL,
292
OPEN_EXISTING,
293
0,
294
NULL);
295
// Read the cookie file. Hopefully, we will never have a 2GB cookie file.
296
DWORD file_size_high = 0;
297
DWORD file_size_low = ::GetFileSize(file_handle, &file_size_high);
298
std::vector<char> file_content(file_size_low + 1);
299
DWORD bytes_read = 0;
300
::ReadFile(file_handle, &file_content[0], file_size_low, &bytes_read, NULL);
301
::CloseHandle(file_handle);
302
303
// Null-terminate and convert to a string for easier manipulation.
304
file_content[bytes_read - 1] = '\0';
305
std::string cookie_file_contents = &file_content[0];
306
return cookie_file_contents;
307
}
308
309
void CookieManager::ParseCookieList(const std::string& cookie_file_contents,
310
const bool include_secure_cookies,
311
std::map<std::string, BrowserCookie>* cookies) {
312
LOG(TRACE) << "Entering CookieManager::ParseCookieList";
313
314
// Each cookie in the file is a record structure separated by
315
// a line containing a single asterisk ('*'). Split the file
316
// content on this delimiter, and parse each record.
317
std::vector<std::string> persistent_cookie_strings;
318
StringUtilities::Split(cookie_file_contents,
319
"\n*\n",
320
&persistent_cookie_strings);
321
std::vector<std::string>::const_iterator cookie_string_iterator;
322
for (cookie_string_iterator = persistent_cookie_strings.begin();
323
cookie_string_iterator != persistent_cookie_strings.end();
324
++cookie_string_iterator) {
325
BrowserCookie persistent_cookie =
326
this->ParseSingleCookie(*cookie_string_iterator);
327
if (include_secure_cookies || !persistent_cookie.is_secure()) {
328
// Omit the cookie if it's 'secure' flag is set and we are *not*
329
// browsing using SSL.
330
cookies->insert(
331
std::pair<std::string, BrowserCookie>(persistent_cookie.name(),
332
persistent_cookie));
333
}
334
}
335
}
336
337
BrowserCookie CookieManager::ParseSingleCookie(const std::string& cookie) {
338
LOG(TRACE) << "Entering CookieManager::ParsePersistentCookieInfo";
339
// Cookies represented by a structured string record type.
340
// This structure is modeled after how some versions of IE
341
// stored perisitent cookeis as files on disk. Each cookie
342
// is represented by 8 lines in the file separated by line
343
// feed (0xA) characters, with the following format:
344
//
345
// cookie_name
346
// cookie_value
347
// cookie.domain.value/cookie/path/value/
348
// <integer representing cookie flags>
349
// <unsigned long representing the low 32 bits of expiration time>
350
// <unsigned long representing the high 32 bits of expiration time>
351
// <unsigned long representing the low 32 bits of last-modified time>
352
// <unsigned long representing the high 32 bits of last-modified time>
353
//
354
// Read each of these lines and set the appropriate values
355
// in the resulting cookie object.
356
std::vector<std::string> cookie_parts;
357
StringUtilities::Split(cookie, "\n", &cookie_parts);
358
359
BrowserCookie cookie_to_return;
360
cookie_to_return.set_name(cookie_parts[0]);
361
cookie_to_return.set_value(cookie_parts[1]);
362
363
size_t position = cookie_parts[2].find_first_of("/");
364
cookie_to_return.set_domain(cookie_parts[2].substr(0, position));
365
cookie_to_return.set_path(cookie_parts[2].substr(position));
366
367
int flags = atoi(cookie_parts[3].c_str());
368
cookie_to_return.set_is_secure(INTERNET_COOKIE_IS_SECURE == (INTERNET_COOKIE_IS_SECURE & flags));
369
cookie_to_return.set_is_httponly(INTERNET_COOKIE_HTTPONLY == (INTERNET_COOKIE_HTTPONLY & flags));
370
371
if (cookie_parts[4].size() > 0 && cookie_parts[5].size() > 0) {
372
unsigned long expiry_time_low = strtoul(cookie_parts[4].c_str(), NULL, 10);
373
unsigned long expiry_time_high = strtoul(cookie_parts[5].c_str(), NULL, 10);
374
unsigned long long expiration_time = (expiry_time_high * static_cast<long long>(pow(2.0, 32))) + expiry_time_low;
375
376
// Cookie expiration time is stored in the file as the number
377
// of 100-nanosecond ticks since 1 January 1601 12:00:00 AM GMT.
378
// We need the number of seconds since 1 January 1970 12:00:00 AM GMT.
379
// This is the conversion.
380
unsigned long cookie_expiration_time = static_cast<unsigned long>((expiration_time / TICKS_PER_SECOND) - UNIX_TIME_OFFSET_SECONDS);
381
cookie_to_return.set_expiration_time(cookie_expiration_time);
382
}
383
return cookie_to_return;
384
}
385
386
void CookieManager::ParseCookieString(const std::wstring& cookie_string,
387
std::map<std::string, std::string>* cookies) {
388
LOG(TRACE) << "Entering CookieManager::ParseCookieString";
389
std::wstring cookie_string_copy = cookie_string;
390
while (cookie_string_copy.size() > 0) {
391
size_t cookie_delimiter_pos = cookie_string_copy.find(L"; ");
392
std::wstring cookie = cookie_string_copy.substr(0, cookie_delimiter_pos);
393
if (cookie_delimiter_pos == std::wstring::npos) {
394
cookie_string_copy = L"";
395
} else {
396
cookie_string_copy = cookie_string_copy.substr(cookie_delimiter_pos + 2);
397
}
398
size_t cookie_separator_pos(cookie.find_first_of(L"="));
399
std::string cookie_name(StringUtilities::ToString(cookie.substr(0, cookie_separator_pos)));
400
std::string cookie_value(StringUtilities::ToString(cookie.substr(cookie_separator_pos + 1)));
401
cookies->insert(std::pair<std::string, std::string>(cookie_name, cookie_value));
402
}
403
}
404
405
std::wstring CookieManager::SendGetCookieMessage(const std::wstring& url,
406
const unsigned int message,
407
HookProcessor* hook) {
408
LOG(TRACE) << "Entering CookieManager::SendGetCookieMessage";
409
hook->PushData(url);
410
411
// Since the named pipe server has to wait for the named pipe client
412
// injected into the browser to connect to it before reading the data,
413
// and since SendMessage is synchronous, we need to send the message
414
// from a different thread to avoid a deadlock.
415
CookieSendMessageInfo info;
416
info.window_handle = this->window_handle_;
417
info.message = message;
418
unsigned int thread_id = 0;
419
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
420
0,
421
&CookieManager::ThreadProc,
422
reinterpret_cast<void*>(&info),
423
0,
424
&thread_id));
425
if (thread_handle != NULL) {
426
::CloseHandle(thread_handle);
427
} else {
428
LOGERR(DEBUG) << "Unable to create thread";
429
}
430
std::vector<char> buffer(0);
431
int bytes = hook->PullData(&buffer);
432
std::wstring cookies = reinterpret_cast<const wchar_t*>(&buffer[0]);
433
return cookies;
434
}
435
436
unsigned int WINAPI CookieManager::ThreadProc(LPVOID lpParameter) {
437
LOG(TRACE) << "Entering CookieManager::ThreadProc";
438
439
CookieSendMessageInfo* info = reinterpret_cast<CookieSendMessageInfo*>(lpParameter);
440
DWORD process_id = ::GetCurrentProcessId();
441
LRESULT result = ::SendMessage(info->window_handle,
442
info->message,
443
process_id,
444
NULL);
445
return 0;
446
}
447
448
} // namespace webdriver
449
450
#ifdef __cplusplus
451
extern "C" {
452
#endif
453
454
// In order to run the IE driver against versions of IE that do not include
455
// a version of WinINet.dll that supports the InternetGetCookiesEx2 API,
456
// we must access the API in a way that does not import it into our DLL.
457
// To that end, we duplicate the INTERNET_COOKIE2 structure here, and will
458
// call the API (if it exists) via GetModuleHandle and GetProcAddress.
459
typedef struct {
460
PWSTR pwszName;
461
PWSTR pwszValue;
462
PWSTR pwszDomain;
463
PWSTR pwszPath;
464
DWORD dwFlags;
465
FILETIME ftExpires;
466
BOOL fExpiresSet;
467
} INTERNETCOOKIE2;
468
469
typedef void* (__stdcall *InternetFreeCookiesProc)(INTERNETCOOKIE2*, DWORD);
470
typedef DWORD(__stdcall *InternetGetCookieEx2Proc)(PCWSTR, PCWSTR, DWORD, INTERNETCOOKIE2**, PDWORD);
471
472
LRESULT CALLBACK CookieWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
473
CWPSTRUCT* call_window_proc_struct = reinterpret_cast<CWPSTRUCT*>(lParam);
474
if (WM_COPYDATA == call_window_proc_struct->message) {
475
COPYDATASTRUCT* data = reinterpret_cast<COPYDATASTRUCT*>(call_window_proc_struct->lParam);
476
webdriver::HookProcessor::CopyDataToBuffer(data->cbData, data->lpData);
477
} else if (WD_GET_ALL_COOKIES == call_window_proc_struct->message) {
478
std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer();
479
int driver_process_id = static_cast<int>(call_window_proc_struct->wParam);
480
481
CComPtr<IUri> uri_pointer;
482
HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer);
483
DWORD scheme = 0;
484
uri_pointer->GetScheme(&scheme);
485
CComBSTR scheme_bstr;
486
uri_pointer->GetSchemeName(&scheme_bstr);
487
CComBSTR host_bstr;
488
uri_pointer->GetHost(&host_bstr);
489
CComBSTR path_bstr;
490
uri_pointer->GetPath(&path_bstr);
491
492
std::wstring parsed_uri = scheme_bstr;
493
parsed_uri.append(L"://");
494
parsed_uri.append(host_bstr);
495
parsed_uri.append(path_bstr);
496
497
InternetGetCookieEx2Proc get_cookie_proc = NULL;
498
InternetFreeCookiesProc free_cookies_proc = NULL;
499
HMODULE wininet_handle = ::GetModuleHandle(L"wininet");
500
if (wininet_handle) {
501
get_cookie_proc = reinterpret_cast<InternetGetCookieEx2Proc>(::GetProcAddress(wininet_handle, "InternetGetCookieEx2"));
502
free_cookies_proc = reinterpret_cast<InternetFreeCookiesProc>(::GetProcAddress(wininet_handle, "InternetFreeCookies"));
503
}
504
505
DWORD cookie_count = 0;
506
INTERNETCOOKIE2* cookie_pointer = NULL;
507
DWORD success = 1;
508
if (get_cookie_proc) {
509
success = get_cookie_proc(parsed_uri.c_str(),
510
NULL,
511
INTERNET_COOKIE_NON_SCRIPT,
512
&cookie_pointer,
513
&cookie_count);
514
}
515
516
if (success == 0) {
517
// Mimic the format of the old persistent cookie files for ease of
518
// transmission back to the driver and parsing.
519
std::wstring all_cookies = L"";
520
for (DWORD cookie_index = 0; cookie_index < cookie_count; ++cookie_index) {
521
if (all_cookies.size() > 0) {
522
all_cookies.append(L"\n*\n");
523
}
524
INTERNETCOOKIE2* current_cookie = cookie_pointer + cookie_index;
525
std::wstring cookie_name = L"";
526
if (current_cookie->pwszName) {
527
// Note that the spec appears to allow "nameless" cookies,
528
// which clients like Selenium may not support.
529
cookie_name = current_cookie->pwszName;
530
}
531
std::wstring cookie_value = L"";
532
if (current_cookie->pwszValue) {
533
cookie_value = current_cookie->pwszValue;
534
}
535
536
// TODO: The spec does not allow a cookie with an empty name
537
// and value. It's unclear what the driver could do in this
538
// case, but we should probably handle it somehow in the off
539
// chance it ever comes up.
540
std::wstring cookie_domain = L"";
541
if (current_cookie->pwszDomain) {
542
cookie_domain = current_cookie->pwszDomain;
543
}
544
std::wstring cookie_path = L"";
545
if (current_cookie->pwszPath) {
546
cookie_path = current_cookie->pwszPath;
547
}
548
DWORD flags = current_cookie->dwFlags;
549
FILETIME expires = current_cookie->ftExpires;
550
all_cookies.append(cookie_name).append(L"\n");
551
all_cookies.append(cookie_value).append(L"\n");
552
all_cookies.append(cookie_domain).append(L"/").append(cookie_path).append(L"\n");
553
all_cookies.append(std::to_wstring(flags)).append(L"\n");
554
// If the expiration time is set, add it to the string for the cookie.
555
// If not, append empty fields to the record so subsequent parsing
556
// of the string will still work.
557
if (current_cookie->fExpiresSet) {
558
all_cookies.append(std::to_wstring(expires.dwLowDateTime)).append(L"\n");
559
all_cookies.append(std::to_wstring(expires.dwHighDateTime)).append(L"\n");
560
} else {
561
all_cookies.append(L"\n\n");
562
}
563
}
564
free_cookies_proc(cookie_pointer, cookie_count);
565
webdriver::HookProcessor::CopyWStringToBuffer(all_cookies);
566
} else {
567
webdriver::HookProcessor::SetDataBufferSize(sizeof(wchar_t));
568
}
569
webdriver::HookProcessor::WriteBufferToPipe(driver_process_id);
570
} else if (WD_GET_HTTPONLY_COOKIES == call_window_proc_struct->message ||
571
WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message ||
572
WD_GET_SECURE_COOKIES == call_window_proc_struct->message) {
573
std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer();
574
int driver_process_id = static_cast<int>(call_window_proc_struct->wParam);
575
576
DWORD get_cookie_flags = 0;
577
if (WD_GET_HTTPONLY_COOKIES == call_window_proc_struct->message ||
578
WD_GET_SECURE_COOKIES == call_window_proc_struct->message) {
579
get_cookie_flags = INTERNET_COOKIE_HTTPONLY;
580
}
581
582
CComPtr<IUri> uri_pointer;
583
HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer);
584
DWORD scheme = 0;
585
uri_pointer->GetScheme(&scheme);
586
CComBSTR scheme_bstr;
587
uri_pointer->GetSchemeName(&scheme_bstr);
588
CComBSTR host_bstr;
589
uri_pointer->GetHost(&host_bstr);
590
CComBSTR path_bstr;
591
uri_pointer->GetPath(&path_bstr);
592
593
// Get only the cookies for the base URL, omitting port, if there is one.
594
// N.B., we only return cookies secure cookies when browsing a site using
595
// SSL. The browser won't see cookies with the 'secure' flag for sites
596
// visited using plain http.
597
std::wstring parsed_uri = L"http";
598
if ((WD_GET_SECURE_COOKIES == call_window_proc_struct->message ||
599
WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message) &&
600
URL_SCHEME_HTTPS == scheme) {
601
parsed_uri.append(L"s");
602
}
603
parsed_uri.append(L"://");
604
parsed_uri.append(host_bstr);
605
parsed_uri.append(path_bstr);
606
607
// Call InternetGetCookieEx once to get the size of the buffer needed,
608
// then call again with the appropriately sized buffer allocated.
609
DWORD buffer_size = 0;
610
BOOL success = ::InternetGetCookieEx(parsed_uri.c_str(),
611
NULL,
612
NULL,
613
&buffer_size,
614
get_cookie_flags,
615
NULL);
616
if (success) {
617
webdriver::HookProcessor::SetDataBufferSize(buffer_size);
618
::InternetGetCookieEx(parsed_uri.c_str(),
619
NULL,
620
reinterpret_cast<LPTSTR>(webdriver::HookProcessor::GetDataBufferAddress()),
621
&buffer_size,
622
get_cookie_flags,
623
NULL);
624
625
webdriver::HookProcessor::WriteBufferToPipe(driver_process_id);
626
} else {
627
if (ERROR_NO_MORE_ITEMS == ::GetLastError()) {
628
webdriver::HookProcessor::SetDataBufferSize(sizeof(wchar_t));
629
webdriver::HookProcessor::WriteBufferToPipe(driver_process_id);
630
}
631
}
632
} else if (WD_GET_COOKIE_CACHE_FILES == call_window_proc_struct->message) {
633
int driver_process_id = static_cast<int>(call_window_proc_struct->wParam);
634
std::wstring file_list = L"";
635
std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer();
636
637
// We need to remove the port to find the entry in the cache.
638
CComPtr<IUri> uri_pointer;
639
HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer);
640
CComBSTR host_bstr;
641
uri_pointer->GetHost(&host_bstr);
642
CComBSTR path_bstr;
643
uri_pointer->GetPath(&path_bstr);
644
std::wstring parsed_uri = host_bstr;
645
parsed_uri.append(path_bstr);
646
647
// A 2048-byte buffer should be large enough to handle cookie
648
// cache entries in all but the most extreme cases.
649
HANDLE cache_enum_handle = NULL;
650
DWORD entry_size = 2048;
651
LPINTERNET_CACHE_ENTRY_INFO entry = NULL;
652
std::vector<char> entry_buffer(entry_size);
653
entry = reinterpret_cast<INTERNET_CACHE_ENTRY_INFO*>(&entry_buffer[0]);
654
cache_enum_handle = ::FindFirstUrlCacheEntry(L"cookie:",
655
entry,
656
&entry_size);
657
if (cache_enum_handle == NULL &&
658
ERROR_INSUFFICIENT_BUFFER == ::GetLastError()) {
659
entry_buffer.resize(entry_size);
660
entry = reinterpret_cast<INTERNET_CACHE_ENTRY_INFO*>(&entry_buffer[0]);
661
cache_enum_handle = ::FindFirstUrlCacheEntry(L"cookie:",
662
entry,
663
&entry_size);
664
}
665
while (cache_enum_handle != NULL) {
666
if (COOKIE_CACHE_ENTRY == (entry->CacheEntryType & COOKIE_CACHE_ENTRY)) {
667
std::wstring name = entry->lpszSourceUrlName;
668
size_t name_separator_pos(name.find_first_of(L"@"));
669
std::wstring domain = name.substr(name_separator_pos + 1);
670
if (parsed_uri.find(domain) != std::wstring::npos) {
671
if (file_list.size() > 0) {
672
file_list.append(L"|");
673
}
674
file_list.append(entry->lpszLocalFileName);
675
}
676
}
677
BOOL success = ::FindNextUrlCacheEntry(cache_enum_handle,
678
entry,
679
&entry_size);
680
if (!success) {
681
DWORD error = ::GetLastError();
682
if (ERROR_INSUFFICIENT_BUFFER == error) {
683
entry_buffer.resize(entry_size);
684
BOOL other_success = ::FindNextUrlCacheEntry(cache_enum_handle,
685
entry,
686
&entry_size);
687
} else if (ERROR_NO_MORE_ITEMS == error) {
688
::FindCloseUrlCache(cache_enum_handle);
689
cache_enum_handle = NULL;
690
}
691
}
692
}
693
webdriver::HookProcessor::CopyWStringToBuffer(file_list);
694
webdriver::HookProcessor::WriteBufferToPipe(driver_process_id);
695
} else if (WD_SET_COOKIE == call_window_proc_struct->message) {
696
DWORD set_cookie_flags = static_cast<DWORD>(call_window_proc_struct->wParam);
697
std::wstring cookie_data = webdriver::HookProcessor::CopyWStringFromBuffer();
698
size_t url_separator_pos = cookie_data.find_first_of(L"|");
699
std::wstring url = cookie_data.substr(0, url_separator_pos);
700
std::wstring cookie = cookie_data.substr(url_separator_pos + 1);
701
702
CComPtr<IUri> uri_pointer;
703
HRESULT hr = ::CreateUri(url.c_str(), Uri_CREATE_ALLOW_RELATIVE, 0, &uri_pointer);
704
CComBSTR scheme_bstr;
705
uri_pointer->GetSchemeName(&scheme_bstr);
706
CComBSTR host_bstr;
707
uri_pointer->GetHost(&host_bstr);
708
std::wstring parsed_uri = scheme_bstr;
709
parsed_uri.append(L"://");
710
parsed_uri.append(host_bstr);
711
712
// Leverage the shared data buffer size to return the error code
713
// back to the driver, if necessary.
714
DWORD cookie_set = ::InternetSetCookieEx(parsed_uri.c_str(),
715
NULL,
716
cookie.c_str(),
717
set_cookie_flags,
718
NULL);
719
if (cookie_set) {
720
webdriver::HookProcessor::SetDataBufferSize(0);
721
} else {
722
DWORD error = ::GetLastError();
723
webdriver::HookProcessor::SetDataBufferSize(error);
724
}
725
}
726
return ::CallNextHookEx(NULL, nCode, wParam, lParam);
727
}
728
729
#ifdef __cplusplus
730
}
731
#endif
732
733