Home | History | Annotate | Download | only in WebCoreSupport
      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 : &regularInstance;
    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