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 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_allowCookies(true) 131 { 132 // Setup the permissions for the file 133 const char* cDatabasePath = databaseFilePath.c_str(); 134 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; 135 if (access(cDatabasePath, F_OK) == 0) 136 chmod(cDatabasePath, mode); 137 else { 138 int fd = open(cDatabasePath, O_CREAT, mode); 139 if (fd >= 0) 140 close(fd); 141 } 142 143 FilePath cookiePath(databaseFilePath.c_str()); 144 m_cookieDb = new SQLitePersistentCookieStore(cookiePath); 145 m_cookieStore = new net::CookieMonster(m_cookieDb.get(), 0); 146 } 147 148 bool WebCookieJar::allowCookies() 149 { 150 MutexLocker lock(m_allowCookiesMutex); 151 return m_allowCookies; 152 } 153 154 void WebCookieJar::setAllowCookies(bool allow) 155 { 156 MutexLocker lock(m_allowCookiesMutex); 157 m_allowCookies = allow; 158 } 159 160 int WebCookieJar::getNumCookiesInDatabase() 161 { 162 if (!m_cookieStore) 163 return 0; 164 return m_cookieStore->GetCookieMonster()->GetAllCookies().size(); 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 class FlushSemaphore : public base::RefCounted<FlushSemaphore> 182 { 183 public: 184 FlushSemaphore() 185 : m_condition(&m_lock) 186 , m_count(0) 187 {} 188 189 void SendFlushRequest(net::CookieMonster* monster) { 190 // FlushStore() needs to run on a Chrome thread (because it will need 191 // to post the callback, and it may want to do so on its own thread.) 192 // We use the IO thread for this purpose. 193 // 194 // TODO(husky): Our threads are hidden away in various files. Clean this 195 // up and consider integrating with Chrome's browser_thread.h. Might be 196 // a better idea to use the DB thread here rather than the IO thread. 197 198 base::Thread* ioThread = WebUrlLoaderClient::ioThread(); 199 if (ioThread) { 200 Task* callback = NewRunnableMethod(this, &FlushSemaphore::Callback); 201 ioThread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 202 monster, &net::CookieMonster::FlushStore, callback)); 203 } else { 204 Callback(); 205 } 206 } 207 208 // Block until the given number of callbacks has been made. 209 void Wait(int numCallbacks) { 210 base::AutoLock al(m_lock); 211 int lastCount = m_count; 212 while (m_count < numCallbacks) { 213 // TODO(husky): Maybe use TimedWait() here? But it's not obvious what 214 // to do if the flush fails. Might be okay just to let the OS kill us. 215 m_condition.Wait(); 216 ASSERT(lastCount != m_count, "Wait finished without incrementing m_count %d %d", m_count, lastCount); 217 lastCount = m_count; 218 } 219 m_count -= numCallbacks; 220 } 221 222 private: 223 friend class base::RefCounted<FlushSemaphore>; 224 225 void Callback() { 226 base::AutoLock al(m_lock); 227 m_count++; 228 m_condition.Broadcast(); 229 } 230 231 base::Lock m_lock; 232 base::ConditionVariable m_condition; 233 volatile int m_count; 234 }; 235 236 void WebCookieJar::flush() 237 { 238 // Flush both cookie stores (private and non-private), wait for 2 callbacks. 239 static scoped_refptr<FlushSemaphore> semaphore(new FlushSemaphore()); 240 semaphore->SendFlushRequest(get(false)->cookieStore()->GetCookieMonster()); 241 semaphore->SendFlushRequest(get(true)->cookieStore()->GetCookieMonster()); 242 semaphore->Wait(2); 243 } 244 245 bool WebCookieJar::acceptFileSchemeCookies() 246 { 247 MutexLocker lock(instanceMutex); 248 return fileSchemeCookiesEnabled; 249 } 250 251 void WebCookieJar::setAcceptFileSchemeCookies(bool accept) 252 { 253 // The Chromium HTTP stack only reflects changes to this flag when creating 254 // a new CookieMonster instance. While we could track whether any 255 // CookieMonster instances currently exist, this would be complicated and is 256 // not required, so we only allow this flag to be changed before the first 257 // instance is created. 258 MutexLocker lock(instanceMutex); 259 if (!isFirstInstanceCreated) 260 fileSchemeCookiesEnabled = accept; 261 } 262 263 } 264