1/*
2 * Copyright 2009, 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// must include config.h first for webkit to fiddle with new/delete
27#include "config.h"
28
29#include "ANPSystem_npapi.h"
30#include "Frame.h"
31#include "JavaSharedClient.h"
32#include "PluginClient.h"
33#include "PluginPackage.h"
34#include "PluginView.h"
35#include "PluginWidgetAndroid.h"
36#include "Settings.h"
37#include "SkString.h"
38#include "WebViewCore.h"
39#include <wtf/text/CString.h>
40
41#include <dirent.h>
42
43//#define PLUGIN_DEBUG_LOCAL // controls the printing of log messages
44#include "PluginDebugAndroid.h"
45
46static const char* gApplicationDataDir = NULL;
47static const char* gApplicationDataDirIncognito = NULL;
48
49using namespace android;
50
51static WebCore::PluginView* pluginViewForInstance(NPP instance) {
52    if (instance && instance->ndata)
53        return static_cast<WebCore::PluginView*>(instance->ndata);
54    return WebCore::PluginView::currentPluginView();
55}
56
57static const char* anp_getApplicationDataDirectory() {
58    if (NULL == gApplicationDataDir) {
59        PluginClient* client = JavaSharedClient::GetPluginClient();
60        if (!client)
61            return NULL;
62
63        WTF::String path = client->getPluginSharedDataDirectory();
64        int length = path.length();
65        if (length == 0)
66            return NULL;
67
68        char* storage = (char*) malloc(length + 1);
69        if (NULL == storage)
70            return NULL;
71
72        memcpy(storage, path.utf8().data(), length);
73        storage[length] = '\0';
74
75        static const char incognitoPath[] = "/incognito_plugins";
76        char* incognitoStorage = (char*) malloc(length + strlen(incognitoPath) + 1);
77
78        strcpy(incognitoStorage, storage);
79        strcat(incognitoStorage, incognitoPath);
80
81        // save this assignment for last, so that if multiple threads call us
82        // (which should never happen), we never return an incomplete global.
83        // At worst, we would allocate storage for the path twice.
84        gApplicationDataDir = storage;
85        gApplicationDataDirIncognito = incognitoStorage;
86    }
87
88    return gApplicationDataDir;
89}
90
91static const char* anp_getApplicationDataDirectoryV2(NPP instance) {
92    WebCore::PluginView* pluginView = pluginViewForInstance(instance);
93    PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
94
95    if (NULL == gApplicationDataDir) {
96        anp_getApplicationDataDirectory();
97    }
98
99    WebCore::Settings* settings = pluginWidget->webViewCore()->mainFrame()->settings();
100    if (settings && settings->privateBrowsingEnabled()) {
101        // if this is an incognito view then check the path to see if it exists
102        // and if it is a directory, otherwise if it does not exist create it.
103        struct stat st;
104        if (stat(gApplicationDataDirIncognito, &st) == 0) {
105            if (!S_ISDIR(st.st_mode)) {
106                return NULL;
107            }
108        } else {
109            if (mkdir(gApplicationDataDirIncognito, S_IRWXU) != 0) {
110                return NULL;
111            }
112        }
113
114        return gApplicationDataDirIncognito;
115    }
116
117    return gApplicationDataDir;
118}
119
120static jclass anp_loadJavaClass(NPP instance, const char* className) {
121    WebCore::PluginView* pluginView = pluginViewForInstance(instance);
122    PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
123
124    jclass result;
125    result = pluginWidget->webViewCore()->getPluginClass(pluginView->plugin()->path(),
126                                                         className);
127    return result;
128}
129
130static void anp_setPowerState(NPP instance, ANPPowerState powerState) {
131    WebCore::PluginView* pluginView = pluginViewForInstance(instance);
132    PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
133
134    pluginWidget->setPowerState(powerState);
135}
136
137///////////////////////////////////////////////////////////////////////////////
138
139#define ASSIGN(obj, name)   (obj)->name = anp_##name
140
141void ANPSystemInterfaceV0_Init(ANPInterface* v) {
142    ANPSystemInterfaceV0* i = reinterpret_cast<ANPSystemInterfaceV0*>(v);
143
144    ASSIGN(i, getApplicationDataDirectory);
145    ASSIGN(i, loadJavaClass);
146}
147
148void ANPSystemInterfaceV1_Init(ANPInterface* v) {
149    // initialize the functions from the previous interface
150    ANPSystemInterfaceV0_Init(v);
151    // add any new functions or override existing functions
152    ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(v);
153    ASSIGN(i, setPowerState);
154}
155
156void ANPSystemInterfaceV2_Init(ANPInterface* v) {
157    // initialize the functions from the previous interface
158    ANPSystemInterfaceV1_Init(v);
159    // add any new functions or override existing functions
160    ANPSystemInterfaceV2* i = reinterpret_cast<ANPSystemInterfaceV2*>(v);
161    i->getApplicationDataDirectory = anp_getApplicationDataDirectoryV2;
162}
163
164///////////////////////////////////////////////////////////////////////////////
165
166static bool isDirectory(const char* path) {
167    struct stat st;
168    return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
169}
170
171static void removeDirectory(const char* path) {
172    // create a pointer to a directory
173    DIR *dir = NULL;
174    dir = opendir(path);
175    if (!dir)
176        return;
177
178    struct dirent* entry = 0;
179    while ((entry = readdir(dir))) { // while there is still something in the directory to list
180        if (!entry)
181            return;
182
183        if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) {
184            PLUGIN_LOG(". file: %s", entry->d_name);
185            continue;
186        }
187
188        // concatenate the strings to get the complete path
189        static const char separator[] = "/";
190        char* file = (char*) malloc(strlen(path) + strlen(separator) + strlen(entry->d_name) + 1);
191        strcpy(file, path);
192        strcat(file, separator);
193        strcat(file, entry->d_name);
194
195        if (isDirectory(file) == true) {
196            PLUGIN_LOG("remove dir: %s", file);
197            removeDirectory(file);
198        } else { // it's a file, we can use remove
199            PLUGIN_LOG("remove file: %s", file);
200            remove(file);
201        }
202
203        free(file);
204    }
205
206    // clean up
207    closedir (dir); // close the directory
208    rmdir(path); // delete the directory
209}
210
211void ANPSystemInterface_CleanupIncognito() {
212    PLUGIN_LOG("cleanup incognito plugin directory");
213
214    if (gApplicationDataDirIncognito == NULL)
215        anp_getApplicationDataDirectory();
216    if (gApplicationDataDirIncognito == NULL)
217        return;
218
219    // check to see if the directory exists and if so delete it
220    if (isDirectory(gApplicationDataDirIncognito))
221        removeDirectory(gApplicationDataDirIncognito);
222}
223