• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Collabora, Ltd.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "PluginDatabase.h"
29 
30 #include "Frame.h"
31 #include "KURL.h"
32 #include "PluginPackage.h"
33 #include <stdlib.h>
34 
35 #if PLATFORM(ANDROID)
36 #include "JavaSharedClient.h"
37 #include "PluginClient.h"
38 #endif
39 
40 namespace WebCore {
41 
42 typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap;
43 
installedPlugins(bool populate)44 PluginDatabase* PluginDatabase::installedPlugins(bool populate)
45 {
46     static PluginDatabase* plugins = 0;
47 
48     if (!plugins) {
49         plugins = new PluginDatabase;
50 
51         if (populate) {
52             plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories());
53             plugins->refresh();
54         }
55     }
56 
57     return plugins;
58 }
59 
isMIMETypeRegistered(const String & mimeType)60 bool PluginDatabase::isMIMETypeRegistered(const String& mimeType)
61 {
62     if (mimeType.isNull())
63         return false;
64     if (m_registeredMIMETypes.contains(mimeType))
65         return true;
66     // No plugin was found, try refreshing the database and searching again
67     return (refresh() && m_registeredMIMETypes.contains(mimeType));
68 }
69 
addExtraPluginDirectory(const String & directory)70 void PluginDatabase::addExtraPluginDirectory(const String& directory)
71 {
72     m_pluginDirectories.append(directory);
73     refresh();
74 }
75 
refresh()76 bool PluginDatabase::refresh()
77 {
78     bool pluginSetChanged = false;
79 
80     if (!m_plugins.isEmpty()) {
81         PluginSet pluginsToUnload;
82         getDeletedPlugins(pluginsToUnload);
83 
84         // Unload plugins
85         PluginSet::const_iterator end = pluginsToUnload.end();
86         for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it)
87             remove(it->get());
88 
89         pluginSetChanged = !pluginsToUnload.isEmpty();
90     }
91 
92     HashSet<String> paths;
93     getPluginPathsInDirectories(paths);
94 
95     HashMap<String, time_t> pathsWithTimes;
96 
97     // We should only skip unchanged files if we didn't remove any plugins above. If we did remove
98     // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions
99     // of RealPlayer installed and just removed the newer one, we'll pick up the older one.
100     bool shouldSkipUnchangedFiles = !pluginSetChanged;
101 
102     HashSet<String>::const_iterator pathsEnd = paths.end();
103     for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) {
104         time_t lastModified;
105         if (!getFileModificationTime(*it, lastModified))
106             continue;
107 
108         pathsWithTimes.add(*it, lastModified);
109 
110         // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything.
111         if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified)
112             continue;
113 
114         if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) {
115             ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified);
116             remove(oldPackage.get());
117         }
118 
119         RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified);
120         if (package && add(package.release()))
121             pluginSetChanged = true;
122     }
123 
124     // Cache all the paths we found with their timestamps for next time.
125     pathsWithTimes.swap(m_pluginPathsWithTimes);
126 
127     if (!pluginSetChanged)
128         return false;
129 
130     m_registeredMIMETypes.clear();
131 
132     // Register plug-in MIME types
133     PluginSet::const_iterator end = m_plugins.end();
134     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
135         // Get MIME types
136         MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin();
137         MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end();
138         for (; map_it != map_end; ++map_it)
139             m_registeredMIMETypes.add(map_it->first);
140     }
141 
142     return true;
143 }
144 
plugins() const145 Vector<PluginPackage*> PluginDatabase::plugins() const
146 {
147     Vector<PluginPackage*> result;
148 
149     PluginSet::const_iterator end = m_plugins.end();
150     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it)
151         result.append((*it).get());
152 
153     return result;
154 }
155 
preferredPluginCompare(const void * a,const void * b)156 int PluginDatabase::preferredPluginCompare(const void* a, const void* b)
157 {
158     PluginPackage* pluginA = *static_cast<PluginPackage* const*>(a);
159     PluginPackage* pluginB = *static_cast<PluginPackage* const*>(b);
160 
161     return pluginA->compare(*pluginB);
162 }
163 
pluginForMIMEType(const String & mimeType)164 PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType)
165 {
166     if (mimeType.isEmpty())
167         return 0;
168 
169     String key = mimeType.lower();
170     PluginSet::const_iterator end = m_plugins.end();
171     PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get();
172     if (preferredPlugin
173         && preferredPlugin->isEnabled()
174         && preferredPlugin->mimeToDescriptions().contains(key)) {
175         return preferredPlugin;
176     }
177 
178     Vector<PluginPackage*, 2> pluginChoices;
179 
180     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
181         PluginPackage* plugin = (*it).get();
182 
183         if (!plugin->isEnabled())
184             continue;
185 
186         if (plugin->mimeToDescriptions().contains(key))
187             pluginChoices.append(plugin);
188     }
189 
190     if (pluginChoices.isEmpty())
191         return 0;
192 
193     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
194 
195     return pluginChoices[0];
196 }
197 
MIMETypeForExtension(const String & extension) const198 String PluginDatabase::MIMETypeForExtension(const String& extension) const
199 {
200     if (extension.isEmpty())
201         return String();
202 
203     PluginSet::const_iterator end = m_plugins.end();
204     String mimeType;
205     Vector<PluginPackage*, 2> pluginChoices;
206     HashMap<PluginPackage*, String> mimeTypeForPlugin;
207 
208     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
209         if (!(*it)->isEnabled())
210             continue;
211 
212         MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end();
213 
214         for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) {
215             mimeType = mime_it->first;
216             PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get();
217             const Vector<String>& extensions = mime_it->second;
218             bool foundMapping = false;
219             for (unsigned i = 0; i < extensions.size(); i++) {
220                 if (equalIgnoringCase(extensions[i], extension)) {
221                     PluginPackage* plugin = (*it).get();
222 
223                     if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin))
224                         return mimeType;
225 
226                     pluginChoices.append(plugin);
227                     mimeTypeForPlugin.add(plugin, mimeType);
228                     foundMapping = true;
229                     break;
230                 }
231             }
232             if (foundMapping)
233                 break;
234         }
235     }
236 
237     if (pluginChoices.isEmpty())
238         return String();
239 
240     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
241 
242     return mimeTypeForPlugin.get(pluginChoices[0]);
243 }
244 
findPlugin(const KURL & url,String & mimeType)245 PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType)
246 {
247     PluginPackage* plugin = pluginForMIMEType(mimeType);
248     String filename = url.string();
249 
250     if (!plugin) {
251         String filename = url.lastPathComponent();
252         if (!filename.endsWith("/")) {
253             int extensionPos = filename.reverseFind('.');
254             if (extensionPos != -1) {
255                 String extension = filename.substring(extensionPos + 1);
256 
257                 mimeType = MIMETypeForExtension(extension);
258                 plugin = pluginForMIMEType(mimeType);
259             }
260         }
261     }
262 
263     // FIXME: if no plugin could be found, query Windows for the mime type
264     // corresponding to the extension.
265 
266     return plugin;
267 }
268 
setPreferredPluginForMIMEType(const String & mimeType,PluginPackage * plugin)269 void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin)
270 {
271     if (!plugin || plugin->mimeToExtensions().contains(mimeType))
272         m_preferredPlugins.set(mimeType.lower(), plugin);
273 }
274 
getDeletedPlugins(PluginSet & plugins) const275 void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const
276 {
277     PluginSet::const_iterator end = m_plugins.end();
278     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
279         if (!fileExists((*it)->path()))
280             plugins.add(*it);
281     }
282 }
283 
add(PassRefPtr<PluginPackage> prpPackage)284 bool PluginDatabase::add(PassRefPtr<PluginPackage> prpPackage)
285 {
286     ASSERT_ARG(prpPackage, prpPackage);
287 
288     RefPtr<PluginPackage> package = prpPackage;
289 
290     if (!m_plugins.add(package).second)
291         return false;
292 
293     m_pluginsByPath.add(package->path(), package);
294     return true;
295 }
296 
remove(PluginPackage * package)297 void PluginDatabase::remove(PluginPackage* package)
298 {
299     MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin();
300     MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end();
301     for ( ; it != end; ++it) {
302         PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first);
303         if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package)
304             m_preferredPlugins.remove(packageInMap);
305     }
306 
307     m_plugins.remove(package);
308     m_pluginsByPath.remove(package->path());
309 }
310 
clear()311 void PluginDatabase::clear()
312 {
313     m_plugins.clear();
314     m_pluginsByPath.clear();
315     m_pluginPathsWithTimes.clear();
316     m_registeredMIMETypes.clear();
317     m_preferredPlugins.clear();
318 }
319 
320 #if !PLATFORM(WIN_OS) || PLATFORM(WX)
321 // For Safari/Win the following three methods are implemented
322 // in PluginDatabaseWin.cpp, but if we can use WebCore constructs
323 // for the logic we should perhaps move it here under XP_WIN?
324 
defaultPluginDirectories()325 Vector<String> PluginDatabase::defaultPluginDirectories()
326 {
327     Vector<String> paths;
328 
329     // Add paths specific to each platform
330 #if defined(XP_UNIX)
331     String userPluginPath = homeDirectoryPath();
332     userPluginPath.append(String("/.mozilla/plugins"));
333     paths.append(userPluginPath);
334 
335     userPluginPath = homeDirectoryPath();
336     userPluginPath.append(String("/.netscape/plugins"));
337     paths.append(userPluginPath);
338 
339     paths.append("/usr/lib/browser/plugins");
340     paths.append("/usr/local/lib/mozilla/plugins");
341     paths.append("/usr/lib/firefox/plugins");
342     paths.append("/usr/lib64/browser-plugins");
343     paths.append("/usr/lib/browser-plugins");
344     paths.append("/usr/lib/mozilla/plugins");
345     paths.append("/usr/local/netscape/plugins");
346     paths.append("/opt/mozilla/plugins");
347     paths.append("/opt/mozilla/lib/plugins");
348     paths.append("/opt/netscape/plugins");
349     paths.append("/opt/netscape/communicator/plugins");
350     paths.append("/usr/lib/netscape/plugins");
351     paths.append("/usr/lib/netscape/plugins-libc5");
352     paths.append("/usr/lib/netscape/plugins-libc6");
353     paths.append("/usr/lib64/netscape/plugins");
354     paths.append("/usr/lib64/mozilla/plugins");
355 
356     String mozHome(getenv("MOZILLA_HOME"));
357     mozHome.append("/plugins");
358     paths.append(mozHome);
359 
360     Vector<String> mozPaths;
361     String mozPath(getenv("MOZ_PLUGIN_PATH"));
362     mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths);
363     paths.append(mozPaths);
364 #elif defined(XP_MACOSX)
365     String userPluginPath = homeDirectoryPath();
366     userPluginPath.append(String("/Library/Internet Plug-Ins"));
367     paths.append(userPluginPath);
368     paths.append("/Library/Internet Plug-Ins");
369 #elif defined(XP_WIN)
370     String userPluginPath = homeDirectoryPath();
371     userPluginPath.append(String("\\Application Data\\Mozilla\\plugins"));
372     paths.append(userPluginPath);
373 #endif
374 
375     // Add paths specific to each port
376 #if PLATFORM(QT)
377     Vector<String> qtPaths;
378     String qtPath(getenv("QTWEBKIT_PLUGIN_PATH"));
379     qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths);
380     paths.append(qtPaths);
381 #endif
382 
383 #if PLATFORM(ANDROID)
384     if (android::JavaSharedClient::GetPluginClient())
385         return android::JavaSharedClient::GetPluginClient()->getPluginDirectories();
386 #endif
387 
388     return paths;
389 }
390 
isPreferredPluginDirectory(const String & path)391 bool PluginDatabase::isPreferredPluginDirectory(const String& path)
392 {
393     String preferredPath = homeDirectoryPath();
394 
395 #if defined(XP_UNIX)
396     preferredPath.append(String("/.mozilla/plugins"));
397 #elif defined(XP_MACOSX)
398     preferredPath.append(String("/Library/Internet Plug-Ins"));
399 #elif defined(XP_WIN)
400     preferredPath.append(String("\\Application Data\\Mozilla\\plugins"));
401 #endif
402 
403     // TODO: We should normalize the path before doing a comparison.
404     return path == preferredPath;
405 }
406 
getPluginPathsInDirectories(HashSet<String> & paths) const407 void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const
408 {
409     // FIXME: This should be a case insensitive set.
410     HashSet<String> uniqueFilenames;
411 
412 #if defined(XP_UNIX) || defined(ANDROID)
413     String fileNameFilter("*.so");
414 #else
415     String fileNameFilter("");
416 #endif
417 
418     Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end();
419     for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) {
420         Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter);
421         Vector<String>::const_iterator pluginsEnd = pluginPaths.end();
422         for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) {
423             if (!fileExists(*pIt))
424                 continue;
425 
426             paths.add(*pIt);
427         }
428     }
429 }
430 
431 #endif // !PLATFORM(WIN_OS)
432 
433 }
434