| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // For WinDDK ATL compatibility, these ATL headers must come first. | |
| 6 #include <atlbase.h> | |
| 7 #include <atlwin.h> | |
| 8 | |
| 9 #include "chrome/default_plugin/plugin_database_handler.h" | |
| 10 | |
| 11 #include "libxml/parser.h" | |
| 12 #include "libxml/xpath.h" | |
| 13 | |
| 14 #include "base/file_util.h" | |
| 15 #include "base/path_service.h" | |
| 16 #include "base/string_number_conversions.h" | |
| 17 #include "base/string_split.h" | |
| 18 #include "base/string_util.h" | |
| 19 #include "base/time.h" | |
| 20 #include "base/utf_string_conversions.h" | |
| 21 #include "chrome/default_plugin/plugin_impl.h" | |
| 22 #include "chrome/default_plugin/plugin_main.h" | |
| 23 | |
| 24 using base::Time; | |
| 25 using base::TimeDelta; | |
| 26 | |
| 27 PluginDatabaseHandler::PluginDatabaseHandler( | |
| 28 PluginInstallerImpl& plugin_installer_instance) | |
| 29 : plugin_downloads_file_(INVALID_HANDLE_VALUE), | |
| 30 plugin_installer_instance_(plugin_installer_instance), | |
| 31 ignore_plugin_db_data_(false) { | |
| 32 } | |
| 33 | |
| 34 PluginDatabaseHandler::~PluginDatabaseHandler() { | |
| 35 if (plugin_downloads_file_ != INVALID_HANDLE_VALUE) { | |
| 36 ::CloseHandle(plugin_downloads_file_); | |
| 37 plugin_downloads_file_ = INVALID_HANDLE_VALUE; | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 bool PluginDatabaseHandler::DownloadPluginsFileIfNeeded( | |
| 42 const std::string& plugin_finder_url) { | |
| 43 DCHECK(!plugin_finder_url.empty()); | |
| 44 // The time in days for which the plugins list is cached. | |
| 45 // TODO(iyengar) Make this configurable. | |
| 46 const int kPluginsListCacheTimeInDays = 3; | |
| 47 | |
| 48 plugin_finder_url_ = plugin_finder_url; | |
| 49 | |
| 50 FilePath module_path; | |
| 51 PathService::Get(base::DIR_MODULE, &module_path); | |
| 52 plugins_file_ = module_path.Append(L"chrome_plugins_file.xml").value(); | |
| 53 | |
| 54 bool initiate_download = false; | |
| 55 if (!file_util::PathExists(FilePath(plugins_file_))) { | |
| 56 initiate_download = true; | |
| 57 } else { | |
| 58 SYSTEMTIME creation_system_time = {0}; | |
| 59 if (!file_util::GetFileCreationLocalTime(plugins_file_, | |
| 60 &creation_system_time)) { | |
| 61 NOTREACHED(); | |
| 62 return false; | |
| 63 } | |
| 64 | |
| 65 FILETIME creation_file_time = {0}; | |
| 66 ::SystemTimeToFileTime(&creation_system_time, &creation_file_time); | |
| 67 | |
| 68 FILETIME current_time = {0}; | |
| 69 ::GetSystemTimeAsFileTime(¤t_time); | |
| 70 | |
| 71 Time file_time = Time::FromFileTime(creation_file_time); | |
| 72 Time current_system_time = Time::FromFileTime(current_time); | |
| 73 | |
| 74 TimeDelta time_diff = file_time - current_system_time; | |
| 75 if (time_diff.InDays() > kPluginsListCacheTimeInDays) { | |
| 76 initiate_download = true; | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 if (initiate_download) { | |
| 81 DVLOG(1) << "Initiating GetURLNotify on the plugin finder URL " | |
| 82 << plugin_finder_url.c_str(); | |
| 83 | |
| 84 plugin_installer_instance_.set_plugin_installer_state( | |
| 85 PluginListDownloadInitiated); | |
| 86 | |
| 87 DCHECK(default_plugin::g_browser->geturlnotify); | |
| 88 default_plugin::g_browser->geturlnotify( | |
| 89 plugin_installer_instance_.instance(), plugin_finder_url.c_str(), | |
| 90 NULL, NULL); | |
| 91 } else { | |
| 92 DVLOG(1) << "Plugins file " << plugins_file_ << " already exists"; | |
| 93 plugin_downloads_file_ = ::CreateFile(plugins_file_.c_str(), GENERIC_READ, | |
| 94 FILE_SHARE_READ, NULL, OPEN_EXISTING, | |
| 95 FILE_ATTRIBUTE_NORMAL, NULL); | |
| 96 if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) { | |
| 97 DVLOG(1) << "Failed to open plugins file " << plugins_file_ | |
| 98 << " Error " << ::GetLastError(); | |
| 99 NOTREACHED(); | |
| 100 return false; | |
| 101 } | |
| 102 // The URLNotify function contains all handling needed to parse the plugins | |
| 103 // file and display the UI accordingly. | |
| 104 plugin_installer_instance_.set_plugin_installer_state( | |
| 105 PluginListDownloadInitiated); | |
| 106 plugin_installer_instance_.URLNotify(plugin_finder_url.c_str(), | |
| 107 NPRES_DONE); | |
| 108 } | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 int32 PluginDatabaseHandler::Write(NPStream* stream, int32 offset, | |
| 113 int32 buffer_length, void* buffer) { | |
| 114 if (ignore_plugin_db_data_) { | |
| 115 return buffer_length; | |
| 116 } | |
| 117 | |
| 118 if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) { | |
| 119 DVLOG(1) << "About to create plugins file " << plugins_file_; | |
| 120 plugin_downloads_file_ = CreateFile(plugins_file_.c_str(), | |
| 121 GENERIC_READ | GENERIC_WRITE, | |
| 122 FILE_SHARE_READ, NULL, CREATE_ALWAYS, | |
| 123 FILE_ATTRIBUTE_NORMAL, NULL); | |
| 124 if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) { | |
| 125 DWORD error = ::GetLastError(); | |
| 126 if (error == ERROR_SHARING_VIOLATION) { | |
| 127 // File may have been downloaded by another plugin instance on this | |
| 128 // page. | |
| 129 plugin_downloads_file_ = ::CreateFile( | |
| 130 plugins_file_.c_str(), GENERIC_READ, | |
| 131 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | |
| 132 FILE_ATTRIBUTE_NORMAL, NULL); | |
| 133 if (plugin_downloads_file_ != INVALID_HANDLE_VALUE) { | |
| 134 ignore_plugin_db_data_ = true; | |
| 135 return buffer_length; | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 DLOG(ERROR) << "Failed to create plugins file " << plugins_file_ | |
| 140 << " Error " << ::GetLastError(); | |
| 141 return 0; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 DWORD bytes_written = 0; | |
| 146 if (0 == lstrcmpiA(stream->url, plugin_finder_url_.c_str())) { | |
| 147 DCHECK(plugin_downloads_file_ != INVALID_HANDLE_VALUE); | |
| 148 | |
| 149 WriteFile(plugin_downloads_file_, buffer, buffer_length, &bytes_written, | |
| 150 NULL); | |
| 151 DCHECK_EQ(buffer_length, static_cast<int32>(bytes_written)); | |
| 152 } | |
| 153 return bytes_written; | |
| 154 } | |
| 155 | |
| 156 | |
| 157 bool PluginDatabaseHandler::ParsePluginList() { | |
| 158 if (plugin_downloads_file_ == INVALID_HANDLE_VALUE) { | |
| 159 DLOG(WARNING) << "Invalid plugins file"; | |
| 160 NOTREACHED(); | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 bool parse_result = false; | |
| 165 | |
| 166 std::string plugins_file = WideToUTF8(plugins_file_.c_str()); | |
| 167 xmlDocPtr plugin_downloads_doc = xmlParseFile(plugins_file.c_str()); | |
| 168 if (plugin_downloads_doc == NULL) { | |
| 169 DLOG(WARNING) << "Failed to parse plugins file " << plugins_file.c_str(); | |
| 170 return parse_result; | |
| 171 } | |
| 172 | |
| 173 xmlXPathContextPtr context = NULL; | |
| 174 xmlXPathObjectPtr plugins_result = NULL; | |
| 175 | |
| 176 do { | |
| 177 context = xmlXPathNewContext(plugin_downloads_doc); | |
| 178 if (context == NULL) { | |
| 179 DLOG(WARNING) << "Failed to retrieve XPath context"; | |
| 180 NOTREACHED(); | |
| 181 parse_result = false; | |
| 182 break; | |
| 183 } | |
| 184 | |
| 185 plugins_result = | |
| 186 xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>("//plugin"), | |
| 187 context); | |
| 188 if ((plugins_result == NULL) || | |
| 189 xmlXPathNodeSetIsEmpty(plugins_result->nodesetval)) { | |
| 190 DLOG(WARNING) << "Failed to find XPath //plugin"; | |
| 191 NOTREACHED(); | |
| 192 parse_result = false; | |
| 193 break; | |
| 194 } | |
| 195 | |
| 196 xmlNodeSetPtr plugin_list = plugins_result->nodesetval; | |
| 197 for (int plugin_index = 0; plugin_index < plugin_list->nodeNr; | |
| 198 ++plugin_index) { | |
| 199 PluginDetail plugin_detail; | |
| 200 if (!ReadPluginInfo(plugin_list->nodeTab[plugin_index]->children, | |
| 201 &plugin_detail)) { | |
| 202 DLOG(ERROR) << "Failed to read plugin details at index " | |
| 203 << plugin_index; | |
| 204 break; | |
| 205 } | |
| 206 downloaded_plugins_list_.push_back(plugin_detail); | |
| 207 } | |
| 208 if (downloaded_plugins_list_.size()) | |
| 209 parse_result = true; | |
| 210 } while (0); | |
| 211 | |
| 212 xmlXPathFreeContext(context); | |
| 213 xmlXPathFreeObject(plugins_result); | |
| 214 xmlFreeDoc(plugin_downloads_doc); | |
| 215 xmlCleanupParser(); | |
| 216 DVLOG(1) << "Parse plugins file result " << parse_result; | |
| 217 return parse_result; | |
| 218 } | |
| 219 | |
| 220 bool PluginDatabaseHandler::GetPluginDetailsForMimeType( | |
| 221 const char* mime_type, const char* language, | |
| 222 std::string* download_url, std::wstring* plugin_name, | |
| 223 bool* download_url_for_display) { | |
| 224 if (!mime_type || !language || !download_url || !plugin_name || | |
| 225 !download_url_for_display) { | |
| 226 NOTREACHED(); | |
| 227 return false; | |
| 228 } | |
| 229 | |
| 230 PluginList::iterator plugin_index; | |
| 231 for (plugin_index = downloaded_plugins_list_.begin(); | |
| 232 plugin_index != downloaded_plugins_list_.end(); | |
| 233 ++plugin_index) { | |
| 234 PluginDetail& current_plugin = *plugin_index; | |
| 235 | |
| 236 std::vector<std::string>::iterator mime_type_index; | |
| 237 for (mime_type_index = current_plugin.mime_types.begin(); | |
| 238 mime_type_index != current_plugin.mime_types.end(); | |
| 239 ++mime_type_index) { | |
| 240 if ((0 == lstrcmpiA(mime_type, (*mime_type_index).c_str())) && | |
| 241 (0 == lstrcmpiA(language, current_plugin.language.c_str()))) { | |
| 242 *download_url = current_plugin.download_url; | |
| 243 *plugin_name = current_plugin.display_name; | |
| 244 *download_url_for_display = current_plugin.download_url_for_display; | |
| 245 return true; | |
| 246 } | |
| 247 } | |
| 248 } | |
| 249 return false; | |
| 250 } | |
| 251 | |
| 252 void PluginDatabaseHandler::Close(bool delete_file) { | |
| 253 if (plugin_downloads_file_ != INVALID_HANDLE_VALUE) { | |
| 254 ::CloseHandle(plugin_downloads_file_); | |
| 255 plugin_downloads_file_ = INVALID_HANDLE_VALUE; | |
| 256 if (delete_file) { | |
| 257 ::DeleteFile(plugins_file_.c_str()); | |
| 258 plugins_file_.clear(); | |
| 259 } | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 bool PluginDatabaseHandler::ReadPluginInfo(_xmlNode* plugin_node, | |
| 264 PluginDetail* plugin_detail) { | |
| 265 static const char kMimeTypeSeparator = ';'; | |
| 266 | |
| 267 if (!plugin_node) { | |
| 268 NOTREACHED(); | |
| 269 return false; | |
| 270 } | |
| 271 | |
| 272 _xmlNode* plugin_mime_type = plugin_node->next; | |
| 273 if ((plugin_mime_type == NULL) || | |
| 274 (plugin_mime_type->next == NULL)) { | |
| 275 DLOG(WARNING) << "Failed to find mime type node in file"; | |
| 276 NOTREACHED(); | |
| 277 return false; | |
| 278 } | |
| 279 _xmlNode* plugin_mime_type_vals = plugin_mime_type->children; | |
| 280 if (plugin_mime_type_vals == NULL) { | |
| 281 DLOG(WARNING) << "Invalid mime type"; | |
| 282 NOTREACHED(); | |
| 283 return false; | |
| 284 } | |
| 285 // Skip the first child of each node as it is the text element | |
| 286 // for that node. | |
| 287 _xmlNode* plugin_lang_node = plugin_mime_type->next->next; | |
| 288 if ((plugin_lang_node == NULL) || | |
| 289 (plugin_lang_node->next == NULL)) { | |
| 290 DLOG(WARNING) << "Failed to find plugin language node"; | |
| 291 NOTREACHED(); | |
| 292 return false; | |
| 293 } | |
| 294 _xmlNode* plugin_lang_val = plugin_lang_node->children; | |
| 295 if (plugin_lang_val == NULL) { | |
| 296 DLOG(WARNING) << "Invalid plugin language"; | |
| 297 NOTREACHED(); | |
| 298 return false; | |
| 299 } | |
| 300 _xmlNode* plugin_name_node = plugin_lang_node->next->next; | |
| 301 if ((plugin_name_node == NULL) || | |
| 302 (plugin_name_node->next == NULL)) { | |
| 303 DLOG(WARNING) << "Failed to find plugin name node"; | |
| 304 NOTREACHED(); | |
| 305 return false; | |
| 306 } | |
| 307 _xmlNode* plugin_name_val = plugin_name_node->children; | |
| 308 if (plugin_name_val == NULL) { | |
| 309 DLOG(WARNING) << "Invalid plugin name"; | |
| 310 NOTREACHED(); | |
| 311 return false; | |
| 312 } | |
| 313 _xmlNode* plugin_download_url_node = plugin_name_node->next->next; | |
| 314 if (plugin_download_url_node == NULL) { | |
| 315 DLOG(WARNING) << "Failed to find plugin URL node"; | |
| 316 NOTREACHED(); | |
| 317 return false; | |
| 318 } | |
| 319 _xmlNode* plugin_download_url_val = plugin_download_url_node->children; | |
| 320 if (plugin_download_url_val == NULL) { | |
| 321 DLOG(WARNING) << "Invalid plugin URL"; | |
| 322 NOTREACHED(); | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 // Look for the DisplayUrl node. By default every URL is treated as an | |
| 327 // executable URL. | |
| 328 plugin_detail->download_url_for_display = false; | |
| 329 | |
| 330 _xmlNode* plugin_download_url_for_display_node = | |
| 331 plugin_download_url_node->next->next; | |
| 332 if (plugin_download_url_for_display_node) { | |
| 333 _xmlNode* url_for_display_val = | |
| 334 plugin_download_url_for_display_node->children; | |
| 335 if (url_for_display_val) { | |
| 336 int url_for_display = 0; | |
| 337 base::StringToInt( | |
| 338 reinterpret_cast<const char*>(url_for_display_val->content), | |
| 339 &url_for_display); | |
| 340 if (url_for_display != 0) | |
| 341 plugin_detail->download_url_for_display = true; | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 plugin_detail->display_name = | |
| 346 UTF8ToWide(reinterpret_cast<const char*>(plugin_name_val->content)); | |
| 347 plugin_detail->download_url = | |
| 348 reinterpret_cast<const char*>(plugin_download_url_val->content); | |
| 349 | |
| 350 base::SplitString( | |
| 351 reinterpret_cast<const char*>(plugin_mime_type_vals->content), | |
| 352 kMimeTypeSeparator, &plugin_detail->mime_types); | |
| 353 | |
| 354 plugin_detail->language = | |
| 355 reinterpret_cast<const char*>(plugin_lang_val->content); | |
| 356 | |
| 357 return true; | |
| 358 } | |
| OLD | NEW |