diff options
Diffstat (limited to 'webkit/glue/plugins/plugin_lib_posix.cc')
-rw-r--r-- | webkit/glue/plugins/plugin_lib_posix.cc | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/webkit/glue/plugins/plugin_lib_posix.cc b/webkit/glue/plugins/plugin_lib_posix.cc new file mode 100644 index 0000000..dbc64ed --- /dev/null +++ b/webkit/glue/plugins/plugin_lib_posix.cc @@ -0,0 +1,255 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/glue/plugins/plugin_lib.h" + +#include <dlfcn.h> +#if defined(OS_OPENBSD) +#include <sys/exec_elf.h> +#else +#include <elf.h> +#include <fcntl.h> +#endif +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/eintr_wrapper.h" +#include "base/file_util.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/utf_string_conversions.h" +#include "webkit/glue/plugins/plugin_list.h" + +// These headers must be included in this order to make the declaration gods +// happy. +#include "base/third_party/nspr/prcpucfg_linux.h" + +namespace { + +using NPAPI::PluginList; + +// Copied from nsplugindefs.h instead of including the file since it has a bunch +// of dependencies. +enum nsPluginVariable { + nsPluginVariable_NameString = 1, + nsPluginVariable_DescriptionString = 2 +}; + +// Read the ELF header and return true if it is usable on +// the current architecture (e.g. 32-bit ELF on 32-bit build). +// Returns false on other errors as well. +bool ELFMatchesCurrentArchitecture(const FilePath& filename) { + // First make sure we can open the file and it is in fact, a regular file. + struct stat stat_buf; + // Open with O_NONBLOCK so we don't block on pipes. + int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK); + if (fd < 0) + return false; + bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode)); + if (HANDLE_EINTR(close(fd)) < 0) + return false; + if (!ret) + return false; + + const size_t kELFBufferSize = 5; + char buffer[kELFBufferSize]; + if (!file_util::ReadFile(filename, buffer, kELFBufferSize)) + return false; + + if (buffer[0] != ELFMAG0 || + buffer[1] != ELFMAG1 || + buffer[2] != ELFMAG2 || + buffer[3] != ELFMAG3) { + // Not an ELF file, perhaps? + return false; + } + + int elf_class = buffer[EI_CLASS]; +#if defined(ARCH_CPU_32_BITS) + if (elf_class == ELFCLASS32) + return true; +#elif defined(ARCH_CPU_64_BITS) + if (elf_class == ELFCLASS64) + return true; +#endif + + return false; +} + +// This structure matches enough of nspluginwrapper's NPW_PluginInfo +// for us to extract the real plugin path. +struct __attribute__((packed)) NSPluginWrapperInfo { + char ident[32]; // NSPluginWrapper magic identifier (includes version). + char path[PATH_MAX]; // Path to wrapped plugin. +}; + +// Test a plugin for whether it's been wrapped by NSPluginWrapper, and +// if so attempt to unwrap it. Pass in an opened plugin handle; on +// success, |dl| and |unwrapped_path| will be filled in with the newly +// opened plugin. On failure, params are left unmodified. +void UnwrapNSPluginWrapper(void **dl, FilePath* unwrapped_path) { + NSPluginWrapperInfo* info = + reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin")); + if (!info) + return; // Not a NSPW plugin. + + // Here we could check the NSPW ident field for the versioning + // information, but the path field is available in all versions + // anyway. + + // Grab the path to the wrapped plugin. Just in case the structure + // format changes, protect against the path not being null-terminated. + char* path_end = static_cast<char*>(memchr(info->path, '\0', + sizeof(info->path))); + if (!path_end) + path_end = info->path + sizeof(info->path); + FilePath path = FilePath(std::string(info->path, path_end - info->path)); + + if (!ELFMatchesCurrentArchitecture(path)) { + LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a " + << "plugin for a different architecture; it will " + << "work better if you instead use a native plugin."; + return; + } + + void* newdl = base::LoadNativeLibrary(path); + if (!newdl) { + // We couldn't load the unwrapped plugin for some reason, despite + // being able to load the wrapped one. Just use the wrapped one. + LOG_IF(INFO, PluginList::DebugPluginLoading()) + << "Could not use unwrapped nspluginwrapper plugin " + << unwrapped_path->value() << ", using the wrapped one."; + return; + } + + // Unload the wrapped plugin, and use the wrapped plugin instead. + LOG_IF(INFO, PluginList::DebugPluginLoading()) + << "Using unwrapped version " << unwrapped_path->value() + << " of nspluginwrapper-wrapped plugin."; + base::UnloadNativeLibrary(*dl); + *dl = newdl; + *unwrapped_path = path; +} + +} // anonymous namespace + +namespace NPAPI { + +bool PluginLib::ReadWebPluginInfo(const FilePath& filename, + WebPluginInfo* info) { + // The file to reference is: + // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUnix.cpp + + // Skip files that aren't appropriate for our architecture. + if (!ELFMatchesCurrentArchitecture(filename)) { + LOG_IF(INFO, PluginList::DebugPluginLoading()) + << "Skipping plugin " << filename.value() + << " because it doesn't match the current architecture."; + return false; + } + + void* dl = base::LoadNativeLibrary(filename); + if (!dl) { + LOG_IF(INFO, PluginList::DebugPluginLoading()) + << "While reading plugin info, unable to load library " + << filename.value() << ", skipping."; + return false; + } + + info->path = filename; + info->enabled = true; + + // Attempt to swap in the wrapped plugin if this is nspluginwrapper. + UnwrapNSPluginWrapper(&dl, &info->path); + + // See comments in plugin_lib_mac regarding this symbol. + typedef const char* (*NP_GetMimeDescriptionType)(); + NP_GetMimeDescriptionType NP_GetMIMEDescription = + reinterpret_cast<NP_GetMimeDescriptionType>( + dlsym(dl, "NP_GetMIMEDescription")); + const char* mime_description = NULL; + if (NP_GetMIMEDescription) + mime_description = NP_GetMIMEDescription(); + + if (mime_description) + ParseMIMEDescription(mime_description, &info->mime_types); + + // The plugin name and description live behind NP_GetValue calls. + typedef NPError (*NP_GetValueType)(void* unused, + nsPluginVariable variable, + void* value_out); + NP_GetValueType NP_GetValue = + reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue")); + if (NP_GetValue) { + const char* name = NULL; + NP_GetValue(NULL, nsPluginVariable_NameString, &name); + if (name) + info->name = UTF8ToUTF16(name); + + const char* description = NULL; + NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description); + if (description) + info->desc = UTF8ToUTF16(description); + + LOG_IF(INFO, PluginList::DebugPluginLoading()) + << "Got info for plugin " << filename.value() + << " Name = \"" << UTF16ToUTF8(info->name) + << "\", Description = \"" << UTF16ToUTF8(info->desc) << "\"."; + } else { + LOG_IF(INFO, PluginList::DebugPluginLoading()) + << "Plugin " << filename.value() + << " has no GetValue() and probably won't work."; + } + + // Intentionally not unloading the plugin here, it can lead to crashes. + + return true; +} + +// static +void PluginLib::ParseMIMEDescription( + const std::string& description, + std::vector<WebPluginMimeType>* mime_types) { + // We parse the description here into WebPluginMimeType structures. + // Naively from the NPAPI docs you'd think you could use + // string-splitting, but the Firefox parser turns out to do something + // different: find the first colon, then the second, then a semi. + // + // See ParsePluginMimeDescription near + // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUtils.h#53 + + std::string::size_type ofs = 0; + for (;;) { + WebPluginMimeType mime_type; + + std::string::size_type end = description.find(':', ofs); + if (end == std::string::npos) + break; + mime_type.mime_type = description.substr(ofs, end - ofs); + ofs = end + 1; + + end = description.find(':', ofs); + if (end == std::string::npos) + break; + const std::string extensions = description.substr(ofs, end - ofs); + SplitString(extensions, ',', &mime_type.file_extensions); + ofs = end + 1; + + end = description.find(';', ofs); + // It's ok for end to run off the string here. If there's no + // trailing semicolon we consume the remainder of the string. + if (end != std::string::npos) { + mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs)); + } else { + mime_type.description = UTF8ToUTF16(description.substr(ofs)); + } + mime_types->push_back(mime_type); + if (end == std::string::npos) + break; + ofs = end + 1; + } +} + +} // namespace NPAPI |