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 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 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 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 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 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 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 129 WebCookieJar::WebCookieJar(const std::string& databaseFilePath) 130 : m_cookieStoreInitialized(false) 131 , m_databaseFilePath(databaseFilePath) 132 , m_allowCookies(true) {} 133 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 155 bool WebCookieJar::allowCookies() 156 { 157 MutexLocker lock(m_allowCookiesMutex); 158 return m_allowCookies; 159 } 160 161 void WebCookieJar::setAllowCookies(bool allow) 162 { 163 MutexLocker lock(m_allowCookiesMutex); 164 m_allowCookies = allow; 165 } 166 167 // From CookiePolicy in chromium 168 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 175 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 181 net::CookieStore* WebCookieJar::cookieStore() 182 { 183 initCookieStore(); 184 return m_cookieStore.get(); 185 } 186 187 int WebCookieJar::getNumCookiesInDatabase() 188 { 189 return cookieStore()->GetCookieMonster()->GetAllCookies().size(); 190 } 191 192 class FlushSemaphore : public base::RefCountedThreadSafe<FlushSemaphore> 193 { 194 public: 195 FlushSemaphore() 196 : m_condition(&m_lock) 197 , m_count(0) 198 {} 199 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. 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 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 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 256 bool WebCookieJar::acceptFileSchemeCookies() 257 { 258 MutexLocker lock(instanceMutex); 259 return fileSchemeCookiesEnabled; 260 } 261 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