@@ -50,6 +50,15 @@ void CookieManager::Initialize(HWND window_handle) {
50
50
this ->window_handle_ = window_handle;
51
51
}
52
52
53
+ bool CookieManager::IsAdvancedCookiesApi () {
54
+ FARPROC address = NULL ;
55
+ HMODULE wininet_handle = ::GetModuleHandle (L" wininet" );
56
+ if (wininet_handle) {
57
+ address = ::GetProcAddress (wininet_handle, " InternetGetCookieEx2" );
58
+ }
59
+ return address != NULL ;
60
+ }
61
+
53
62
int CookieManager::SetCookie (const std::string& url,
54
63
const BrowserCookie& cookie) {
55
64
std::string full_data = url + " |" + cookie.ToString ();
@@ -105,70 +114,93 @@ int CookieManager::GetCookies(const std::string& url,
105
114
}
106
115
hook.Initialize (hook_settings);
107
116
108
- // Get all cookies for the current URL visible to JavaScript.
109
- std::wstring scriptable_cookie_string =
110
- this ->SendGetCookieMessage (wide_url,
111
- WD_GET_SCRIPTABLE_COOKIES,
112
- &hook);
113
- std::map<std::string, std::string> scriptable_cookies;
114
- this ->ParseCookieString (scriptable_cookie_string, &scriptable_cookies);
115
-
116
- // Get all cookies for the insecure version of the current URL,
117
- // which will include HttpOnly cookies.
118
- std::wstring insecure_cookie_string =
119
- this ->SendGetCookieMessage (wide_url,
120
- WD_GET_HTTPONLY_COOKIES,
121
- &hook);
122
- std::map<std::string, std::string> insecure_cookies;
123
- this ->ParseCookieString (insecure_cookie_string, &insecure_cookies);
124
-
125
- // Get all cookies for the current secure URL. This will include
126
- // HttpOnly cookies.
127
- std::wstring secure_cookie_string =
128
- this ->SendGetCookieMessage (wide_url,
129
- WD_GET_SECURE_COOKIES,
130
- &hook);
131
- std::map<std::string, std::string> secure_cookies;
132
- this ->ParseCookieString (secure_cookie_string, &secure_cookies);
133
-
134
- // Get all of the persistent cookie files in the cache for the
135
- // URL currently being browsed.
136
- std::wstring file_list = this ->SendGetCookieMessage (
137
- wide_url,
138
- WD_GET_COOKIE_CACHE_FILES,
139
- &hook);
140
- std::vector<std::wstring> files;
141
- StringUtilities::Split (file_list, L" |" , &files);
142
-
143
- // Parse the persistent cookie files to produce a list of
144
- // cookies.
145
- std::map<std::string, BrowserCookie> persistent_cookies;
146
- for (std::vector<std::wstring>::const_iterator file_iterator = files.begin ();
147
- file_iterator != files.end ();
148
- ++file_iterator) {
149
- this ->ReadPersistentCookieFile (*file_iterator,
150
- is_secure_url,
151
- &persistent_cookies);
152
- }
117
+ bool supports_advanced_api = this ->IsAdvancedCookiesApi ();
118
+ if (supports_advanced_api) {
119
+ // The version of WinINet installed supports the InternetGetCookieEx2
120
+ // API, which gets all cookies (session and persistent) at once.
121
+ std::wstring raw_cookie_data =
122
+ this ->SendGetCookieMessage (wide_url,
123
+ WD_GET_ALL_COOKIES,
124
+ &hook);
125
+ std::string all_cookies_list = StringUtilities::ToString (raw_cookie_data);
126
+ std::map<std::string, BrowserCookie> cookies;
127
+ this ->ParseCookieList (all_cookies_list,
128
+ is_secure_url,
129
+ &cookies);
130
+ std::map<std::string, BrowserCookie>::const_iterator cookie_iterator;
131
+ for (cookie_iterator = cookies.begin ();
132
+ cookie_iterator != cookies.end ();
133
+ ++cookie_iterator) {
134
+ all_cookies->push_back (cookie_iterator->second );
135
+ }
136
+ } else {
137
+ // Get all cookies for the current URL visible to JavaScript.
138
+ std::wstring scriptable_cookie_string =
139
+ this ->SendGetCookieMessage (wide_url,
140
+ WD_GET_SCRIPTABLE_COOKIES,
141
+ &hook);
142
+ std::map<std::string, std::string> scriptable_cookies;
143
+ this ->ParseCookieString (scriptable_cookie_string, &scriptable_cookies);
144
+
145
+ // Get all cookies for the insecure version of the current URL,
146
+ // which will include HttpOnly cookies.
147
+ std::wstring insecure_cookie_string =
148
+ this ->SendGetCookieMessage (wide_url,
149
+ WD_GET_HTTPONLY_COOKIES,
150
+ &hook);
151
+ std::map<std::string, std::string> insecure_cookies;
152
+ this ->ParseCookieString (insecure_cookie_string, &insecure_cookies);
153
+
154
+ // Get all cookies for the current secure URL. This will include
155
+ // HttpOnly cookies.
156
+ std::wstring secure_cookie_string =
157
+ this ->SendGetCookieMessage (wide_url,
158
+ WD_GET_SECURE_COOKIES,
159
+ &hook);
160
+ std::map<std::string, std::string> secure_cookies;
161
+ this ->ParseCookieString (secure_cookie_string, &secure_cookies);
162
+
163
+ // Get all of the persistent cookie files in the cache for the
164
+ // URL currently being browsed.
165
+ std::wstring file_list =
166
+ this ->SendGetCookieMessage (wide_url,
167
+ WD_GET_COOKIE_CACHE_FILES,
168
+ &hook);
169
+ std::vector<std::wstring> files;
170
+ StringUtilities::Split (file_list, L" |" , &files);
171
+
172
+ // Parse the persistent cookie files to produce a list of
173
+ // cookies.
174
+ std::map<std::string, BrowserCookie> persistent_cookies;
175
+ std::vector<std::wstring>::const_iterator file_iterator;
176
+ for (file_iterator = files.begin ();
177
+ file_iterator != files.end ();
178
+ ++file_iterator) {
179
+ std::string cookie_file_contents = this ->ReadCookieFile (*file_iterator);
180
+ this ->ParseCookieList (cookie_file_contents,
181
+ is_secure_url,
182
+ &persistent_cookies);
183
+ }
153
184
154
- // Loop through the entire list of cookies, including HttpOnly and secure
155
- // cookies. If the cookie exists as a persistent cookie, use its data from
156
- // the cache. If the cookie is found in the list of cookies visible to
157
- // JavaScript, set the HttpOnly property of the cookie to false. If the
158
- // cookie is found in the list of cookies set on the insecure version of
159
- // the URL, set the Secure property of the cookie to false.
160
- std::map<std::string, std::string>::const_iterator it = secure_cookies.begin ();
161
- for (; it != secure_cookies.end (); ++it) {
162
- BrowserCookie browser_cookie;
163
- if (persistent_cookies.find (it->first ) != persistent_cookies.end ()) {
164
- browser_cookie = persistent_cookies[it->first ];
165
- } else {
166
- browser_cookie.set_name (it->first );
167
- browser_cookie.set_value (it->second );
168
- browser_cookie.set_is_httponly (scriptable_cookies.find (it->first ) == scriptable_cookies.end ());
169
- browser_cookie.set_is_secure (insecure_cookies.find (it->first ) == insecure_cookies.end ());
185
+ // Loop through the entire list of cookies, including HttpOnly and secure
186
+ // cookies. If the cookie exists as a persistent cookie, use its data from
187
+ // the cache. If the cookie is found in the list of cookies visible to
188
+ // JavaScript, set the HttpOnly property of the cookie to false. If the
189
+ // cookie is found in the list of cookies set on the insecure version of
190
+ // the URL, set the Secure property of the cookie to false.
191
+ std::map<std::string, std::string>::const_iterator it = secure_cookies.begin ();
192
+ for (; it != secure_cookies.end (); ++it) {
193
+ BrowserCookie browser_cookie;
194
+ if (persistent_cookies.find (it->first ) != persistent_cookies.end ()) {
195
+ browser_cookie = persistent_cookies[it->first ];
196
+ } else {
197
+ browser_cookie.set_name (it->first );
198
+ browser_cookie.set_value (it->second );
199
+ browser_cookie.set_is_httponly (scriptable_cookies.find (it->first ) == scriptable_cookies.end ());
200
+ browser_cookie.set_is_secure (insecure_cookies.find (it->first ) == insecure_cookies.end ());
201
+ }
202
+ all_cookies->push_back (browser_cookie);
170
203
}
171
- all_cookies->push_back (browser_cookie);
172
204
}
173
205
return WD_SUCCESS;
174
206
}
@@ -252,10 +284,8 @@ bool CookieManager::RecurseCookieDomain(const std::string& url,
252
284
return status == WD_SUCCESS;
253
285
}
254
286
255
- void CookieManager::ReadPersistentCookieFile (const std::wstring& file_name,
256
- const bool include_secure_cookies,
257
- std::map<std::string, BrowserCookie>* cookies) {
258
- LOG (TRACE) << " Entering CookieManager::ReadPersistentCookieFile" ;
287
+ std::string CookieManager::ReadCookieFile (const std::wstring& file_name) {
288
+ LOG (TRACE) << " Entering CookieManager::ReadCookieFile" ;
259
289
HANDLE file_handle = ::CreateFile (file_name.c_str (),
260
290
GENERIC_READ,
261
291
FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -274,6 +304,13 @@ void CookieManager::ReadPersistentCookieFile(const std::wstring& file_name,
274
304
// Null-terminate and convert to a string for easier manipulation.
275
305
file_content[bytes_read - 1 ] = ' \0 ' ;
276
306
std::string cookie_file_contents = &file_content[0 ];
307
+ return cookie_file_contents;
308
+ }
309
+
310
+ void CookieManager::ParseCookieList (const std::string& cookie_file_contents,
311
+ const bool include_secure_cookies,
312
+ std::map<std::string, BrowserCookie>* cookies) {
313
+ LOG (TRACE) << " Entering CookieManager::ParseCookieList" ;
277
314
278
315
// Each cookie in the file is a record structure separated by
279
316
// a line containing a single asterisk ('*'). Split the file
@@ -282,12 +319,12 @@ void CookieManager::ReadPersistentCookieFile(const std::wstring& file_name,
282
319
StringUtilities::Split (cookie_file_contents,
283
320
" \n *\n " ,
284
321
&persistent_cookie_strings);
285
- std::vector<std::string>::const_iterator cookie_string_iterator = persistent_cookie_strings. begin () ;
286
- for (;
322
+ std::vector<std::string>::const_iterator cookie_string_iterator;
323
+ for (cookie_string_iterator = persistent_cookie_strings. begin () ;
287
324
cookie_string_iterator != persistent_cookie_strings.end ();
288
325
++cookie_string_iterator) {
289
326
BrowserCookie persistent_cookie =
290
- this ->ParsePersistentCookieInfo (*cookie_string_iterator);
327
+ this ->ParseSingleCookie (*cookie_string_iterator);
291
328
if (include_secure_cookies || !persistent_cookie.is_secure ()) {
292
329
// Omit the cookie if it's 'secure' flag is set and we are *not*
293
330
// browsing using SSL.
@@ -298,12 +335,13 @@ void CookieManager::ReadPersistentCookieFile(const std::wstring& file_name,
298
335
}
299
336
}
300
337
301
- BrowserCookie CookieManager::ParsePersistentCookieInfo (const std::string& cookie) {
338
+ BrowserCookie CookieManager::ParseSingleCookie (const std::string& cookie) {
302
339
LOG (TRACE) << " Entering CookieManager::ParsePersistentCookieInfo" ;
303
- // Persistent cookies are read from out of the cached
304
- // files on disk. Each cookie is represented by 8 lines
305
- // in the file separated by line feed (0xA) characters,
306
- // with the following format:
340
+ // Cookies represented by a structured string record type.
341
+ // This structure is modeled after how some versions of IE
342
+ // stored perisitent cookeis as files on disk. Each cookie
343
+ // is represented by 8 lines in the file separated by line
344
+ // feed (0xA) characters, with the following format:
307
345
//
308
346
// cookie_name
309
347
// cookie_value
@@ -331,16 +369,18 @@ BrowserCookie CookieManager::ParsePersistentCookieInfo(const std::string& cookie
331
369
cookie_to_return.set_is_secure (INTERNET_COOKIE_IS_SECURE == (INTERNET_COOKIE_IS_SECURE & flags));
332
370
cookie_to_return.set_is_httponly (INTERNET_COOKIE_HTTPONLY == (INTERNET_COOKIE_HTTPONLY & flags));
333
371
334
- unsigned long expiry_time_low = strtoul (cookie_parts[4 ].c_str (), NULL , 10 );
335
- unsigned long expiry_time_high = strtoul (cookie_parts[5 ].c_str (), NULL , 10 );
336
- unsigned long long expiration_time = (expiry_time_high * static_cast <long long >(pow (2.0 , 32 ))) + expiry_time_low;
337
-
338
- // Cookie expiration time is stored in the file as the number
339
- // of 100-nanosecond ticks since 1 January 1601 12:00:00 AM GMT.
340
- // We need the number of seconds since 1 January 1970 12:00:00 AM GMT.
341
- // This is the conversion.
342
- unsigned long cookie_expiration_time = static_cast <unsigned long >((expiration_time / TICKS_PER_SECOND) - UNIX_TIME_OFFSET_SECONDS);
343
- cookie_to_return.set_expiration_time (cookie_expiration_time);
372
+ if (cookie_parts[4 ].size () > 0 && cookie_parts[5 ].size () > 0 ) {
373
+ unsigned long expiry_time_low = strtoul (cookie_parts[4 ].c_str (), NULL , 10 );
374
+ unsigned long expiry_time_high = strtoul (cookie_parts[5 ].c_str (), NULL , 10 );
375
+ unsigned long long expiration_time = (expiry_time_high * static_cast <long long >(pow (2.0 , 32 ))) + expiry_time_low;
376
+
377
+ // Cookie expiration time is stored in the file as the number
378
+ // of 100-nanosecond ticks since 1 January 1601 12:00:00 AM GMT.
379
+ // We need the number of seconds since 1 January 1970 12:00:00 AM GMT.
380
+ // This is the conversion.
381
+ unsigned long cookie_expiration_time = static_cast <unsigned long >((expiration_time / TICKS_PER_SECOND) - UNIX_TIME_OFFSET_SECONDS);
382
+ cookie_to_return.set_expiration_time (cookie_expiration_time);
383
+ }
344
384
return cookie_to_return;
345
385
}
346
386
@@ -417,6 +457,79 @@ LRESULT CALLBACK CookieWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
417
457
if (WM_COPYDATA == call_window_proc_struct->message ) {
418
458
COPYDATASTRUCT* data = reinterpret_cast <COPYDATASTRUCT*>(call_window_proc_struct->lParam );
419
459
webdriver::HookProcessor::CopyDataToBuffer (data->cbData , data->lpData );
460
+ } else if (WD_GET_ALL_COOKIES == call_window_proc_struct->message ) {
461
+ std::wstring url = webdriver::HookProcessor::CopyWStringFromBuffer ();
462
+ int driver_process_id = static_cast <int >(call_window_proc_struct->wParam );
463
+
464
+ CComPtr<IUri> uri_pointer;
465
+ HRESULT hr = ::CreateUri (url.c_str (), Uri_CREATE_ALLOW_RELATIVE, 0 , &uri_pointer);
466
+ DWORD scheme = 0 ;
467
+ uri_pointer->GetScheme (&scheme);
468
+ CComBSTR scheme_bstr;
469
+ uri_pointer->GetSchemeName (&scheme_bstr);
470
+ CComBSTR host_bstr;
471
+ uri_pointer->GetHost (&host_bstr);
472
+ CComBSTR path_bstr;
473
+ uri_pointer->GetPath (&path_bstr);
474
+
475
+ // Get only the cookies for the base URL, omitting port, if there is one.
476
+ // N.B., we only return cookies secure cookies when browsing a site using
477
+ // SSL. The browser won't see cookies with the 'secure' flag for sites
478
+ // visited using plain http.
479
+ std::wstring parsed_uri = L" http" ;
480
+ if ((WD_GET_SECURE_COOKIES == call_window_proc_struct->message ||
481
+ WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message ) &&
482
+ URL_SCHEME_HTTPS == scheme) {
483
+ parsed_uri.append (L" s" );
484
+ }
485
+ parsed_uri.append (L" ://" );
486
+ parsed_uri.append (host_bstr);
487
+ parsed_uri.append (path_bstr);
488
+
489
+ // Allocate a static array for cookies, since IE is documented
490
+ // to limit the number of cookies to 50.
491
+ DWORD cookie_count = 0 ;
492
+ INTERNET_COOKIE2* cookie_pointer;
493
+ DWORD success = ::InternetGetCookieEx2 (parsed_uri.c_str (),
494
+ NULL ,
495
+ INTERNET_COOKIE_NON_SCRIPT,
496
+ &cookie_pointer,
497
+ &cookie_count);
498
+ if (success == 0 ) {
499
+ // Mimic the format of the old persistent cookie files for ease of
500
+ // transmission back to the driver and parsing.
501
+ std::wstring all_cookies = L" " ;
502
+ for (int cookie_index = 0 ; cookie_index < cookie_count; ++cookie_index) {
503
+ if (all_cookies.size () > 0 ) {
504
+ all_cookies.append (L" \n *\n " );
505
+ }
506
+ INTERNET_COOKIE2* current_cookie = cookie_pointer + cookie_index;
507
+ std::wstring cookie_name = current_cookie->pwszName ;
508
+ std::wstring cookie_value = current_cookie->pwszValue ;
509
+ std::wstring cookie_domain = current_cookie->pwszDomain ;
510
+ std::wstring cookie_path = current_cookie->pwszPath ;
511
+ DWORD flags = current_cookie->dwFlags ;
512
+ FILETIME expires = current_cookie->ftExpires ;
513
+ all_cookies.append (cookie_name).append (L" \n " );
514
+ all_cookies.append (cookie_value).append (L" \n " );
515
+ all_cookies.append (cookie_domain).append (L" /" ).append (cookie_path).append (L" \n " );
516
+ all_cookies.append (std::to_wstring (flags)).append (L" \n " );
517
+ // If the expiration time is set, add it to the string for the cookie.
518
+ // If not, append empty fields to the record so subsequent parsing
519
+ // of the string will still work.
520
+ if (current_cookie->fExpiresSet ) {
521
+ all_cookies.append (std::to_wstring (expires.dwLowDateTime )).append (L" \n " );
522
+ all_cookies.append (std::to_wstring (expires.dwHighDateTime )).append (L" \n " );
523
+ } else {
524
+ all_cookies.append (L" \n\n " );
525
+ }
526
+ }
527
+ ::InternetFreeCookies (cookie_pointer, cookie_count);
528
+ webdriver::HookProcessor::CopyWStringToBuffer (all_cookies);
529
+ } else {
530
+ webdriver::HookProcessor::SetDataBufferSize (sizeof (wchar_t ));
531
+ }
532
+ webdriver::HookProcessor::WriteBufferToPipe (driver_process_id);
420
533
} else if (WD_GET_HTTPONLY_COOKIES == call_window_proc_struct->message ||
421
534
WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message ||
422
535
WD_GET_SECURE_COOKIES == call_window_proc_struct->message ) {
@@ -425,7 +538,7 @@ LRESULT CALLBACK CookieWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
425
538
426
539
DWORD get_cookie_flags = 0 ;
427
540
if (WD_GET_HTTPONLY_COOKIES == call_window_proc_struct->message ||
428
- WD_GET_SECURE_COOKIES == call_window_proc_struct->message ) {
541
+ WD_GET_SECURE_COOKIES == call_window_proc_struct->message ) {
429
542
get_cookie_flags = INTERNET_COOKIE_HTTPONLY;
430
543
}
431
544
@@ -439,14 +552,14 @@ LRESULT CALLBACK CookieWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
439
552
uri_pointer->GetHost (&host_bstr);
440
553
CComBSTR path_bstr;
441
554
uri_pointer->GetPath (&path_bstr);
442
-
555
+
443
556
// Get only the cookies for the base URL, omitting port, if there is one.
444
557
// N.B., we only return cookies secure cookies when browsing a site using
445
558
// SSL. The browser won't see cookies with the 'secure' flag for sites
446
559
// visited using plain http.
447
560
std::wstring parsed_uri = L" http" ;
448
561
if ((WD_GET_SECURE_COOKIES == call_window_proc_struct->message ||
449
- WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message ) &&
562
+ WD_GET_SCRIPTABLE_COOKIES == call_window_proc_struct->message ) &&
450
563
URL_SCHEME_HTTPS == scheme) {
451
564
parsed_uri.append (L" s" );
452
565
}
0 commit comments