• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 : &regularInstance;
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