1 // Copyright 2007, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "gears/base/common/string_utils.h"
29 #include "gears/localserver/common/http_cookies.h"
31 //------------------------------------------------------------------------------
32 // Browser-independent code
33 //------------------------------------------------------------------------------
35 const std::string16
kNegatedRequiredCookieValue(STRING16(L
";NONE;"));
37 static const std::string16
kCookieDelimiter(STRING16(L
";"));
39 void ParseCookieString(const std::string16
&cookies
, CookieMap
*map
) {
41 std::vector
<std::string16
> tokens
;
42 Tokenize(cookies
, kCookieDelimiter
, &tokens
);
43 for (int i
= 0; i
< static_cast<int>(tokens
.size()); ++i
) {
44 std::string16 name
, value
;
45 ParseCookieNameAndValue(tokens
[i
], &name
, &value
);
47 // If the map already has a more specific entry for this name, don't
48 // add this less specific value
49 if (!map
->HasCookie(name
)) {
56 bool CookieMap::LoadMapForUrl(const char16
*url
) {
57 std::string16 cookies_string
;
58 if (!GetCookieString(url
, &cookies_string
))
60 ParseCookieString(cookies_string
, this);
64 bool CookieMap::GetCookie(const std::string16
&cookie_name
,
65 std::string16
*cookie_value
) {
66 const_iterator found
= find(cookie_name
);
69 *cookie_value
= found
->second
;
73 bool CookieMap::HasCookie(const std::string16
&cookie_name
) {
74 const_iterator found
= find(cookie_name
);
75 return found
!= end();
78 bool CookieMap::HasSpecificCookie(const std::string16
&cookie_name
,
79 const std::string16
&cookie_value
) {
80 const_iterator found
= find(cookie_name
);
83 return cookie_value
== found
->second
;
86 bool CookieMap::HasLocalServerRequiredCookie(
87 const std::string16
&required_cookie
) {
88 if (required_cookie
.empty())
91 std::string16 name
, value
;
92 ParseCookieNameAndValue(required_cookie
, &name
, &value
);
96 return (value
== kNegatedRequiredCookieValue
)
97 ? !HasCookie(name
) : HasSpecificCookie(name
, value
);
100 void ParseCookieNameAndValue(const std::string16
&name_and_value
,
102 std::string16
*value
) {
103 // Some observations about cookie names and values
104 // - names/values cannot have leading or trailing whitespace
105 // - names/values can have embedded whitespace
106 // - names/values cannot contain the ';' character
107 // - values can contain embedded '=' and ',' charaters
108 size_t equal_pos
= name_and_value
.find('=');
109 if (equal_pos
== std::string16::npos
) {
110 const char16
*start
= name_and_value
.c_str();
111 int len
= name_and_value
.length();
112 StripWhiteSpace(&start
, &len
);
113 name
->assign(start
, len
);
116 const char16
*start
= name_and_value
.c_str();
117 int len
= static_cast<int>(equal_pos
);
118 StripWhiteSpace(&start
, &len
);
119 name
->assign(start
, len
);
120 size_t value_pos
= equal_pos
+ 1;
121 if (value_pos
< name_and_value
.length() - 1) {
122 start
= name_and_value
.c_str() + value_pos
;
123 len
= name_and_value
.length() - value_pos
;
124 StripWhiteSpace(&start
, &len
);
125 value
->assign(start
, len
);
134 #include "gears/base/common/mutex.h"
135 static Mutex g_fake_lock
;
136 static std::string16 g_fake_url
;
137 static std::string16 g_fake_cookies
;
139 void SetFakeCookieString(const char16
* url
, const char16
*cookies
) {
140 MutexLock
locker(&g_fake_lock
);
141 const char16
*kEmptyString
= STRING16(L
"");
142 g_fake_url
= url
? url
: kEmptyString
;
143 g_fake_cookies
= cookies
? cookies
: kEmptyString
;
146 static bool GetFakeCookieString(const char16
*url
, std::string16
*cookies
) {
147 MutexLock
locker(&g_fake_lock
);
148 if (url
== g_fake_url
) {
149 *cookies
= g_fake_cookies
;
157 //------------------------------------------------------------------------------
158 // Browser-dependent code
159 //------------------------------------------------------------------------------
163 #include "gears/base/ie/atl_headers.h"
165 bool GetCookieString(const char16
*url
, std::string16
*cookies_out
) {
169 if (GetFakeCookieString(url
, cookies_out
)) {
174 cookies_out
->clear();
176 // To defend against the cookie string changing between the first and second
177 // calls to InternetGetCookieW, we loop if we need to
178 DWORD last_len
= 0xffffffff;
181 if (!InternetGetCookieW(url
, NULL
, NULL
, &len
)) {
182 DWORD last_error
= GetLastError();
183 // FALSE is returned when the cookie string is empty (error code is
184 // ERROR_NO_MORE_ITEMS), but this is not an error condition. Return true
185 // and an empty string.
187 // On WinCE, FALSE is also returned when we pass NULL for the data string.
188 // The error code is ERROR_INSUFFICIENT_BUFFER, but the length parameter
189 // is set correctly. In this case, we continue getting the cookie.
191 // If the error is anything else, return false.
192 if (ERROR_NO_MORE_ITEMS
== last_error
) {
194 } else if (ERROR_INSUFFICIENT_BUFFER
!= last_error
) {
200 return false; // don't loop forever
204 CStringW cookies_str
;
205 BOOL ok
= InternetGetCookieW(url
, NULL
, cookies_str
.GetBuffer(len
+ 1),
207 DWORD last_error
= GetLastError();
208 cookies_str
.ReleaseBuffer(len
);
210 cookies_out
->assign(cookies_str
.GetString());
212 } else if (last_error
== ERROR_NO_MORE_ITEMS
) {
214 } else if (last_error
== ERROR_INSUFFICIENT_BUFFER
) {
215 continue; // loop and try again with a larger buffer
221 // GetBuffer(len + 1) followied ReleaseBuffer(len) looks odd, here's
222 // what's going on with that ATL::CString wart.
223 // For GetBuffer, len is the minimum size of the character buffer in
224 // characters. This value does not include space for a null terminator.
225 // For ReleaseBuffer, len is the new length of the string in characters,
226 // not counting a null terminator
227 // So you have to leave room for the NULL when calling GetBuffer yourself.
230 //------------------------------------------------------------------------------
233 #include <windows.h> // must manually #include before nsIEventQueueService.h
235 #include <gecko_sdk/include/nsMemory.h>
236 #include <gecko_sdk/include/nsIURI.h>
237 #include <gecko_sdk/include/nsIIOService.h>
238 #include <gecko_internal/nsICookieService.h>
239 #include <gecko_internal/nsIEventQueueService.h>
240 #include <gecko_internal/nsIProxyObjectManager.h>
241 #include "gears/base/common/common.h"
243 bool GetCookieString(const char16
*url
, std::string16
*cookies_out
) {
247 if (GetFakeCookieString(url
, cookies_out
)) {
252 cookies_out
->clear();
254 // Get the ioservice which we need to create nsIURIs
255 nsCOMPtr
<nsIIOService
> ios
=
256 do_GetService("@mozilla.org/network/io-service;1");
257 if (!ios
) { return false; }
259 // Convert url to utf8 nsCString, so we can create an nsIURI
263 nsresult nr
= NS_UTF16ToCString(url_str
, NS_CSTRING_ENCODING_UTF8
, url_utf8
);
264 NS_ENSURE_SUCCESS(nr
, false);
266 // Manufacture an nsIURI, so we can get the cookie string
267 nsCOMPtr
<nsIURI
> url_obj
;
268 nr
= ios
->NewURI(url_utf8
, nsnull
, nsnull
, getter_AddRefs(url_obj
));
269 NS_ENSURE_SUCCESS(nr
, false);
271 // Get the cookie service
272 nsCOMPtr
<nsICookieService
> cookie_service
=
273 do_GetService("@mozilla.org/cookieService;1");
274 if (!cookie_service
) return false;
276 // Make a proxy for it in case we're being called form a worker thread.
277 // If we're on the main thread, we'll call straight through.
278 // from nsProxyEvent.h
279 #define PROXY_SYNC 0x0001 // acts just like a function call
280 #define PROXY_ASYNC 0x0002 // fire and forget
281 #define PROXY_ALWAYS 0x0004 // ignore check to see if the eventQ
282 // is on the same thread as the caller
283 nsCOMPtr
<nsIProxyObjectManager
> proxy_manager
=
284 do_GetService(NS_XPCOMPROXY_CONTRACTID
);
285 if (!proxy_manager
) return false;
286 nsCOMPtr
<nsICookieService
> cookie_service_proxy
;
287 nr
= proxy_manager
->GetProxyForObject(NS_UI_THREAD_EVENTQ
,
288 NS_GET_IID(nsICookieService
),
291 getter_AddRefs(cookie_service_proxy
));
292 if (!cookie_service_proxy
|| NS_FAILED(nr
)) return false;
293 if (cookie_service_proxy
!= cookie_service
) {
294 LOG(("WARNING: GetCookieString not called on the UI thread"));
297 // Finally, call the cookie service
298 char *cookies_str
= NULL
;
299 nr
= cookie_service_proxy
->GetCookieString(url_obj
, NULL
, &cookies_str
);
300 NS_ENSURE_SUCCESS(nr
, false);
302 // A return value of NS_OK and a NULL cookie_str means an empty string
304 assert(cookies_out
->empty()); // was cleared on function entry
308 // One last hoop, convert to string16
309 bool rv
= UTF8ToString16(cookies_str
, cookies_out
);
310 nsMemory::Free(cookies_str
);
315 //------------------------------------------------------------------------------
318 bool GetCookieString(const char16
*url
, std::string16
*cookies_out
) {
319 // TODO(mpcomplete): Uh oh... how do we get cookies in NPAPI?
324 #include "gears/base/safari/scoped_cf.h"
325 #include "gears/base/safari/string_utils.h"
326 #include "gears/localserver/safari/http_cookies_sf.h"
328 bool GetCookieString(const char16
*url
, std::string16
*cookies_out
) {
332 if (GetFakeCookieString(url
, cookies_out
)) {
337 scoped_CFString
url_cfstr(CFStringCreateWithString16(url
));
338 scoped_CFString
cookie_cfstr(GetHTTPCookieString(url_cfstr
.get()));
340 return CFStringRefToString16(cookie_cfstr
.get(), cookies_out
);