зеркало из https://github.com/mozilla/gecko-dev.git
1212 строки
37 KiB
C++
1212 строки
37 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* PluginFinder.cpp - manages finding plugins on disk, and keeping plugin
|
|
* lists in the host updated. */
|
|
|
|
#include "PluginFinder.h"
|
|
|
|
#include "nsIBlocklistService.h"
|
|
#include "nsIFile.h"
|
|
#include "nsIXULRuntime.h"
|
|
#if defined(XP_MACOSX)
|
|
# include "nsILocalFileMac.h"
|
|
#endif
|
|
#include "nsIWritablePropertyBag2.h"
|
|
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/Result.h"
|
|
#include "mozilla/ResultExtensions.h"
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#include "nscore.h"
|
|
#include "prio.h"
|
|
|
|
#include "nsAppDirectoryServiceDefs.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsDirectoryServiceDefs.h"
|
|
#include "nsPluginDirServiceProvider.h"
|
|
#include "nsPluginHost.h"
|
|
#include "nsPluginLogging.h"
|
|
#include "nsPluginManifestLineReader.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "xpcpublic.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
static const char* kPluginRegistryVersion = "0.19";
|
|
|
|
StaticRefPtr<nsIFile> sPluginRegFile;
|
|
|
|
#define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
|
|
|
|
#define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
|
|
{ \
|
|
while (list_) { \
|
|
type_ temp = list_->mNext_; \
|
|
list_->mNext_ = nullptr; \
|
|
list_ = temp; \
|
|
} \
|
|
}
|
|
|
|
/* to cope with short read */
|
|
/* we should probably put this into a global library now that this is the second
|
|
time we need this. */
|
|
static int32_t busy_beaver_PR_Read(PRFileDesc* fd, void* start, int32_t len) {
|
|
int n;
|
|
int32_t remaining = len;
|
|
|
|
while (remaining > 0) {
|
|
n = PR_Read(fd, start, remaining);
|
|
if (n < 0) {
|
|
/* may want to repeat if errno == EINTR */
|
|
if ((len - remaining) == 0) // no octet is ever read
|
|
return -1;
|
|
break;
|
|
}
|
|
remaining -= n;
|
|
char* cp = (char*)start;
|
|
cp += n;
|
|
start = cp;
|
|
}
|
|
return len - remaining;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
|
|
|
|
nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath,
|
|
int64_t aLastModifiedTime)
|
|
: mFullPath(aFullPath),
|
|
mLastModifiedTime(aLastModifiedTime),
|
|
mSeen(false) {}
|
|
|
|
nsInvalidPluginTag::~nsInvalidPluginTag() = default;
|
|
|
|
// flat file reg funcs
|
|
static bool ReadSectionHeader(nsPluginManifestLineReader& reader,
|
|
const char* token) {
|
|
do {
|
|
if (*reader.LinePtr() == '[') {
|
|
char* p = reader.LinePtr() + (reader.LineLength() - 1);
|
|
if (*p != ']') break;
|
|
*p = 0;
|
|
|
|
char* values[1];
|
|
if (1 != reader.ParseLine(values, 1)) break;
|
|
// ignore the leading '['
|
|
if (PL_strcmp(values[0] + 1, token)) {
|
|
break; // it's wrong token
|
|
}
|
|
return true;
|
|
}
|
|
} while (reader.NextLine());
|
|
return false;
|
|
}
|
|
|
|
nsPluginTag* PluginFinder::FirstPluginWithPath(const nsCString& path) {
|
|
for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
|
|
if (tag->mFullPath.Equals(path)) {
|
|
return tag;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(PluginFinder, nsIRunnable, nsIAsyncShutdownBlocker)
|
|
|
|
// nsIAsyncShutdownBlocker
|
|
nsresult PluginFinder::GetName(nsAString& aName) {
|
|
aName.AssignLiteral("PluginFinder: finding plugins to load");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP PluginFinder::BlockShutdown(
|
|
nsIAsyncShutdownClient* aBarrierClient) {
|
|
// Finding plugins isn't interruptable, but we can attempt to prevent loading
|
|
// the plugins in nsPluginHost.
|
|
mShutdown = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP PluginFinder::GetState(nsIPropertyBag** aBagOut) {
|
|
MOZ_ASSERT(aBagOut);
|
|
nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
|
|
do_CreateInstance("@mozilla.org/hash-property-bag;1");
|
|
|
|
if (NS_WARN_IF(!propertyBag)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
propertyBag->SetPropertyAsBool(NS_LITERAL_STRING("Finding"),
|
|
!mFinishedFinding);
|
|
propertyBag->SetPropertyAsBool(NS_LITERAL_STRING("CreatingList"),
|
|
mCreateList);
|
|
propertyBag->SetPropertyAsBool(NS_LITERAL_STRING("FlashOnly"), mFlashOnly);
|
|
propertyBag->SetPropertyAsBool(NS_LITERAL_STRING("HavePlugins"), !!mPlugins);
|
|
propertyBag.forget(aBagOut);
|
|
return NS_OK;
|
|
}
|
|
|
|
PluginFinder::PluginFinder(bool aFlashOnly)
|
|
: mFlashOnly(aFlashOnly),
|
|
mCreateList(false),
|
|
mPluginsChanged(false),
|
|
mFinishedFinding(false),
|
|
mCalledOnMainthread(false), // overwritten from Run()
|
|
mShutdown(false) {}
|
|
|
|
nsresult PluginFinder::DoFullSearch(const FoundPluginCallback& aCallback) {
|
|
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
|
mCreateList = true;
|
|
mFoundPluginCallback = std::move(aCallback);
|
|
|
|
nsresult rv = EnsurePluginReg();
|
|
// We pass this back to the caller, and ignore all other errors.
|
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
return rv;
|
|
}
|
|
|
|
// Figure out plugin directories:
|
|
MOZ_TRY(DeterminePluginDirs());
|
|
// If we're only interested in flash, provide an initial update from
|
|
// cache. We'll do a scan async, away from the main thread, and if
|
|
// there are updates then we'll pass them in.
|
|
if (mFlashOnly) {
|
|
ReadFlashInfo();
|
|
// Don't do a blocklist check until we're done scanning,
|
|
// as the version might change anyway.
|
|
nsTArray<std::pair<bool, RefPtr<nsPluginTag>>> arr;
|
|
mFoundPluginCallback(!!mPlugins, mPlugins, arr);
|
|
// We've passed ownership of the flash plugin to the host, so make sure
|
|
// we don't accidentally try to use it when we leave the mainthread.
|
|
mPlugins = nullptr;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PluginFinder::HavePluginsChanged(
|
|
const PluginChangeCallback& aCallback) {
|
|
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
|
mChangeCallback = std::move(aCallback);
|
|
|
|
nsresult rv = EnsurePluginReg();
|
|
// We pass this back to the caller, and ignore all other errors.
|
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
return rv;
|
|
}
|
|
|
|
// Read cached flash info if in flash-only mode.
|
|
if (mFlashOnly) {
|
|
ReadFlashInfo();
|
|
}
|
|
|
|
// Figure out plugin directories:
|
|
MOZ_TRY(DeterminePluginDirs());
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PluginFinder::Run() {
|
|
if (!mFinishedFinding) {
|
|
mCalledOnMainthread = NS_IsMainThread();
|
|
mFinishedFinding = true;
|
|
FindPlugins(); // Will call WritePluginInfo()
|
|
if (!mCalledOnMainthread) {
|
|
return NS_DispatchToMainThread(this);
|
|
}
|
|
}
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (!mShutdown) {
|
|
// Write flash info here because we can only do so when on the main thread.
|
|
// pluginreg.dat got written in FindPlugins if mPluginsChanged is true.
|
|
if (mFlashOnly && mPluginsChanged) {
|
|
WriteFlashInfo(mPlugins);
|
|
}
|
|
if (mCreateList) {
|
|
mFoundPluginCallback(mPluginsChanged, mPlugins, mPluginBlocklistRequests);
|
|
} else {
|
|
mChangeCallback(mPluginsChanged);
|
|
}
|
|
}
|
|
|
|
// The host will adopt these plugin tag instances when given them,
|
|
// so we should delete our references.
|
|
mPlugins = nullptr; // Don't iterate over these as the host needs them.
|
|
NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
|
|
NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
|
|
|
|
// We need to tell the host we're done. We're on the main thread,
|
|
// but we could still be in the process of creating the pluginhost,
|
|
// if we were called synchronously. In that case, the host is
|
|
// responsible for cleaning us up, as we can't tell it to do so.
|
|
// So if we were called *async*, tell the host we're done:
|
|
if (mCreateList && !mCalledOnMainthread) {
|
|
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
|
host->FindingFinished();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PluginFinder::ReadFlashInfo() {
|
|
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
|
|
if (!runtime) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefs = Preferences::GetRootBranch();
|
|
nsAutoCString arch;
|
|
nsresult rv = prefs->GetCharPref("plugin.flash.arch", arch);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
nsAutoCString ourArch;
|
|
rv = runtime->GetXPCOMABI(ourArch);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
// Bail if we've changed architectures.
|
|
if (!ourArch.Equals(arch)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAutoCString version;
|
|
rv = prefs->GetCharPref("plugin.flash.version", version);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
nsAutoCString path;
|
|
rv = prefs->GetCharPref("plugin.flash.path", path);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
nsCOMPtr<nsIFile> pluginFile;
|
|
#ifdef XP_WIN
|
|
// "native" files don't use utf-8 paths, convert:
|
|
rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), false,
|
|
getter_AddRefs(pluginFile));
|
|
#else
|
|
rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(pluginFile));
|
|
#endif
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
nsAutoString filename;
|
|
rv = pluginFile->GetLeafName(filename);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
int32_t blocklistState;
|
|
rv = Preferences::GetInt("plugin.flash.blockliststate", &blocklistState);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
int32_t lastModLo;
|
|
int32_t lastModHi;
|
|
rv = Preferences::GetInt("plugin.flash.lastmod_lo", &lastModLo);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
rv = Preferences::GetInt("plugin.flash.lastmod_hi", &lastModHi);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
int64_t lastMod =
|
|
(int64_t)(((uint64_t)lastModHi) << 32 | (uint32_t)lastModLo);
|
|
|
|
nsAutoCString desc;
|
|
rv = prefs->GetCharPref("plugin.flash.desc", desc);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
const char* const mimetypes[3] = {"application/x-shockwave-flash",
|
|
"application/x-futuresplash", nullptr};
|
|
const char* const extensions[3] = {"swf", "spl", nullptr};
|
|
const int32_t mimetypecount = 2;
|
|
// For some reason, the descriptions are different between Windows and
|
|
// Linux/Mac:
|
|
#ifdef XP_WIN
|
|
const char* const mimedescriptions[3] = {"Adobe Flash movie",
|
|
"FutureSplash movie", nullptr};
|
|
#else
|
|
const char* const mimedescriptions[3] = {"Shockwave Flash",
|
|
"FutureSplash Player", nullptr};
|
|
#endif
|
|
|
|
MOZ_ASSERT((!mPlugins) && (!mCachedPlugins));
|
|
// We will pass this to the host:
|
|
RefPtr<nsPluginTag> tag = new nsPluginTag(
|
|
"Shockwave Flash", desc.get(), NS_ConvertUTF16toUTF8(filename).get(),
|
|
path.get(), version.get(), mimetypes, mimedescriptions, extensions,
|
|
mimetypecount, lastMod, blocklistState, true);
|
|
mPlugins = tag;
|
|
// And keep this in cache:
|
|
RefPtr<nsPluginTag> cachedTag = new nsPluginTag(
|
|
"Shockwave Flash", desc.get(), NS_ConvertUTF16toUTF8(filename).get(),
|
|
path.get(), version.get(), mimetypes, mimedescriptions, extensions,
|
|
mimetypecount, lastMod, blocklistState, true);
|
|
mCachedPlugins = cachedTag;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */ nsresult PluginFinder::WriteFlashInfo(nsPluginTag* aPlugins) {
|
|
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
|
|
if (!runtime) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!aPlugins) {
|
|
Preferences::ClearUser("plugin.flash.arch");
|
|
Preferences::ClearUser("plugin.flash.blockliststate");
|
|
Preferences::ClearUser("plugin.flash.desc");
|
|
Preferences::ClearUser("plugin.flash.lastmod");
|
|
Preferences::ClearUser("plugin.flash.path");
|
|
Preferences::ClearUser("plugin.flash.version");
|
|
return NS_OK;
|
|
}
|
|
nsPluginTag* tag = aPlugins;
|
|
|
|
nsAutoCString arch;
|
|
nsresult rv = runtime->GetXPCOMABI(arch);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefs = Preferences::GetRootBranch();
|
|
prefs->SetCharPref("plugin.flash.arch", arch);
|
|
|
|
nsAutoCString path;
|
|
tag->GetFullpath(path);
|
|
prefs->SetCharPref("plugin.flash.path", path);
|
|
|
|
int64_t lastModifiedTime;
|
|
tag->GetLastModifiedTime(&lastModifiedTime);
|
|
uint32_t lastModHi = ((uint64_t)lastModifiedTime) >> 32;
|
|
Preferences::SetInt("plugin.flash.lastmod_hi", (int32_t)lastModHi);
|
|
uint32_t lastModLo = ((uint64_t)lastModifiedTime) & 0xffffffff;
|
|
Preferences::SetInt("plugin.flash.lastmod_lo", (int32_t)lastModLo);
|
|
|
|
nsAutoCString version;
|
|
tag->GetVersion(version);
|
|
prefs->SetCharPref("plugin.flash.version", version);
|
|
|
|
nsAutoCString desc;
|
|
tag->GetDescription(desc);
|
|
prefs->SetCharPref("plugin.flash.desc", desc);
|
|
|
|
uint32_t blocklistState;
|
|
tag->GetBlocklistState(&blocklistState);
|
|
Preferences::SetInt("plugin.flash.blockliststate", blocklistState);
|
|
return NS_OK;
|
|
}
|
|
|
|
namespace {
|
|
|
|
int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile) {
|
|
PRTime fileModTime = 0;
|
|
|
|
#if defined(XP_MACOSX)
|
|
// On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
|
|
// is a much better guide to when it was last modified than the date of
|
|
// its package directory. See bug 313700.
|
|
nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
|
|
if (localFileMac) {
|
|
localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
|
|
} else {
|
|
localfile->GetLastModifiedTime(&fileModTime);
|
|
}
|
|
#else
|
|
localfile->GetLastModifiedTime(&fileModTime);
|
|
#endif
|
|
|
|
return fileModTime;
|
|
}
|
|
|
|
struct CompareFilesByTime {
|
|
bool LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const {
|
|
return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
|
|
}
|
|
|
|
bool Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const {
|
|
return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
bool PluginFinder::ShouldAddPlugin(const nsPluginInfo& info) {
|
|
if (!info.fName ||
|
|
(strcmp(info.fName, "Shockwave Flash") != 0 && mFlashOnly)) {
|
|
return false;
|
|
}
|
|
for (uint32_t i = 0; i < info.fVariantCount; ++i) {
|
|
if (info.fMimeTypeArray[i] &&
|
|
(!strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash") ||
|
|
!strcmp(info.fMimeTypeArray[i],
|
|
"application/x-shockwave-flash-test"))) {
|
|
return true;
|
|
}
|
|
if (mFlashOnly) {
|
|
continue;
|
|
}
|
|
if (info.fMimeTypeArray[i] &&
|
|
(!strcmp(info.fMimeTypeArray[i], "application/x-test") ||
|
|
!strcmp(info.fMimeTypeArray[i], "application/x-Second-Test"))) {
|
|
return true;
|
|
}
|
|
}
|
|
#ifdef PLUGIN_LOGGING
|
|
PLUGIN_LOG(PLUGIN_LOG_NORMAL,
|
|
("ShouldAddPlugin : Ignoring non-flash plugin library %s\n",
|
|
aPluginTag->FileName().get()));
|
|
#endif // PLUGIN_LOGGING
|
|
return false;
|
|
}
|
|
|
|
nsresult PluginFinder::ScanPluginsDirectory(nsIFile* pluginsDir,
|
|
bool* aPluginsChanged) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
|
|
NS_ENSURE_ARG_POINTER(aPluginsChanged);
|
|
nsresult rv;
|
|
|
|
*aPluginsChanged = false;
|
|
|
|
#ifdef PLUGIN_LOGGING
|
|
nsAutoCString dirPath;
|
|
pluginsDir->GetNativePath(dirPath);
|
|
PLUGIN_LOG(PLUGIN_LOG_BASIC,
|
|
("PluginFinder::ScanPluginsDirectory dir=%s\n", dirPath.get()));
|
|
#endif
|
|
|
|
nsCOMPtr<nsIDirectoryEnumerator> iter;
|
|
rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
AutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
|
|
|
|
nsCOMPtr<nsIFile> dirEntry;
|
|
while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(dirEntry))) &&
|
|
dirEntry) {
|
|
// Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll
|
|
// crash. See bug 197855.
|
|
dirEntry->Normalize();
|
|
|
|
if (nsPluginsDir::IsPluginFile(dirEntry)) {
|
|
pluginFiles.AppendElement(dirEntry);
|
|
}
|
|
}
|
|
|
|
pluginFiles.Sort(CompareFilesByTime());
|
|
|
|
for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
|
|
nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
|
|
|
|
nsString utf16FilePath;
|
|
rv = localfile->GetPath(utf16FilePath);
|
|
if (NS_FAILED(rv)) continue;
|
|
|
|
const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
|
|
|
|
// Look for it in our cache
|
|
NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
|
|
RefPtr<nsPluginTag> pluginTag;
|
|
RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
|
|
|
|
bool seenBefore = false;
|
|
uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
|
|
|
|
if (pluginTag) {
|
|
seenBefore = true;
|
|
blocklistState = pluginTag->GetBlocklistState();
|
|
// If plugin changed, delete cachedPluginTag and don't use cache
|
|
if (fileModTime != pluginTag->mLastModifiedTime) {
|
|
// Plugins has changed. Don't use cached plugin info.
|
|
pluginTag = nullptr;
|
|
|
|
// plugin file changed, flag this fact
|
|
*aPluginsChanged = true;
|
|
}
|
|
|
|
// If we're not creating a list and we already know something changed then
|
|
// we're done.
|
|
if (!mCreateList) {
|
|
if (*aPluginsChanged) {
|
|
return NS_OK;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
bool isKnownInvalidPlugin = false;
|
|
for (RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
|
|
invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
|
|
// If already marked as invalid, ignore it
|
|
if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
|
|
invalidPlugins->mLastModifiedTime == fileModTime) {
|
|
if (mCreateList) {
|
|
invalidPlugins->mSeen = true;
|
|
}
|
|
isKnownInvalidPlugin = true;
|
|
break;
|
|
}
|
|
}
|
|
if (isKnownInvalidPlugin) {
|
|
continue;
|
|
}
|
|
|
|
// if it is not found in cache info list or has been changed, create a new
|
|
// one
|
|
if (!pluginTag) {
|
|
nsPluginFile pluginFile(localfile);
|
|
|
|
// create a tag describing this plugin.
|
|
PRLibrary* library = nullptr;
|
|
nsPluginInfo info;
|
|
memset(&info, 0, sizeof(info));
|
|
nsresult res;
|
|
// Opening a block for the telemetry AutoTimer
|
|
{
|
|
Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
|
|
res = pluginFile.GetPluginInfo(info, &library);
|
|
}
|
|
// if we don't have mime type don't proceed, this is not a plugin
|
|
if (NS_FAILED(res) || !info.fMimeTypeArray || !ShouldAddPlugin(info)) {
|
|
RefPtr<nsInvalidPluginTag> invalidTag =
|
|
new nsInvalidPluginTag(filePath.get(), fileModTime);
|
|
pluginFile.FreePluginInfo(info);
|
|
|
|
if (mCreateList) {
|
|
invalidTag->mSeen = true;
|
|
}
|
|
invalidTag->mNext = mInvalidPlugins;
|
|
if (mInvalidPlugins) {
|
|
mInvalidPlugins->mPrev = invalidTag;
|
|
}
|
|
mInvalidPlugins = invalidTag;
|
|
|
|
// Mark aPluginsChanged so pluginreg is rewritten
|
|
*aPluginsChanged = true;
|
|
continue;
|
|
}
|
|
|
|
pluginTag = new nsPluginTag(&info, fileModTime, blocklistState);
|
|
pluginTag->mLibrary = library;
|
|
pluginFile.FreePluginInfo(info);
|
|
|
|
// We'll need to do a blocklist request later.
|
|
mPluginBlocklistRequests.AppendElement(
|
|
std::make_pair(!seenBefore, pluginTag));
|
|
|
|
// Plugin unloading is tag-based. If we created a new tag and loaded
|
|
// the library in the process then we want to attempt to unload it here.
|
|
// Only do this if the pref is set for aggressive unloading.
|
|
if (UnloadPluginsASAP()) {
|
|
if (NS_IsMainThread()) {
|
|
pluginTag->TryUnloadPlugin(false);
|
|
} else {
|
|
nsCOMPtr<nsIRunnable> unloadRunnable =
|
|
mozilla::NewRunnableMethod<bool>(
|
|
"nsPluginTag::TryUnloadPlugin", pluginTag,
|
|
&nsPluginTag::TryUnloadPlugin, false);
|
|
NS_DispatchToMainThread(unloadRunnable);
|
|
}
|
|
}
|
|
}
|
|
|
|
// do it if we still want it
|
|
if (!seenBefore) {
|
|
// We have a valid new plugin so report that plugins have changed.
|
|
*aPluginsChanged = true;
|
|
}
|
|
|
|
// Don't add the same plugin again if it hasn't changed
|
|
if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
|
|
if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If we're not creating a plugin list, simply looking for changes,
|
|
// then we're done.
|
|
if (!mCreateList) {
|
|
return NS_OK;
|
|
}
|
|
|
|
pluginTag->mNext = mPlugins;
|
|
mPlugins = pluginTag;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// if mCreateList is false we will just scan for plugins
|
|
// and see if any changes have been made to the plugins.
|
|
// This is needed in ReloadPlugins to prevent possible recursive reloads
|
|
nsresult PluginFinder::FindPlugins() {
|
|
Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
|
|
|
|
// Read cached plugins info. We ignore failures, as we can proceed
|
|
// without a pluginreg file.
|
|
ReadPluginInfo();
|
|
|
|
for (nsIFile* pluginDir : mPluginDirs) {
|
|
if (!pluginDir) {
|
|
continue;
|
|
}
|
|
// Ensure we have a directory; if this isn't a directory, try the parent.
|
|
// We do this because in some cases items in this list of supposed plugin
|
|
// directories can be individual plugin files instead of directories.
|
|
bool isDir = false;
|
|
nsCOMPtr<nsIFile> parent;
|
|
if (NS_FAILED(pluginDir->IsDirectory(&isDir)) || !isDir) {
|
|
pluginDir->GetParent(getter_AddRefs(parent));
|
|
pluginDir = parent;
|
|
if (!pluginDir) {
|
|
continue;
|
|
}
|
|
}
|
|
// pluginDir not existing will be handled transparently in
|
|
// ScanPluginsDirectory
|
|
|
|
// don't pass mPluginsChanged directly, to prevent its
|
|
// possible reset in subsequent ScanPluginsDirectory calls
|
|
bool pluginschanged = false;
|
|
ScanPluginsDirectory(pluginDir, &pluginschanged);
|
|
|
|
if (pluginschanged) {
|
|
mPluginsChanged = true;
|
|
if (!mCreateList) {
|
|
// We're just looking for changes, so we can stop looking.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we are just looking for possible changes,
|
|
// no need to proceed if changes are detected
|
|
if (!mCreateList && mPluginsChanged) {
|
|
NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
|
|
NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
|
|
return NS_OK;
|
|
}
|
|
|
|
// We should also consider plugins to have changed if any plugins have been
|
|
// removed. We'll know if any were removed if they weren't taken out of the
|
|
// cached plugins list during our scan, thus we can assume something was
|
|
// removed if the cached plugins list contains anything.
|
|
if (!mPluginsChanged && mCachedPlugins) {
|
|
mPluginsChanged = true;
|
|
}
|
|
|
|
// Remove unseen invalid plugins
|
|
RefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
|
|
while (invalidPlugins) {
|
|
if (!invalidPlugins->mSeen) {
|
|
RefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
|
|
|
|
if (invalidPlugin->mPrev) {
|
|
invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
|
|
} else {
|
|
mInvalidPlugins = invalidPlugin->mNext;
|
|
}
|
|
if (invalidPlugin->mNext) {
|
|
invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
|
|
}
|
|
|
|
invalidPlugins = invalidPlugin->mNext;
|
|
|
|
invalidPlugin->mPrev = nullptr;
|
|
invalidPlugin->mNext = nullptr;
|
|
} else {
|
|
invalidPlugins->mSeen = false;
|
|
invalidPlugins = invalidPlugins->mNext;
|
|
}
|
|
}
|
|
|
|
// if we are not creating the list, there is no need to proceed
|
|
if (!mCreateList) {
|
|
NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
|
|
NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
|
|
return NS_OK;
|
|
}
|
|
|
|
// if we are creating the list, it is already done;
|
|
// update the plugins info cache if changes are detected
|
|
if (mPluginsChanged) {
|
|
WritePluginInfo(mFlashOnly, mPlugins, mInvalidPlugins);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */ nsresult PluginFinder::WritePluginInfo(
|
|
bool aFlashOnly, nsPluginTag* aPlugins,
|
|
nsInvalidPluginTag* aInvalidPlugins) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
// We can't write to prefs from non-main threads. Run() will update prefs
|
|
// from the main thread if we're flash only.
|
|
// We can't separate this out completely because the blocklist updates
|
|
// from nsPluginHost expect to be able to write here.
|
|
if (NS_IsMainThread() && aFlashOnly) {
|
|
WriteFlashInfo(aPlugins);
|
|
}
|
|
|
|
if (!sPluginRegFile) return NS_ERROR_FAILURE;
|
|
|
|
// Get the tmp file by getting the parent and then re-appending
|
|
// kPluginRegistryFilename followed by `.tmp`.
|
|
nsCOMPtr<nsIFile> pluginReg;
|
|
nsresult rv = sPluginRegFile->GetParent(getter_AddRefs(pluginReg));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsAutoCString filename(kPluginRegistryFilename);
|
|
filename.AppendLiteral(".tmp");
|
|
rv = pluginReg->AppendNative(filename);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
PRFileDesc* fd = nullptr;
|
|
rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
|
0600, &fd);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
|
|
if (!runtime) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsAutoCString arch;
|
|
rv = runtime->GetXPCOMABI(arch);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
PR_fprintf(fd, "Generated File. Do not edit.\n");
|
|
|
|
PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c%c\nArch%c%s%c%c\n",
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, kPluginRegistryVersion,
|
|
aFlashOnly ? 't' : 'f', PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER,
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, arch.get(),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
|
|
|
// Store all plugins in the mPlugins list - all plugins currently in use.
|
|
PR_fprintf(fd, "\n[PLUGINS]\n");
|
|
|
|
// Do not write plugin info if we're in flash-only mode.
|
|
for (nsPluginTag* tag = aPlugins; !aFlashOnly && tag; tag = tag->mNext) {
|
|
// store each plugin info into the registry
|
|
// filename & fullpath are on separate line
|
|
// because they can contain field delimiter char
|
|
PR_fprintf(
|
|
fd, "%s%c%c\n%s%c%c\n%s%c%c\n", (tag->FileName().get()),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, PLUGIN_REGISTRY_END_OF_LINE_MARKER,
|
|
(tag->mFullPath.get()), PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER, (tag->Version().get()),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
|
|
|
// lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension|blocklistState
|
|
PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%d%c%c\n", tag->mLastModifiedTime,
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
false, // did store whether or not to unload in-process plugins
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
0, // legacy field for flags
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, false,
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, tag->BlocklistState(),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
|
|
|
// description, name & mtypecount are on separate line
|
|
PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n", (tag->Description().get()),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER, (tag->Name().get()),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER, tag->MimeTypes().Length());
|
|
|
|
// Add in each mimetype this plugin supports
|
|
for (uint32_t i = 0; i < tag->MimeTypes().Length(); i++) {
|
|
PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n", i, PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
(tag->MimeTypes()[i].get()), PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
(tag->MimeDescriptions()[i].get()),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, (tag->Extensions()[i].get()),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
|
}
|
|
}
|
|
|
|
PR_fprintf(fd, "\n[INVALID]\n");
|
|
|
|
RefPtr<nsInvalidPluginTag> invalidPlugins = aInvalidPlugins;
|
|
while (invalidPlugins) {
|
|
// fullPath
|
|
PR_fprintf(
|
|
fd, "%s%c%c\n",
|
|
(!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get()
|
|
: ""),
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER, PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
|
|
|
// lastModifiedTimeStamp
|
|
PR_fprintf(fd, "%lld%c%c\n", invalidPlugins->mLastModifiedTime,
|
|
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
|
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
|
|
|
invalidPlugins = invalidPlugins->mNext;
|
|
}
|
|
|
|
PRStatus prrc;
|
|
prrc = PR_Close(fd);
|
|
if (prrc != PR_SUCCESS) {
|
|
// we should obtain a refined value based on prrc;
|
|
rv = NS_ERROR_FAILURE;
|
|
MOZ_ASSERT(false, "PR_Close() failed.");
|
|
return rv;
|
|
}
|
|
rv = pluginReg->MoveToNative(nullptr, kPluginRegistryFilename);
|
|
return rv;
|
|
}
|
|
|
|
/* static */ nsresult PluginFinder::EnsurePluginReg() {
|
|
if (sPluginRegFile) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!NS_IsMainThread()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIProperties> directoryService(
|
|
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIFile> pluginRegFile;
|
|
directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
|
|
getter_AddRefs(pluginRegFile));
|
|
|
|
if (!pluginRegFile) {
|
|
// There is no profile yet, this will tell us if there is going to be one
|
|
// in the future.
|
|
directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
|
|
getter_AddRefs(pluginRegFile));
|
|
if (!pluginRegFile) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
pluginRegFile->AppendNative(kPluginRegistryFilename);
|
|
sPluginRegFile = pluginRegFile;
|
|
ClearOnShutdown(&sPluginRegFile);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PluginFinder::DeterminePluginDirs() {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIProperties> dirService(
|
|
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// Get the app-defined list of plugin dirs.
|
|
nsCOMPtr<nsISimpleEnumerator> dirEnum;
|
|
MOZ_TRY(dirService->Get(NS_APP_PLUGINS_DIR_LIST,
|
|
NS_GET_IID(nsISimpleEnumerator),
|
|
getter_AddRefs(dirEnum)));
|
|
|
|
bool hasMore = false;
|
|
while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
|
|
nsCOMPtr<nsISupports> supports;
|
|
nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mPluginDirs.AppendElement(nextDir);
|
|
}
|
|
}
|
|
}
|
|
|
|
// In tests, load plugins from the profile.
|
|
if (xpc::IsInAutomation()) {
|
|
nsCOMPtr<nsIFile> profDir;
|
|
rv = dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
|
|
getter_AddRefs(profDir));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
profDir->Append(NS_LITERAL_STRING("plugins"));
|
|
mPluginDirs.AppendElement(profDir);
|
|
}
|
|
}
|
|
|
|
// Get the directories from the windows registry. Note: these can
|
|
// be files instead of directories. We'll have to filter them later.
|
|
#ifdef XP_WIN
|
|
bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
|
|
if (bScanPLIDs) {
|
|
GetPLIDDirectories(mPluginDirs);
|
|
}
|
|
#endif /* XP_WIN */
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PluginFinder::ReadPluginInfo() {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
// Non-flash-only (for tests that use other plugins) and updates to the list
|
|
// sadly still use the main thread - but assert about the flash startup case:
|
|
if (mFlashOnly && mCreateList) {
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
} else {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
nsresult rv = EnsurePluginReg();
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// Cloning ensures we don't have a stat cache and get an
|
|
// accurate filesize.
|
|
nsCOMPtr<nsIFile> pluginReg;
|
|
rv = sPluginRegFile->Clone(getter_AddRefs(pluginReg));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
int64_t fileSize;
|
|
rv = pluginReg->GetFileSize(&fileSize);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (fileSize > INT32_MAX) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
int32_t flen = int32_t(fileSize);
|
|
if (flen == 0) {
|
|
NS_WARNING("Plugins Registry Empty!");
|
|
return NS_OK; // ERROR CONDITION
|
|
}
|
|
|
|
nsPluginManifestLineReader reader;
|
|
char* registry = reader.Init(flen);
|
|
if (!registry) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
PRFileDesc* fd = nullptr;
|
|
rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// set rv to return an error on goto out
|
|
rv = NS_ERROR_FAILURE;
|
|
|
|
// We know how many octes we are supposed to read.
|
|
// So let use the busy_beaver_PR_Read version.
|
|
int32_t bread = busy_beaver_PR_Read(fd, registry, flen);
|
|
|
|
PRStatus prrc;
|
|
prrc = PR_Close(fd);
|
|
if (prrc != PR_SUCCESS) {
|
|
// Strange error: this is one of those "Should not happen" error.
|
|
// we may want to report something more refined than NS_ERROR_FAILURE.
|
|
MOZ_ASSERT(false, "PR_Close() failed.");
|
|
return rv;
|
|
}
|
|
|
|
// short read error, so to speak.
|
|
if (flen > bread) return rv;
|
|
|
|
if (!ReadSectionHeader(reader, "HEADER")) return rv;
|
|
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
char* values[6];
|
|
|
|
// VersionLiteral, kPluginRegistryVersion
|
|
if (2 != reader.ParseLine(values, 2)) return rv;
|
|
|
|
// VersionLiteral
|
|
if (PL_strcmp(values[0], "Version")) return rv;
|
|
|
|
// If we're reading an old registry, ignore it
|
|
// If we flipped the flash-only pref, ignore it
|
|
nsAutoCString expectedVersion(kPluginRegistryVersion);
|
|
expectedVersion.Append(mFlashOnly ? 't' : 'f');
|
|
|
|
if (!expectedVersion.Equals(values[1])) {
|
|
return rv;
|
|
}
|
|
|
|
char* archValues[6];
|
|
if (!reader.NextLine()) {
|
|
return rv;
|
|
}
|
|
|
|
// ArchLiteral, Architecture
|
|
if (2 != reader.ParseLine(archValues, 2)) {
|
|
return rv;
|
|
}
|
|
|
|
// ArchLiteral
|
|
if (PL_strcmp(archValues[0], "Arch")) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
|
|
if (!runtime) {
|
|
return rv;
|
|
}
|
|
|
|
nsAutoCString arch;
|
|
if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
|
|
return rv;
|
|
}
|
|
|
|
// If this is a registry from a different architecture then don't attempt to
|
|
// read it
|
|
if (PL_strcmp(archValues[1], arch.get())) {
|
|
return rv;
|
|
}
|
|
|
|
if (!ReadSectionHeader(reader, "PLUGINS")) return rv;
|
|
|
|
while (reader.NextLine()) {
|
|
if (*reader.LinePtr() == '[') {
|
|
break;
|
|
}
|
|
// Ignore all listed plugins if we're in flash-only mode.
|
|
// In that case, we're only reading this file to find invalid plugin info
|
|
// so we can avoid reading/loading those.
|
|
if (mFlashOnly) {
|
|
continue;
|
|
}
|
|
|
|
const char* filename = reader.LinePtr();
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
const char* fullpath = reader.LinePtr();
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
const char* version;
|
|
version = reader.LinePtr();
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
// lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension|blocklistState
|
|
if (5 != reader.ParseLine(values, 5)) return rv;
|
|
|
|
int64_t lastmod = nsCRT::atoll(values[0]);
|
|
uint16_t blocklistState = atoi(values[4]);
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
char* description = reader.LinePtr();
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
const char* name = reader.LinePtr();
|
|
if (!reader.NextLine()) return rv;
|
|
|
|
const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
|
|
const long PLUGIN_REG_MAX_MIMETYPES = 1000;
|
|
|
|
long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
|
|
if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
|
|
mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
char* stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
|
|
char** mimetypes;
|
|
char** mimedescriptions;
|
|
char** extensions;
|
|
char** heapalloced = 0;
|
|
if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
|
|
heapalloced = new char*[mimetypecount * 3];
|
|
mimetypes = heapalloced;
|
|
} else {
|
|
mimetypes = stackalloced;
|
|
}
|
|
mimedescriptions = mimetypes + mimetypecount;
|
|
extensions = mimedescriptions + mimetypecount;
|
|
|
|
int mtr = 0; // mimetype read
|
|
for (; mtr < mimetypecount; mtr++) {
|
|
if (!reader.NextLine()) break;
|
|
|
|
// line number|mimetype|description|extension
|
|
if (4 != reader.ParseLine(values, 4)) break;
|
|
int line = atoi(values[0]);
|
|
if (line != mtr) break;
|
|
mimetypes[mtr] = values[1];
|
|
mimedescriptions[mtr] = values[2];
|
|
extensions[mtr] = values[3];
|
|
}
|
|
|
|
if (mtr != mimetypecount) {
|
|
delete[] heapalloced;
|
|
return rv;
|
|
}
|
|
|
|
RefPtr<nsPluginTag> tag = new nsPluginTag(
|
|
name, description, filename, fullpath, version,
|
|
(const char* const*)mimetypes, (const char* const*)mimedescriptions,
|
|
(const char* const*)extensions, mimetypecount, lastmod, blocklistState,
|
|
true);
|
|
|
|
delete[] heapalloced;
|
|
|
|
// Import flags from registry into prefs for old registry versions
|
|
MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
|
|
("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n",
|
|
tag->FileName().get()));
|
|
|
|
tag->mNext = mCachedPlugins;
|
|
mCachedPlugins = tag;
|
|
}
|
|
|
|
if (!ReadSectionHeader(reader, "INVALID")) {
|
|
return rv;
|
|
}
|
|
|
|
while (reader.NextLine()) {
|
|
const char* fullpath = reader.LinePtr();
|
|
if (!reader.NextLine()) {
|
|
return rv;
|
|
}
|
|
|
|
const char* lastModifiedTimeStamp = reader.LinePtr();
|
|
int64_t lastmod = nsCRT::atoll(lastModifiedTimeStamp);
|
|
|
|
RefPtr<nsInvalidPluginTag> invalidTag =
|
|
new nsInvalidPluginTag(fullpath, lastmod);
|
|
|
|
invalidTag->mNext = mInvalidPlugins;
|
|
if (mInvalidPlugins) {
|
|
mInvalidPlugins->mPrev = invalidTag;
|
|
}
|
|
mInvalidPlugins = invalidTag;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void PluginFinder::RemoveCachedPluginsInfo(const char* filePath,
|
|
nsPluginTag** result) {
|
|
RefPtr<nsPluginTag> prev;
|
|
RefPtr<nsPluginTag> tag = mCachedPlugins;
|
|
while (tag) {
|
|
if (tag->mFullPath.Equals(filePath)) {
|
|
// Found it. Remove it from our list
|
|
if (prev)
|
|
prev->mNext = tag->mNext;
|
|
else
|
|
mCachedPlugins = tag->mNext;
|
|
tag->mNext = nullptr;
|
|
*result = tag;
|
|
NS_ADDREF(*result);
|
|
break;
|
|
}
|
|
prev = tag;
|
|
tag = tag->mNext;
|
|
}
|
|
}
|