text: // 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;
  }
  