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 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_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