// 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 #include #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(¤t_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(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("//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::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(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(plugin_name_val->content)); plugin_detail->download_url = reinterpret_cast(plugin_download_url_val->content); base::SplitString( reinterpret_cast(plugin_mime_type_vals->content), kMimeTypeSeparator, &plugin_detail->mime_types); plugin_detail->language = reinterpret_cast(plugin_lang_val->content); return true; }