1 /*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "WebCookieJar.h"
28
29 #include "JNIUtility.h"
30 #include "WebCoreJni.h"
31 #include "WebRequestContext.h"
32 #include "WebUrlLoaderClient.h"
33
34 #include <cutils/log.h>
35 #include <dirent.h>
36
37 #undef ASSERT
38 #define ASSERT(assertion, ...) do \
39 if (!(assertion)) { \
40 android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \
41 } \
42 while (0)
43
44 namespace android {
45
46 static WTF::Mutex instanceMutex;
47 static bool isFirstInstanceCreated = false;
48 static bool fileSchemeCookiesEnabled = false;
49
databaseDirectory()50 static const std::string& databaseDirectory()
51 {
52 // This method may be called on any thread, as the Java method is
53 // synchronized.
54 static WTF::Mutex databaseDirectoryMutex;
55 MutexLocker lock(databaseDirectoryMutex);
56 static std::string databaseDirectory;
57 if (databaseDirectory.empty()) {
58 JNIEnv* env = JSC::Bindings::getJNIEnv();
59 jclass bridgeClass = env->FindClass("android/webkit/JniUtil");
60 jmethodID method = env->GetStaticMethodID(bridgeClass, "getDatabaseDirectory", "()Ljava/lang/String;");
61 databaseDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method)));
62 env->DeleteLocalRef(bridgeClass);
63 }
64 return databaseDirectory;
65 }
66
removeFileOrDirectory(const char * filename)67 static void removeFileOrDirectory(const char* filename)
68 {
69 struct stat filetype;
70 if (stat(filename, &filetype) != 0)
71 return;
72 if (S_ISDIR(filetype.st_mode)) {
73 DIR* directory = opendir(filename);
74 if (directory) {
75 while (struct dirent* entry = readdir(directory)) {
76 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
77 continue;
78 std::string entryName(filename);
79 entryName.append("/");
80 entryName.append(entry->d_name);
81 removeFileOrDirectory(entryName.c_str());
82 }
83 closedir(directory);
84 rmdir(filename);
85 }
86 return;
87 }
88 unlink(filename);
89 }
90
databaseDirectory(bool isPrivateBrowsing)91 static std::string databaseDirectory(bool isPrivateBrowsing)
92 {
93 static const char* const kDatabaseFilename = "/webviewCookiesChromium.db";
94 static const char* const kDatabaseFilenamePrivateBrowsing = "/webviewCookiesChromiumPrivate.db";
95
96 std::string databaseFilePath = databaseDirectory();
97 databaseFilePath.append(isPrivateBrowsing ? kDatabaseFilenamePrivateBrowsing : kDatabaseFilename);
98 return databaseFilePath;
99 }
100
instance(bool isPrivateBrowsing)101 static scoped_refptr<WebCookieJar>* instance(bool isPrivateBrowsing)
102 {
103 static scoped_refptr<WebCookieJar> regularInstance;
104 static scoped_refptr<WebCookieJar> privateInstance;
105 return isPrivateBrowsing ? &privateInstance : ®ularInstance;
106 }
107
get(bool isPrivateBrowsing)108 WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing)
109 {
110 MutexLocker lock(instanceMutex);
111 if (!isFirstInstanceCreated && fileSchemeCookiesEnabled)
112 net::CookieMonster::EnableFileScheme();
113 isFirstInstanceCreated = true;
114 scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing);
115 if (!instancePtr->get())
116 *instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing));
117 return instancePtr->get();
118 }
119
cleanup(bool isPrivateBrowsing)120 void WebCookieJar::cleanup(bool isPrivateBrowsing)
121 {
122 // This is called on the UI thread.
123 MutexLocker lock(instanceMutex);
124 scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing);
125 *instancePtr = 0;
126 removeFileOrDirectory(databaseDirectory(isPrivateBrowsing).c_str());
127 }
128
WebCookieJar(const std::string & databaseFilePath)129 WebCookieJar::WebCookieJar(const std::string& databaseFilePath)
130 : m_cookieStoreInitialized(false)
131 , m_databaseFilePath(databaseFilePath)
132 , m_allowCookies(true) {}
133
initCookieStore()134 void WebCookieJar::initCookieStore() {
135 MutexLocker lock(m_cookieStoreInitializeMutex);
136 if (m_cookieStoreInitialized)
137 return;
138 // Setup the permissions for the file
139 const char* cDatabasePath = m_databaseFilePath.c_str();
140 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
141 if (access(cDatabasePath, F_OK) == 0)
142 chmod(cDatabasePath, mode);
143 else {
144 int fd = open(cDatabasePath, O_CREAT, mode);
145 if (fd >= 0)
146 close(fd);
147 }
148
149 FilePath cookiePath(cDatabasePath);
150 m_cookieDb = new SQLitePersistentCookieStore(cookiePath);
151 m_cookieStore = new net::CookieMonster(m_cookieDb.get(), 0);
152 m_cookieStoreInitialized = true;
153 }
154
allowCookies()155 bool WebCookieJar::allowCookies()
156 {
157 MutexLocker lock(m_allowCookiesMutex);
158 return m_allowCookies;
159 }
160
setAllowCookies(bool allow)161 void WebCookieJar::setAllowCookies(bool allow)
162 {
163 MutexLocker lock(m_allowCookiesMutex);
164 m_allowCookies = allow;
165 }
166
167 // From CookiePolicy in chromium
CanGetCookies(const GURL &,const GURL &) const168 int WebCookieJar::CanGetCookies(const GURL&, const GURL&) const
169 {
170 MutexLocker lock(m_allowCookiesMutex);
171 return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED;
172 }
173
174 // From CookiePolicy in chromium
CanSetCookie(const GURL &,const GURL &,const std::string &) const175 int WebCookieJar::CanSetCookie(const GURL&, const GURL&, const std::string&) const
176 {
177 MutexLocker lock(m_allowCookiesMutex);
178 return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED;
179 }
180
cookieStore()181 net::CookieStore* WebCookieJar::cookieStore()
182 {
183 initCookieStore();
184 return m_cookieStore.get();
185 }
186
getNumCookiesInDatabase()187 int WebCookieJar::getNumCookiesInDatabase()
188 {
189 return cookieStore()->GetCookieMonster()->GetAllCookies().size();
190 }
191
192 class FlushSemaphore : public base::RefCountedThreadSafe<FlushSemaphore>
193 {
194 public:
FlushSemaphore()195 FlushSemaphore()
196 : m_condition(&m_lock)
197 , m_count(0)
198 {}
199
SendFlushRequest(net::CookieMonster * monster)200 void SendFlushRequest(net::CookieMonster* monster) {
201 // FlushStore() needs to run on a Chrome thread (because it will need
202 // to post the callback, and it may want to do so on its own thread.)
203 // We use the IO thread for this purpose.
204 //
205 // TODO(husky): Our threads are hidden away in various files. Clean this
206 // up and consider integrating with Chrome's browser_thread.h. Might be
207 // a better idea to use the DB thread here rather than the IO thread.
208
209 base::Thread* ioThread = WebUrlLoaderClient::ioThread();
210 if (ioThread) {
211 Task* callback = NewRunnableMethod(this, &FlushSemaphore::Callback);
212 ioThread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
213 monster, &net::CookieMonster::FlushStore, callback));
214 } else {
215 Callback();
216 }
217 }
218
219 // Block until the given number of callbacks has been made.
Wait(int numCallbacks)220 void Wait(int numCallbacks) {
221 base::AutoLock al(m_lock);
222 int lastCount = m_count;
223 while (m_count < numCallbacks) {
224 // TODO(husky): Maybe use TimedWait() here? But it's not obvious what
225 // to do if the flush fails. Might be okay just to let the OS kill us.
226 m_condition.Wait();
227 ASSERT(lastCount != m_count, "Wait finished without incrementing m_count %d %d", m_count, lastCount);
228 lastCount = m_count;
229 }
230 m_count -= numCallbacks;
231 }
232
233 private:
234 friend class base::RefCounted<FlushSemaphore>;
235
Callback()236 void Callback() {
237 base::AutoLock al(m_lock);
238 m_count++;
239 m_condition.Broadcast();
240 }
241
242 base::Lock m_lock;
243 base::ConditionVariable m_condition;
244 volatile int m_count;
245 };
246
flush()247 void WebCookieJar::flush()
248 {
249 // Flush both cookie stores (private and non-private), wait for 2 callbacks.
250 static scoped_refptr<FlushSemaphore> semaphore(new FlushSemaphore());
251 semaphore->SendFlushRequest(get(false)->cookieStore()->GetCookieMonster());
252 semaphore->SendFlushRequest(get(true)->cookieStore()->GetCookieMonster());
253 semaphore->Wait(2);
254 }
255
acceptFileSchemeCookies()256 bool WebCookieJar::acceptFileSchemeCookies()
257 {
258 MutexLocker lock(instanceMutex);
259 return fileSchemeCookiesEnabled;
260 }
261
setAcceptFileSchemeCookies(bool accept)262 void WebCookieJar::setAcceptFileSchemeCookies(bool accept)
263 {
264 // The Chromium HTTP stack only reflects changes to this flag when creating
265 // a new CookieMonster instance. While we could track whether any
266 // CookieMonster instances currently exist, this would be complicated and is
267 // not required, so we only allow this flag to be changed before the first
268 // instance is created.
269 MutexLocker lock(instanceMutex);
270 if (!isFirstInstanceCreated)
271 fileSchemeCookiesEnabled = accept;
272 }
273
274 }
275