• 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 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
34 #include "FileSystem.h"
35 #endif
36 #include <stdlib.h>
37 #include <wtf/text/CString.h>
38 
39 #if PLATFORM(ANDROID)
40 #include "JavaSharedClient.h"
41 #include "PluginClient.h"
42 #endif
43 
44 namespace WebCore {
45 
46 typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap;
47 
48 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
49 static const size_t maximumPersistentPluginMetadataCacheSize = 32768;
50 
51 static bool gPersistentPluginMetadataCacheIsEnabled;
52 
persistentPluginMetadataCachePath()53 String& persistentPluginMetadataCachePath()
54 {
55     DEFINE_STATIC_LOCAL(String, cachePath, ());
56     return cachePath;
57 }
58 #endif
59 
PluginDatabase()60 PluginDatabase::PluginDatabase()
61 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
62     : m_persistentMetadataCacheIsLoaded(false)
63 #endif
64 {
65 }
66 
installedPlugins(bool populate)67 PluginDatabase* PluginDatabase::installedPlugins(bool populate)
68 {
69     static PluginDatabase* plugins = 0;
70 
71     if (!plugins) {
72         plugins = new PluginDatabase;
73 
74         if (populate) {
75             plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories());
76             plugins->refresh();
77         }
78     }
79 
80     return plugins;
81 }
82 
isMIMETypeRegistered(const String & mimeType)83 bool PluginDatabase::isMIMETypeRegistered(const String& mimeType)
84 {
85     if (mimeType.isNull())
86         return false;
87     if (m_registeredMIMETypes.contains(mimeType))
88         return true;
89     // No plugin was found, try refreshing the database and searching again
90     return (refresh() && m_registeredMIMETypes.contains(mimeType));
91 }
92 
addExtraPluginDirectory(const String & directory)93 void PluginDatabase::addExtraPluginDirectory(const String& directory)
94 {
95     m_pluginDirectories.append(directory);
96     refresh();
97 }
98 
refresh()99 bool PluginDatabase::refresh()
100 {
101 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
102     if (!m_persistentMetadataCacheIsLoaded)
103         loadPersistentMetadataCache();
104 #endif
105     bool pluginSetChanged = false;
106 
107     if (!m_plugins.isEmpty()) {
108         PluginSet pluginsToUnload;
109         getDeletedPlugins(pluginsToUnload);
110 
111         // Unload plugins
112         PluginSet::const_iterator end = pluginsToUnload.end();
113         for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it)
114             remove(it->get());
115 
116         pluginSetChanged = !pluginsToUnload.isEmpty();
117     }
118 
119     HashSet<String> paths;
120     getPluginPathsInDirectories(paths);
121 
122     HashMap<String, time_t> pathsWithTimes;
123 
124     // We should only skip unchanged files if we didn't remove any plugins above. If we did remove
125     // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions
126     // of RealPlayer installed and just removed the newer one, we'll pick up the older one.
127     bool shouldSkipUnchangedFiles = !pluginSetChanged;
128 
129     HashSet<String>::const_iterator pathsEnd = paths.end();
130     for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) {
131         time_t lastModified;
132         if (!getFileModificationTime(*it, lastModified))
133             continue;
134 
135         pathsWithTimes.add(*it, lastModified);
136 
137         // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything.
138         if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified)
139             continue;
140 
141         if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) {
142             ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified);
143             remove(oldPackage.get());
144         }
145 
146         RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified);
147         if (package && add(package.release()))
148             pluginSetChanged = true;
149     }
150 
151     // Cache all the paths we found with their timestamps for next time.
152     pathsWithTimes.swap(m_pluginPathsWithTimes);
153 
154     if (!pluginSetChanged)
155         return false;
156 
157 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
158     updatePersistentMetadataCache();
159 #endif
160 
161     m_registeredMIMETypes.clear();
162 
163     // Register plug-in MIME types
164     PluginSet::const_iterator end = m_plugins.end();
165     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
166         // Get MIME types
167         MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin();
168         MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end();
169         for (; map_it != map_end; ++map_it)
170             m_registeredMIMETypes.add(map_it->first);
171     }
172 
173     return true;
174 }
175 
plugins() const176 Vector<PluginPackage*> PluginDatabase::plugins() const
177 {
178     Vector<PluginPackage*> result;
179 
180     PluginSet::const_iterator end = m_plugins.end();
181     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it)
182         result.append((*it).get());
183 
184     return result;
185 }
186 
preferredPluginCompare(const void * a,const void * b)187 int PluginDatabase::preferredPluginCompare(const void* a, const void* b)
188 {
189     PluginPackage* pluginA = *static_cast<PluginPackage* const*>(a);
190     PluginPackage* pluginB = *static_cast<PluginPackage* const*>(b);
191 
192     return pluginA->compare(*pluginB);
193 }
194 
pluginForMIMEType(const String & mimeType)195 PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType)
196 {
197     if (mimeType.isEmpty())
198         return 0;
199 
200     String key = mimeType.lower();
201     PluginSet::const_iterator end = m_plugins.end();
202     PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get();
203     if (preferredPlugin
204         && preferredPlugin->isEnabled()
205         && preferredPlugin->mimeToDescriptions().contains(key)) {
206         return preferredPlugin;
207     }
208 
209     Vector<PluginPackage*, 2> pluginChoices;
210 
211     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
212         PluginPackage* plugin = (*it).get();
213 
214         if (!plugin->isEnabled())
215             continue;
216 
217         if (plugin->mimeToDescriptions().contains(key)) {
218 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
219             if (!plugin->ensurePluginLoaded())
220                 continue;
221 #endif
222             pluginChoices.append(plugin);
223         }
224     }
225 
226     if (pluginChoices.isEmpty())
227         return 0;
228 
229     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
230 
231     return pluginChoices[0];
232 }
233 
MIMETypeForExtension(const String & extension) const234 String PluginDatabase::MIMETypeForExtension(const String& extension) const
235 {
236     if (extension.isEmpty())
237         return String();
238 
239     PluginSet::const_iterator end = m_plugins.end();
240     String mimeType;
241     Vector<PluginPackage*, 2> pluginChoices;
242     HashMap<PluginPackage*, String> mimeTypeForPlugin;
243 
244     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
245         if (!(*it)->isEnabled())
246             continue;
247 
248         MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end();
249 
250         for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) {
251             mimeType = mime_it->first;
252             PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get();
253             const Vector<String>& extensions = mime_it->second;
254             bool foundMapping = false;
255             for (unsigned i = 0; i < extensions.size(); i++) {
256                 if (equalIgnoringCase(extensions[i], extension)) {
257                     PluginPackage* plugin = (*it).get();
258 
259                     if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin))
260                         return mimeType;
261 
262 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
263                     if (!plugin->ensurePluginLoaded())
264                         continue;
265 #endif
266                     pluginChoices.append(plugin);
267                     mimeTypeForPlugin.add(plugin, mimeType);
268                     foundMapping = true;
269                     break;
270                 }
271             }
272             if (foundMapping)
273                 break;
274         }
275     }
276 
277     if (pluginChoices.isEmpty())
278         return String();
279 
280     qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare);
281 
282     return mimeTypeForPlugin.get(pluginChoices[0]);
283 }
284 
findPlugin(const KURL & url,String & mimeType)285 PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType)
286 {
287     if (!mimeType.isEmpty())
288         return pluginForMIMEType(mimeType);
289 
290     String filename = url.lastPathComponent();
291     if (filename.endsWith("/"))
292         return 0;
293 
294     int extensionPos = filename.reverseFind('.');
295     if (extensionPos == -1)
296         return 0;
297 
298     String mimeTypeForExtension = MIMETypeForExtension(filename.substring(extensionPos + 1));
299     PluginPackage* plugin = pluginForMIMEType(mimeTypeForExtension);
300     if (!plugin) {
301         // FIXME: if no plugin could be found, query Windows for the mime type
302         // corresponding to the extension.
303         return 0;
304     }
305 
306     mimeType = mimeTypeForExtension;
307     return plugin;
308 }
309 
setPreferredPluginForMIMEType(const String & mimeType,PluginPackage * plugin)310 void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin)
311 {
312     if (!plugin || plugin->mimeToExtensions().contains(mimeType))
313         m_preferredPlugins.set(mimeType.lower(), plugin);
314 }
315 
getDeletedPlugins(PluginSet & plugins) const316 void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const
317 {
318     PluginSet::const_iterator end = m_plugins.end();
319     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
320         if (!fileExists((*it)->path()))
321             plugins.add(*it);
322     }
323 }
324 
add(PassRefPtr<PluginPackage> prpPackage)325 bool PluginDatabase::add(PassRefPtr<PluginPackage> prpPackage)
326 {
327     ASSERT_ARG(prpPackage, prpPackage);
328 
329     RefPtr<PluginPackage> package = prpPackage;
330 
331     if (!m_plugins.add(package).second)
332         return false;
333 
334     m_pluginsByPath.add(package->path(), package);
335     return true;
336 }
337 
remove(PluginPackage * package)338 void PluginDatabase::remove(PluginPackage* package)
339 {
340     MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin();
341     MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end();
342     for ( ; it != end; ++it) {
343         PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first);
344         if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package)
345             m_preferredPlugins.remove(packageInMap);
346     }
347 
348     m_plugins.remove(package);
349     m_pluginsByPath.remove(package->path());
350 }
351 
clear()352 void PluginDatabase::clear()
353 {
354     m_plugins.clear();
355     m_pluginsByPath.clear();
356     m_pluginPathsWithTimes.clear();
357     m_registeredMIMETypes.clear();
358     m_preferredPlugins.clear();
359 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
360     m_persistentMetadataCacheIsLoaded = false;
361 #endif
362 }
363 
364 #if (!OS(WINCE)) && (!OS(SYMBIAN)) && (!OS(WINDOWS) || !ENABLE(NETSCAPE_PLUGIN_API))
365 // For Safari/Win the following three methods are implemented
366 // in PluginDatabaseWin.cpp, but if we can use WebCore constructs
367 // for the logic we should perhaps move it here under XP_WIN?
368 
defaultPluginDirectories()369 Vector<String> PluginDatabase::defaultPluginDirectories()
370 {
371     Vector<String> paths;
372 
373     // Add paths specific to each platform
374 #if defined(XP_UNIX)
375     String userPluginPath = homeDirectoryPath();
376     userPluginPath.append(String("/.mozilla/plugins"));
377     paths.append(userPluginPath);
378 
379     userPluginPath = homeDirectoryPath();
380     userPluginPath.append(String("/.netscape/plugins"));
381     paths.append(userPluginPath);
382 
383     paths.append("/usr/lib/browser/plugins");
384     paths.append("/usr/local/lib/mozilla/plugins");
385     paths.append("/usr/lib/firefox/plugins");
386     paths.append("/usr/lib64/browser-plugins");
387     paths.append("/usr/lib/browser-plugins");
388     paths.append("/usr/lib/mozilla/plugins");
389     paths.append("/usr/local/netscape/plugins");
390     paths.append("/opt/mozilla/plugins");
391     paths.append("/opt/mozilla/lib/plugins");
392     paths.append("/opt/netscape/plugins");
393     paths.append("/opt/netscape/communicator/plugins");
394     paths.append("/usr/lib/netscape/plugins");
395     paths.append("/usr/lib/netscape/plugins-libc5");
396     paths.append("/usr/lib/netscape/plugins-libc6");
397     paths.append("/usr/lib64/netscape/plugins");
398     paths.append("/usr/lib64/mozilla/plugins");
399     paths.append("/usr/lib/nsbrowser/plugins");
400     paths.append("/usr/lib64/nsbrowser/plugins");
401 
402     String mozHome(getenv("MOZILLA_HOME"));
403     mozHome.append("/plugins");
404     paths.append(mozHome);
405 
406     Vector<String> mozPaths;
407     String mozPath(getenv("MOZ_PLUGIN_PATH"));
408     mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths);
409     paths.append(mozPaths);
410 #elif defined(XP_MACOSX)
411     String userPluginPath = homeDirectoryPath();
412     userPluginPath.append(String("/Library/Internet Plug-Ins"));
413     paths.append(userPluginPath);
414     paths.append("/Library/Internet Plug-Ins");
415 #elif defined(XP_WIN)
416     String userPluginPath = homeDirectoryPath();
417     userPluginPath.append(String("\\Application Data\\Mozilla\\plugins"));
418     paths.append(userPluginPath);
419 #endif
420 
421     // Add paths specific to each port
422 #if PLATFORM(QT)
423     Vector<String> qtPaths;
424     String qtPath(qgetenv("QTWEBKIT_PLUGIN_PATH").constData());
425     qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths);
426     paths.append(qtPaths);
427 #endif
428 
429 #if PLATFORM(ANDROID)
430     if (android::JavaSharedClient::GetPluginClient())
431         return android::JavaSharedClient::GetPluginClient()->getPluginDirectories();
432 #endif
433 
434     return paths;
435 }
436 
isPreferredPluginDirectory(const String & path)437 bool PluginDatabase::isPreferredPluginDirectory(const String& path)
438 {
439     String preferredPath = homeDirectoryPath();
440 
441 #if defined(XP_UNIX)
442     preferredPath.append(String("/.mozilla/plugins"));
443 #elif defined(XP_MACOSX)
444     preferredPath.append(String("/Library/Internet Plug-Ins"));
445 #elif defined(XP_WIN)
446     preferredPath.append(String("\\Application Data\\Mozilla\\plugins"));
447 #endif
448 
449     // TODO: We should normalize the path before doing a comparison.
450     return path == preferredPath;
451 }
452 
getPluginPathsInDirectories(HashSet<String> & paths) const453 void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const
454 {
455     // FIXME: This should be a case insensitive set.
456     HashSet<String> uniqueFilenames;
457 
458 #if defined(XP_UNIX) || defined(ANDROID)
459     String fileNameFilter("*.so");
460 #else
461     String fileNameFilter("");
462 #endif
463 
464     Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end();
465     for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) {
466         Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter);
467         Vector<String>::const_iterator pluginsEnd = pluginPaths.end();
468         for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) {
469             if (!fileExists(*pIt))
470                 continue;
471 
472             paths.add(*pIt);
473         }
474     }
475 }
476 
477 #endif // !OS(SYMBIAN) && !OS(WINDOWS)
478 
479 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
480 
fillBufferWithContentsOfFile(PlatformFileHandle file,Vector<char> & buffer)481 static void fillBufferWithContentsOfFile(PlatformFileHandle file, Vector<char>& buffer)
482 {
483     size_t bufferSize = 0;
484     size_t bufferCapacity = 1024;
485     buffer.resize(bufferCapacity);
486 
487     do {
488         bufferSize += readFromFile(file, buffer.data() + bufferSize, bufferCapacity - bufferSize);
489         if (bufferSize == bufferCapacity) {
490             if (bufferCapacity < maximumPersistentPluginMetadataCacheSize) {
491                 bufferCapacity *= 2;
492                 buffer.resize(bufferCapacity);
493             } else {
494                 buffer.clear();
495                 return;
496             }
497         } else
498             break;
499     } while (true);
500 
501     buffer.shrink(bufferSize);
502 }
503 
readUTF8String(String & resultString,char * & start,const char * end)504 static bool readUTF8String(String& resultString, char*& start, const char* end)
505 {
506     if (start >= end)
507         return false;
508 
509     int len = strlen(start);
510     resultString = String::fromUTF8(start, len);
511     start += len + 1;
512 
513     return true;
514 }
515 
readTime(time_t & resultTime,char * & start,const char * end)516 static bool readTime(time_t& resultTime, char*& start, const char* end)
517 {
518     if (start + sizeof(time_t) >= end)
519         return false;
520 
521     resultTime = *reinterpret_cast_ptr<time_t*>(start);
522     start += sizeof(time_t);
523 
524     return true;
525 }
526 
527 static const char schemaVersion = '1';
528 static const char persistentPluginMetadataCacheFilename[] = "PluginMetadataCache.bin";
529 
loadPersistentMetadataCache()530 void PluginDatabase::loadPersistentMetadataCache()
531 {
532     if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty())
533         return;
534 
535     PlatformFileHandle file;
536     String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename);
537     file = openFile(absoluteCachePath, OpenForRead);
538 
539     if (!isHandleValid(file))
540         return;
541 
542     // Mark cache as loaded regardless of success or failure. If
543     // there's error in the cache, we won't try to load it anymore.
544     m_persistentMetadataCacheIsLoaded = true;
545 
546     Vector<char> fileContents;
547     fillBufferWithContentsOfFile(file, fileContents);
548     closeFile(file);
549 
550     if (fileContents.size() < 2 || fileContents.first() != schemaVersion || fileContents.last() != '\0') {
551         LOG_ERROR("Unable to read plugin metadata cache: corrupt schema");
552         deleteFile(absoluteCachePath);
553         return;
554     }
555 
556     char* bufferPos = fileContents.data() + 1;
557     char* end = fileContents.data() + fileContents.size();
558 
559     PluginSet cachedPlugins;
560     HashMap<String, time_t> cachedPluginPathsWithTimes;
561     HashMap<String, RefPtr<PluginPackage> > cachedPluginsByPath;
562 
563     while (bufferPos < end) {
564         String path;
565         time_t lastModified;
566         String name;
567         String desc;
568         String mimeDesc;
569         if (!(readUTF8String(path, bufferPos, end)
570               && readTime(lastModified, bufferPos, end)
571               && readUTF8String(name, bufferPos, end)
572               && readUTF8String(desc, bufferPos, end)
573               && readUTF8String(mimeDesc, bufferPos, end))) {
574             LOG_ERROR("Unable to read plugin metadata cache: corrupt data");
575             deleteFile(absoluteCachePath);
576             return;
577         }
578 
579         // Skip metadata that points to plugins from directories that
580         // are not part of plugin directory list anymore.
581         String pluginDirectoryName = directoryName(path);
582         if (m_pluginDirectories.find(pluginDirectoryName) == WTF::notFound)
583             continue;
584 
585         RefPtr<PluginPackage> package = PluginPackage::createPackageFromCache(path, lastModified, name, desc, mimeDesc);
586 
587         if (package && cachedPlugins.add(package).second) {
588             cachedPluginPathsWithTimes.add(package->path(), package->lastModified());
589             cachedPluginsByPath.add(package->path(), package);
590         }
591     }
592 
593     m_plugins.swap(cachedPlugins);
594     m_pluginsByPath.swap(cachedPluginsByPath);
595     m_pluginPathsWithTimes.swap(cachedPluginPathsWithTimes);
596 }
597 
writeUTF8String(PlatformFileHandle file,const String & string)598 static bool writeUTF8String(PlatformFileHandle file, const String& string)
599 {
600     CString utf8String = string.utf8();
601     int length = utf8String.length() + 1;
602     return writeToFile(file, utf8String.data(), length) == length;
603 }
604 
writeTime(PlatformFileHandle file,const time_t & time)605 static bool writeTime(PlatformFileHandle file, const time_t& time)
606 {
607     return writeToFile(file, reinterpret_cast<const char*>(&time), sizeof(time_t)) == sizeof(time_t);
608 }
609 
updatePersistentMetadataCache()610 void PluginDatabase::updatePersistentMetadataCache()
611 {
612     if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty())
613         return;
614 
615     makeAllDirectories(persistentMetadataCachePath());
616     String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename);
617     deleteFile(absoluteCachePath);
618 
619     if (m_plugins.isEmpty())
620         return;
621 
622     PlatformFileHandle file;
623     file = openFile(absoluteCachePath, OpenForWrite);
624 
625     if (!isHandleValid(file)) {
626         LOG_ERROR("Unable to open plugin metadata cache for saving");
627         return;
628     }
629 
630     char localSchemaVersion = schemaVersion;
631     if (writeToFile(file, &localSchemaVersion, 1) != 1) {
632         LOG_ERROR("Unable to write plugin metadata cache schema");
633         closeFile(file);
634         deleteFile(absoluteCachePath);
635         return;
636     }
637 
638     PluginSet::const_iterator end = m_plugins.end();
639     for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
640         if (!(writeUTF8String(file, (*it)->path())
641               && writeTime(file, (*it)->lastModified())
642               && writeUTF8String(file, (*it)->name())
643               && writeUTF8String(file, (*it)->description())
644               && writeUTF8String(file, (*it)->fullMIMEDescription()))) {
645             LOG_ERROR("Unable to write plugin metadata to cache");
646             closeFile(file);
647             deleteFile(absoluteCachePath);
648             return;
649         }
650     }
651 
652     closeFile(file);
653 }
654 
isPersistentMetadataCacheEnabled()655 bool PluginDatabase::isPersistentMetadataCacheEnabled()
656 {
657     return gPersistentPluginMetadataCacheIsEnabled;
658 }
659 
setPersistentMetadataCacheEnabled(bool isEnabled)660 void PluginDatabase::setPersistentMetadataCacheEnabled(bool isEnabled)
661 {
662     gPersistentPluginMetadataCacheIsEnabled = isEnabled;
663 }
664 
persistentMetadataCachePath()665 String PluginDatabase::persistentMetadataCachePath()
666 {
667     return WebCore::persistentPluginMetadataCachePath();
668 }
669 
setPersistentMetadataCachePath(const String & persistentMetadataCachePath)670 void PluginDatabase::setPersistentMetadataCachePath(const String& persistentMetadataCachePath)
671 {
672     WebCore::persistentPluginMetadataCachePath() = persistentMetadataCachePath;
673 }
674 #endif
675 }
676