1// Copyright (c) 2012 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#include "chrome/browser/plugins/plugin_finder.h"
6
7#include "base/bind.h"
8#include "base/json/json_reader.h"
9#include "base/message_loop/message_loop.h"
10#include "base/prefs/pref_registry_simple.h"
11#include "base/prefs/pref_service.h"
12#include "base/stl_util.h"
13#include "base/strings/sys_string_conversions.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/values.h"
16#include "chrome/browser/browser_process.h"
17#include "chrome/browser/plugins/plugin_metadata.h"
18#include "chrome/common/pref_names.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/browser/plugin_service.h"
21#include "grit/browser_resources.h"
22#include "ui/base/resource/resource_bundle.h"
23#include "url/gurl.h"
24
25#if defined(ENABLE_PLUGIN_INSTALLATION)
26#include "chrome/browser/plugins/plugin_installer.h"
27#endif
28
29using base::DictionaryValue;
30using content::PluginService;
31
32namespace {
33
34typedef std::map<std::string, PluginMetadata*> PluginMap;
35
36// Gets the full path of the plug-in file as the identifier.
37std::string GetLongIdentifier(const content::WebPluginInfo& plugin) {
38  return plugin.path.AsUTF8Unsafe();
39}
40
41// Gets the base name of the file path as the identifier.
42std::string GetIdentifier(const content::WebPluginInfo& plugin) {
43  return plugin.path.BaseName().AsUTF8Unsafe();
44}
45
46// Gets the plug-in group name as the plug-in name if it is not empty or
47// the filename without extension if the name is empty.
48static base::string16 GetGroupName(const content::WebPluginInfo& plugin) {
49  if (!plugin.name.empty())
50    return plugin.name;
51
52  return plugin.path.BaseName().RemoveExtension().AsUTF16Unsafe();
53}
54
55void LoadMimeTypes(bool matching_mime_types,
56                   const base::DictionaryValue* plugin_dict,
57                   PluginMetadata* plugin) {
58  const base::ListValue* mime_types = NULL;
59  std::string list_key =
60      matching_mime_types ? "matching_mime_types" : "mime_types";
61  if (!plugin_dict->GetList(list_key, &mime_types))
62    return;
63
64  bool success = false;
65  for (base::ListValue::const_iterator mime_type_it = mime_types->begin();
66       mime_type_it != mime_types->end(); ++mime_type_it) {
67    std::string mime_type_str;
68    success = (*mime_type_it)->GetAsString(&mime_type_str);
69    DCHECK(success);
70    if (matching_mime_types) {
71      plugin->AddMatchingMimeType(mime_type_str);
72    } else {
73      plugin->AddMimeType(mime_type_str);
74    }
75  }
76}
77
78PluginMetadata* CreatePluginMetadata(
79    const std::string& identifier,
80    const base::DictionaryValue* plugin_dict) {
81  std::string url;
82  bool success = plugin_dict->GetString("url", &url);
83  std::string help_url;
84  plugin_dict->GetString("help_url", &help_url);
85  base::string16 name;
86  success = plugin_dict->GetString("name", &name);
87  DCHECK(success);
88  bool display_url = false;
89  plugin_dict->GetBoolean("displayurl", &display_url);
90  base::string16 group_name_matcher;
91  success = plugin_dict->GetString("group_name_matcher", &group_name_matcher);
92  DCHECK(success);
93  std::string language_str;
94  plugin_dict->GetString("lang", &language_str);
95
96  PluginMetadata* plugin = new PluginMetadata(identifier,
97                                              name,
98                                              display_url,
99                                              GURL(url),
100                                              GURL(help_url),
101                                              group_name_matcher,
102                                              language_str);
103  const base::ListValue* versions = NULL;
104  if (plugin_dict->GetList("versions", &versions)) {
105    for (base::ListValue::const_iterator it = versions->begin();
106         it != versions->end(); ++it) {
107      base::DictionaryValue* version_dict = NULL;
108      if (!(*it)->GetAsDictionary(&version_dict)) {
109        NOTREACHED();
110        continue;
111      }
112      std::string version;
113      success = version_dict->GetString("version", &version);
114      DCHECK(success);
115      std::string status_str;
116      success = version_dict->GetString("status", &status_str);
117      DCHECK(success);
118      PluginMetadata::SecurityStatus status =
119          PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
120      success = PluginMetadata::ParseSecurityStatus(status_str, &status);
121      DCHECK(success);
122      plugin->AddVersion(Version(version), status);
123    }
124  }
125
126  LoadMimeTypes(false, plugin_dict, plugin);
127  LoadMimeTypes(true, plugin_dict, plugin);
128  return plugin;
129}
130
131}  // namespace
132
133// static
134void PluginFinder::RegisterPrefs(PrefRegistrySimple* registry) {
135  registry->RegisterBooleanPref(prefs::kDisablePluginFinder, false);
136}
137
138// static
139PluginFinder* PluginFinder::GetInstance() {
140  // PluginFinder::GetInstance() is the only method that's allowed to call
141  // Singleton<PluginFinder>::get().
142  return Singleton<PluginFinder>::get();
143}
144
145PluginFinder::PluginFinder() : version_(-1) {
146  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
147}
148
149void PluginFinder::Init() {
150  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
151  // Load the built-in plug-in list first. If we have a newer version stored
152  // locally or download one, we will replace this one with it.
153  scoped_ptr<base::DictionaryValue> plugin_list(LoadBuiltInPluginList());
154  DCHECK(plugin_list);
155  ReinitializePlugins(plugin_list.get());
156}
157
158// static
159base::DictionaryValue* PluginFinder::LoadBuiltInPluginList() {
160  base::StringPiece json_resource(
161      ResourceBundle::GetSharedInstance().GetRawDataResource(
162          IDR_PLUGIN_DB_JSON));
163  std::string error_str;
164  scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
165      json_resource,
166      base::JSON_PARSE_RFC,
167      NULL,
168      &error_str));
169  if (!value.get()) {
170    DLOG(ERROR) << error_str;
171    return NULL;
172  }
173  if (value->GetType() != base::Value::TYPE_DICTIONARY)
174    return NULL;
175  return static_cast<base::DictionaryValue*>(value.release());
176}
177
178PluginFinder::~PluginFinder() {
179#if defined(ENABLE_PLUGIN_INSTALLATION)
180  STLDeleteValues(&installers_);
181#endif
182  STLDeleteValues(&identifier_plugin_);
183}
184
185#if defined(ENABLE_PLUGIN_INSTALLATION)
186bool PluginFinder::FindPlugin(
187    const std::string& mime_type,
188    const std::string& language,
189    PluginInstaller** installer,
190    scoped_ptr<PluginMetadata>* plugin_metadata) {
191  if (g_browser_process->local_state()->GetBoolean(prefs::kDisablePluginFinder))
192    return false;
193
194  base::AutoLock lock(mutex_);
195  PluginMap::const_iterator metadata_it = identifier_plugin_.begin();
196  for (; metadata_it != identifier_plugin_.end(); ++metadata_it) {
197    if (language == metadata_it->second->language() &&
198        metadata_it->second->HasMimeType(mime_type)) {
199      *plugin_metadata = metadata_it->second->Clone();
200
201      std::map<std::string, PluginInstaller*>::const_iterator installer_it =
202          installers_.find(metadata_it->second->identifier());
203      DCHECK(installer_it != installers_.end());
204      *installer = installer_it->second;
205      return true;
206    }
207  }
208  return false;
209}
210
211bool PluginFinder::FindPluginWithIdentifier(
212    const std::string& identifier,
213    PluginInstaller** installer,
214    scoped_ptr<PluginMetadata>* plugin_metadata) {
215  base::AutoLock lock(mutex_);
216  PluginMap::const_iterator metadata_it = identifier_plugin_.find(identifier);
217  if (metadata_it == identifier_plugin_.end())
218    return false;
219  *plugin_metadata = metadata_it->second->Clone();
220
221  if (installer) {
222    std::map<std::string, PluginInstaller*>::const_iterator installer_it =
223        installers_.find(identifier);
224    if (installer_it == installers_.end())
225      return false;
226    *installer = installer_it->second;
227  }
228  return true;
229}
230#endif
231
232void PluginFinder::ReinitializePlugins(
233    const base::DictionaryValue* plugin_list) {
234  base::AutoLock lock(mutex_);
235  int version = 0;  // If no version is defined, we default to 0.
236  const char kVersionKey[] = "x-version";
237  plugin_list->GetInteger(kVersionKey, &version);
238  if (version <= version_)
239    return;
240
241  version_ = version;
242
243  STLDeleteValues(&identifier_plugin_);
244  identifier_plugin_.clear();
245
246  for (base::DictionaryValue::Iterator plugin_it(*plugin_list);
247      !plugin_it.IsAtEnd(); plugin_it.Advance()) {
248    const base::DictionaryValue* plugin = NULL;
249    const std::string& identifier = plugin_it.key();
250    if (plugin_list->GetDictionaryWithoutPathExpansion(identifier, &plugin)) {
251      DCHECK(!identifier_plugin_[identifier]);
252      identifier_plugin_[identifier] = CreatePluginMetadata(identifier, plugin);
253
254#if defined(ENABLE_PLUGIN_INSTALLATION)
255      if (installers_.find(identifier) == installers_.end())
256        installers_[identifier] = new PluginInstaller();
257#endif
258    }
259  }
260}
261
262base::string16 PluginFinder::FindPluginNameWithIdentifier(
263    const std::string& identifier) {
264  base::AutoLock lock(mutex_);
265  PluginMap::const_iterator it = identifier_plugin_.find(identifier);
266  base::string16 name;
267  if (it != identifier_plugin_.end())
268    name = it->second->name();
269
270  return name.empty() ? base::UTF8ToUTF16(identifier) : name;
271}
272
273scoped_ptr<PluginMetadata> PluginFinder::GetPluginMetadata(
274    const content::WebPluginInfo& plugin) {
275  base::AutoLock lock(mutex_);
276  for (PluginMap::const_iterator it = identifier_plugin_.begin();
277       it != identifier_plugin_.end(); ++it) {
278    if (!it->second->MatchesPlugin(plugin))
279      continue;
280
281    return it->second->Clone();
282  }
283
284  // The plug-in metadata was not found, create a dummy one holding
285  // the name, identifier and group name only.
286  std::string identifier = GetIdentifier(plugin);
287  PluginMetadata* metadata = new PluginMetadata(identifier,
288                                                GetGroupName(plugin),
289                                                false,
290                                                GURL(),
291                                                GURL(),
292                                                plugin.name,
293                                                std::string());
294  for (size_t i = 0; i < plugin.mime_types.size(); ++i)
295    metadata->AddMatchingMimeType(plugin.mime_types[i].mime_type);
296
297  DCHECK(metadata->MatchesPlugin(plugin));
298  if (identifier_plugin_.find(identifier) != identifier_plugin_.end())
299    identifier = GetLongIdentifier(plugin);
300
301  DCHECK(identifier_plugin_.find(identifier) == identifier_plugin_.end());
302  identifier_plugin_[identifier] = metadata;
303  return metadata->Clone();
304}
305