blob: d265931964fe20f882563cbda265cece1d6716ba [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// For WinDDK ATL compatibility, these ATL headers must come first.
#include <atlbase.h>
#include <atlwin.h>
#include "chrome/default_plugin/plugin_database_handler.h"
#include "libxml/parser.h"
#include "libxml/xpath.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/default_plugin/plugin_impl.h"
#include "chrome/default_plugin/plugin_main.h"
using base::Time;
using base::TimeDelta;
PluginDatabaseHandler::PluginDatabaseHandler(
PluginInstallerImpl& plugin_installer_instance)
: plugin_downloads_file_(INVALID_HANDLE_VALUE),
plugin_installer_instance_(plugin_installer_instance),
ignore_plugin_db_data_(false) {
}
PluginDatabaseHandler::~PluginDatabaseHandler() {
if (plugin_downloads_file_ != INVALID_HANDLE_VALUE) {
::CloseHandle(plugin_downloads_file_);
plugin_downloads_file_ = INVALID_HANDLE_VALUE;
}
}
bool PluginDatabaseHandler::DownloadPluginsFileIfNeeded(
const std::string& plugin_finder_url) {
DCHECK(!plugin_finder_url.empty());
// The time in days for which the plugins list is cached.
// TODO(iyengar) Make this configurable.
const int kPluginsListCacheTimeInDays = 3;
plugin_finder_url_ = plugin_finder_url;
FilePath module_path;
PathService::Get(base::DIR_MODULE, &module_path);
plugins_file_ = module_path.Append(L"chrome_plugins_file.xml").value();
bool initiate_download = false;
if (!file_util::PathExists(FilePath(plugins_file_))) {
initiate_download = true;
} else {
SYSTEMTIME creation_system_time = {0};
if (!file_util::GetFileCreationLocalTime(plugins_file_,
&creation_system_time)) {
NOTREACHED();
return false;
}
FILETIME creation_file_time = {0};
::SystemTimeToFileTime(&creation_system_time, &creation_file_time);
FILETIME current_time = {0};
::GetSystemTimeAsFileTime(&current_time);
Time file_time = Time::FromFileTime(creation_file_time);
Time current_system_time = Time::FromFileTime(current_time);
TimeDelta time_diff = file_time - current_system_time;
if (time_diff.InDays() > kPluginsListCacheTimeInDays) {
initiate_download = true;
}
}
if (initiate_download) {
DVLOG(1) << "Initiating GetURLNotify on the plugin finder URL "
<< plugin_finder_url.c_str();
plugin_installer_instance_.set_plugin_installer_state(
PluginListDownloadInitiated);
DCHECK(default_plugin::g_browser->geturlnotify);
default_plugin::g_browser->geturlnotify(
plugin_installer_instance_.instance(), plugin_finder_url.c_str(),
NULL, NULL);
} else {
DVLOG(1) << "Plugins file " << plugins_file_ << " already exists";
plugin_downloads_file_ = ::CreateFile(plugins_file_.c_str(), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) {
DVLOG(1) << "Failed to open plugins file " << plugins_file_
<< " Error " << ::GetLastError();
NOTREACHED();
return false;
}
// The URLNotify function contains all handling needed to parse the plugins
// file and display the UI accordingly.
plugin_installer_instance_.set_plugin_installer_state(
PluginListDownloadInitiated);
plugin_installer_instance_.URLNotify(plugin_finder_url.c_str(),
NPRES_DONE);
}
return true;
}
int32 PluginDatabaseHandler::Write(NPStream* stream, int32 offset,
int32 buffer_length, void* buffer) {
if (ignore_plugin_db_data_) {
return buffer_length;
}
if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) {
DVLOG(1) << "About to create plugins file " << plugins_file_;
plugin_downloads_file_ = CreateFile(plugins_file_.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) {
DWORD error = ::GetLastError();
if (error == ERROR_SHARING_VIOLATION) {
// File may have been downloaded by another plugin instance on this
// page.
plugin_downloads_file_ = ::CreateFile(
plugins_file_.c_str(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (plugin_downloads_file_ != INVALID_HANDLE_VALUE) {
ignore_plugin_db_data_ = true;
return buffer_length;
}
}
DLOG(ERROR) << "Failed to create plugins file " << plugins_file_
<< " Error " << ::GetLastError();
return 0;
}
}
DWORD bytes_written = 0;
if (0 == lstrcmpiA(stream->url, plugin_finder_url_.c_str())) {
DCHECK(plugin_downloads_file_ != INVALID_HANDLE_VALUE);
WriteFile(plugin_downloads_file_, buffer, buffer_length, &bytes_written,
NULL);
DCHECK_EQ(buffer_length, static_cast<int32>(bytes_written));
}
return bytes_written;
}
bool PluginDatabaseHandler::ParsePluginList() {
if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) {
DLOG(WARNING) << "Invalid plugins file";
NOTREACHED();
return false;
}
bool parse_result = false;
std::string plugins_file = WideToUTF8(plugins_file_.c_str());
xmlDocPtr plugin_downloads_doc = xmlParseFile(plugins_file.c_str());
if (plugin_downloads_doc == NULL) {
DLOG(WARNING) << "Failed to parse plugins file " << plugins_file.c_str();
return parse_result;
}
xmlXPathContextPtr context = NULL;
xmlXPathObjectPtr plugins_result = NULL;
do {
context = xmlXPathNewContext(plugin_downloads_doc);
if (context == NULL) {
DLOG(WARNING) << "Failed to retrieve XPath context";
NOTREACHED();
parse_result = false;
break;
}
plugins_result =
xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>("//plugin"),
context);
if ((plugins_result == NULL) ||
xmlXPathNodeSetIsEmpty(plugins_result->nodesetval)) {
DLOG(WARNING) << "Failed to find XPath //plugin";
NOTREACHED();
parse_result = false;
break;
}
xmlNodeSetPtr plugin_list = plugins_result->nodesetval;
for (int plugin_index = 0; plugin_index < plugin_list->nodeNr;
++plugin_index) {
PluginDetail plugin_detail;
if (!ReadPluginInfo(plugin_list->nodeTab[plugin_index]->children,
&plugin_detail)) {
DLOG(ERROR) << "Failed to read plugin details at index "
<< plugin_index;
break;
}
downloaded_plugins_list_.push_back(plugin_detail);
}
if (downloaded_plugins_list_.size())
parse_result = true;
} while (0);
xmlXPathFreeContext(context);
xmlXPathFreeObject(plugins_result);
xmlFreeDoc(plugin_downloads_doc);
xmlCleanupParser();
DVLOG(1) << "Parse plugins file result " << parse_result;
return parse_result;
}
bool PluginDatabaseHandler::GetPluginDetailsForMimeType(
const char* mime_type, const char* language,
std::string* download_url, std::wstring* plugin_name,
bool* download_url_for_display) {
if (!mime_type || !language || !download_url || !plugin_name ||
!download_url_for_display) {
NOTREACHED();
return false;
}
PluginList::iterator plugin_index;
for (plugin_index = downloaded_plugins_list_.begin();
plugin_index != downloaded_plugins_list_.end();
++plugin_index) {
PluginDetail& current_plugin = *plugin_index;
std::vector<std::string>::iterator mime_type_index;
for (mime_type_index = current_plugin.mime_types.begin();
mime_type_index != current_plugin.mime_types.end();
++mime_type_index) {
if ((0 == lstrcmpiA(mime_type, (*mime_type_index).c_str())) &&
(0 == lstrcmpiA(language, current_plugin.language.c_str()))) {
*download_url = current_plugin.download_url;
*plugin_name = current_plugin.display_name;
*download_url_for_display = current_plugin.download_url_for_display;
return true;
}
}
}
return false;
}
void PluginDatabaseHandler::Close(bool delete_file) {
if (plugin_downloads_file_ != INVALID_HANDLE_VALUE) {
::CloseHandle(plugin_downloads_file_);
plugin_downloads_file_ = INVALID_HANDLE_VALUE;
if (delete_file) {
::DeleteFile(plugins_file_.c_str());
plugins_file_.clear();
}
}
}
bool PluginDatabaseHandler::ReadPluginInfo(_xmlNode* plugin_node,
PluginDetail* plugin_detail) {
static const char kMimeTypeSeparator = ';';
if (!plugin_node) {
NOTREACHED();
return false;
}
_xmlNode* plugin_mime_type = plugin_node->next;
if ((plugin_mime_type == NULL) ||
(plugin_mime_type->next == NULL)) {
DLOG(WARNING) << "Failed to find mime type node in file";
NOTREACHED();
return false;
}
_xmlNode* plugin_mime_type_vals = plugin_mime_type->children;
if (plugin_mime_type_vals == NULL) {
DLOG(WARNING) << "Invalid mime type";
NOTREACHED();
return false;
}
// Skip the first child of each node as it is the text element
// for that node.
_xmlNode* plugin_lang_node = plugin_mime_type->next->next;
if ((plugin_lang_node == NULL) ||
(plugin_lang_node->next == NULL)) {
DLOG(WARNING) << "Failed to find plugin language node";
NOTREACHED();
return false;
}
_xmlNode* plugin_lang_val = plugin_lang_node->children;
if (plugin_lang_val == NULL) {
DLOG(WARNING) << "Invalid plugin language";
NOTREACHED();
return false;
}
_xmlNode* plugin_name_node = plugin_lang_node->next->next;
if ((plugin_name_node == NULL) ||
(plugin_name_node->next == NULL)) {
DLOG(WARNING) << "Failed to find plugin name node";
NOTREACHED();
return false;
}
_xmlNode* plugin_name_val = plugin_name_node->children;
if (plugin_name_val == NULL) {
DLOG(WARNING) << "Invalid plugin name";
NOTREACHED();
return false;
}
_xmlNode* plugin_download_url_node = plugin_name_node->next->next;
if (plugin_download_url_node == NULL) {
DLOG(WARNING) << "Failed to find plugin URL node";
NOTREACHED();
return false;
}
_xmlNode* plugin_download_url_val = plugin_download_url_node->children;
if (plugin_download_url_val == NULL) {
DLOG(WARNING) << "Invalid plugin URL";
NOTREACHED();
return false;
}
// Look for the DisplayUrl node. By default every URL is treated as an
// executable URL.
plugin_detail->download_url_for_display = false;
_xmlNode* plugin_download_url_for_display_node =
plugin_download_url_node->next->next;
if (plugin_download_url_for_display_node) {
_xmlNode* url_for_display_val =
plugin_download_url_for_display_node->children;
if (url_for_display_val) {
int url_for_display = 0;
base::StringToInt(
reinterpret_cast<const char*>(url_for_display_val->content),
&url_for_display);
if (url_for_display != 0)
plugin_detail->download_url_for_display = true;
}
}
plugin_detail->display_name =
UTF8ToWide(reinterpret_cast<const char*>(plugin_name_val->content));
plugin_detail->download_url =
reinterpret_cast<const char*>(plugin_download_url_val->content);
base::SplitString(
reinterpret_cast<const char*>(plugin_mime_type_vals->content),
kMimeTypeSeparator, &plugin_detail->mime_types);
plugin_detail->language =
reinterpret_cast<const char*>(plugin_lang_val->content);
return true;
}