[Author: zork]
[google-gears.git] / gears / localserver / common / http_cookies.cc
blobe6309471050ddb7d78b184f1111c29390695c95b
1 // Copyright 2007, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
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.
26 #include <assert.h>
27 #include <vector>
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) {
40 map->clear();
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);
46 if (!name.empty()) {
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)) {
50 (*map)[name] = value;
56 bool CookieMap::LoadMapForUrl(const char16 *url) {
57 std::string16 cookies_string;
58 if (!GetCookieString(url, &cookies_string))
59 return false;
60 ParseCookieString(cookies_string, this);
61 return true;
64 bool CookieMap::GetCookie(const std::string16 &cookie_name,
65 std::string16 *cookie_value) {
66 const_iterator found = find(cookie_name);
67 if (found == end())
68 return false;
69 *cookie_value = found->second;
70 return true;
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);
81 if (found == end())
82 return false;
83 return cookie_value == found->second;
86 bool CookieMap::HasLocalServerRequiredCookie(
87 const std::string16 &required_cookie) {
88 if (required_cookie.empty())
89 return true;
91 std::string16 name, value;
92 ParseCookieNameAndValue(required_cookie, &name, &value);
93 if (name.empty())
94 return false;
96 return (value == kNegatedRequiredCookieValue)
97 ? !HasCookie(name) : HasSpecificCookie(name, value);
100 void ParseCookieNameAndValue(const std::string16 &name_and_value,
101 std::string16 *name,
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);
114 value->clear();
115 } else {
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);
126 } else {
127 value->clear();
133 #ifdef DEBUG
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;
150 return true;
151 } else {
152 return false;
155 #endif
157 //------------------------------------------------------------------------------
158 // Browser-dependent code
159 //------------------------------------------------------------------------------
160 #if BROWSER_IE
161 #include <windows.h>
162 #include <wininet.h>
163 #include "gears/base/ie/atl_headers.h"
165 bool GetCookieString(const char16 *url, std::string16 *cookies_out) {
166 assert(url);
167 assert(cookies_out);
168 #ifdef DEBUG
169 if (GetFakeCookieString(url, cookies_out)) {
170 return true;
172 #endif
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;
179 while (true) {
180 DWORD len = 0;
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) {
193 return true;
194 } else if (ERROR_INSUFFICIENT_BUFFER != last_error) {
195 return false;
199 if (len == last_len)
200 return false; // don't loop forever
201 else
202 last_len = len;
204 CStringW cookies_str;
205 BOOL ok = InternetGetCookieW(url, NULL, cookies_str.GetBuffer(len + 1),
206 &len);
207 DWORD last_error = GetLastError();
208 cookies_str.ReleaseBuffer(len);
209 if (ok) {
210 cookies_out->assign(cookies_str.GetString());
211 return true;
212 } else if (last_error == ERROR_NO_MORE_ITEMS) {
213 return true;
214 } else if (last_error == ERROR_INSUFFICIENT_BUFFER) {
215 continue; // loop and try again with a larger buffer
216 } else {
217 return false;
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 //------------------------------------------------------------------------------
231 #elif BROWSER_FF
232 #ifdef WIN32
233 #include <windows.h> // must manually #include before nsIEventQueueService.h
234 #endif
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) {
244 assert(url);
245 assert(cookies_out);
246 #ifdef DEBUG
247 if (GetFakeCookieString(url, cookies_out)) {
248 return true;
250 #endif
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
260 nsString url_str;
261 nsCString url_utf8;
262 url_str.Assign(url);
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),
289 cookie_service,
290 PROXY_SYNC,
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
303 if (!cookies_str) {
304 assert(cookies_out->empty()); // was cleared on function entry
305 return true;
308 // One last hoop, convert to string16
309 bool rv = UTF8ToString16(cookies_str, cookies_out);
310 nsMemory::Free(cookies_str);
312 return rv;
315 //------------------------------------------------------------------------------
316 #elif BROWSER_NPAPI
318 bool GetCookieString(const char16 *url, std::string16 *cookies_out) {
319 // TODO(mpcomplete): Uh oh... how do we get cookies in NPAPI?
320 return false;
323 #elif BROWSER_SAFARI
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) {
329 assert(url);
330 assert(cookies_out);
331 #ifdef DEBUG
332 if (GetFakeCookieString(url, cookies_out)) {
333 return true;
335 #endif
337 scoped_CFString url_cfstr(CFStringCreateWithString16(url));
338 scoped_CFString cookie_cfstr(GetHTTPCookieString(url_cfstr.get()));
340 return CFStringRefToString16(cookie_cfstr.get(), cookies_out);
343 #endif