| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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 "webkit/glue/plugins/plugin_lib.h" | |
| 6 | |
| 7 #include <dlfcn.h> | |
| 8 #if defined(OS_OPENBSD) | |
| 9 #include <sys/exec_elf.h> | |
| 10 #else | |
| 11 #include <elf.h> | |
| 12 #include <fcntl.h> | |
| 13 #endif | |
| 14 #include <sys/stat.h> | |
| 15 #include <sys/types.h> | |
| 16 #include <unistd.h> | |
| 17 | |
| 18 #include "base/eintr_wrapper.h" | |
| 19 #include "base/file_util.h" | |
| 20 #include "base/string_split.h" | |
| 21 #include "base/string_util.h" | |
| 22 #include "base/sys_string_conversions.h" | |
| 23 #include "base/utf_string_conversions.h" | |
| 24 #include "webkit/glue/plugins/plugin_list.h" | |
| 25 | |
| 26 // These headers must be included in this order to make the declaration gods | |
| 27 // happy. | |
| 28 #include "base/third_party/nspr/prcpucfg_linux.h" | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 using NPAPI::PluginList; | |
| 33 | |
| 34 // Copied from nsplugindefs.h instead of including the file since it has a bunch | |
| 35 // of dependencies. | |
| 36 enum nsPluginVariable { | |
| 37 nsPluginVariable_NameString = 1, | |
| 38 nsPluginVariable_DescriptionString = 2 | |
| 39 }; | |
| 40 | |
| 41 // Read the ELF header and return true if it is usable on | |
| 42 // the current architecture (e.g. 32-bit ELF on 32-bit build). | |
| 43 // Returns false on other errors as well. | |
| 44 bool ELFMatchesCurrentArchitecture(const FilePath& filename) { | |
| 45 // First make sure we can open the file and it is in fact, a regular file. | |
| 46 struct stat stat_buf; | |
| 47 // Open with O_NONBLOCK so we don't block on pipes. | |
| 48 int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK); | |
| 49 if (fd < 0) | |
| 50 return false; | |
| 51 bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode)); | |
| 52 if (HANDLE_EINTR(close(fd)) < 0) | |
| 53 return false; | |
| 54 if (!ret) | |
| 55 return false; | |
| 56 | |
| 57 const size_t kELFBufferSize = 5; | |
| 58 char buffer[kELFBufferSize]; | |
| 59 if (!file_util::ReadFile(filename, buffer, kELFBufferSize)) | |
| 60 return false; | |
| 61 | |
| 62 if (buffer[0] != ELFMAG0 || | |
| 63 buffer[1] != ELFMAG1 || | |
| 64 buffer[2] != ELFMAG2 || | |
| 65 buffer[3] != ELFMAG3) { | |
| 66 // Not an ELF file, perhaps? | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 int elf_class = buffer[EI_CLASS]; | |
| 71 #if defined(ARCH_CPU_32_BITS) | |
| 72 if (elf_class == ELFCLASS32) | |
| 73 return true; | |
| 74 #elif defined(ARCH_CPU_64_BITS) | |
| 75 if (elf_class == ELFCLASS64) | |
| 76 return true; | |
| 77 #endif | |
| 78 | |
| 79 return false; | |
| 80 } | |
| 81 | |
| 82 // This structure matches enough of nspluginwrapper's NPW_PluginInfo | |
| 83 // for us to extract the real plugin path. | |
| 84 struct __attribute__((packed)) NSPluginWrapperInfo { | |
| 85 char ident[32]; // NSPluginWrapper magic identifier (includes version). | |
| 86 char path[PATH_MAX]; // Path to wrapped plugin. | |
| 87 }; | |
| 88 | |
| 89 // Test a plugin for whether it's been wrapped by NSPluginWrapper, and | |
| 90 // if so attempt to unwrap it. Pass in an opened plugin handle; on | |
| 91 // success, |dl| and |unwrapped_path| will be filled in with the newly | |
| 92 // opened plugin. On failure, params are left unmodified. | |
| 93 void UnwrapNSPluginWrapper(void **dl, FilePath* unwrapped_path) { | |
| 94 NSPluginWrapperInfo* info = | |
| 95 reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin")); | |
| 96 if (!info) | |
| 97 return; // Not a NSPW plugin. | |
| 98 | |
| 99 // Here we could check the NSPW ident field for the versioning | |
| 100 // information, but the path field is available in all versions | |
| 101 // anyway. | |
| 102 | |
| 103 // Grab the path to the wrapped plugin. Just in case the structure | |
| 104 // format changes, protect against the path not being null-terminated. | |
| 105 char* path_end = static_cast<char*>(memchr(info->path, '\0', | |
| 106 sizeof(info->path))); | |
| 107 if (!path_end) | |
| 108 path_end = info->path + sizeof(info->path); | |
| 109 FilePath path = FilePath(std::string(info->path, path_end - info->path)); | |
| 110 | |
| 111 if (!ELFMatchesCurrentArchitecture(path)) { | |
| 112 LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a " | |
| 113 << "plugin for a different architecture; it will " | |
| 114 << "work better if you instead use a native plugin."; | |
| 115 return; | |
| 116 } | |
| 117 | |
| 118 void* newdl = base::LoadNativeLibrary(path); | |
| 119 if (!newdl) { | |
| 120 // We couldn't load the unwrapped plugin for some reason, despite | |
| 121 // being able to load the wrapped one. Just use the wrapped one. | |
| 122 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 123 << "Could not use unwrapped nspluginwrapper plugin " | |
| 124 << unwrapped_path->value() << ", using the wrapped one."; | |
| 125 return; | |
| 126 } | |
| 127 | |
| 128 // Unload the wrapped plugin, and use the wrapped plugin instead. | |
| 129 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 130 << "Using unwrapped version " << unwrapped_path->value() | |
| 131 << " of nspluginwrapper-wrapped plugin."; | |
| 132 base::UnloadNativeLibrary(*dl); | |
| 133 *dl = newdl; | |
| 134 *unwrapped_path = path; | |
| 135 } | |
| 136 | |
| 137 } // anonymous namespace | |
| 138 | |
| 139 namespace NPAPI { | |
| 140 | |
| 141 bool PluginLib::ReadWebPluginInfo(const FilePath& filename, | |
| 142 WebPluginInfo* info) { | |
| 143 // The file to reference is: | |
| 144 // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirU
nix.cpp | |
| 145 | |
| 146 // Skip files that aren't appropriate for our architecture. | |
| 147 if (!ELFMatchesCurrentArchitecture(filename)) { | |
| 148 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 149 << "Skipping plugin " << filename.value() | |
| 150 << " because it doesn't match the current architecture."; | |
| 151 return false; | |
| 152 } | |
| 153 | |
| 154 void* dl = base::LoadNativeLibrary(filename); | |
| 155 if (!dl) { | |
| 156 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 157 << "While reading plugin info, unable to load library " | |
| 158 << filename.value() << ", skipping."; | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 info->path = filename; | |
| 163 info->enabled = true; | |
| 164 | |
| 165 // Attempt to swap in the wrapped plugin if this is nspluginwrapper. | |
| 166 UnwrapNSPluginWrapper(&dl, &info->path); | |
| 167 | |
| 168 // See comments in plugin_lib_mac regarding this symbol. | |
| 169 typedef const char* (*NP_GetMimeDescriptionType)(); | |
| 170 NP_GetMimeDescriptionType NP_GetMIMEDescription = | |
| 171 reinterpret_cast<NP_GetMimeDescriptionType>( | |
| 172 dlsym(dl, "NP_GetMIMEDescription")); | |
| 173 const char* mime_description = NULL; | |
| 174 if (NP_GetMIMEDescription) | |
| 175 mime_description = NP_GetMIMEDescription(); | |
| 176 | |
| 177 if (mime_description) | |
| 178 ParseMIMEDescription(mime_description, &info->mime_types); | |
| 179 | |
| 180 // The plugin name and description live behind NP_GetValue calls. | |
| 181 typedef NPError (*NP_GetValueType)(void* unused, | |
| 182 nsPluginVariable variable, | |
| 183 void* value_out); | |
| 184 NP_GetValueType NP_GetValue = | |
| 185 reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue")); | |
| 186 if (NP_GetValue) { | |
| 187 const char* name = NULL; | |
| 188 NP_GetValue(NULL, nsPluginVariable_NameString, &name); | |
| 189 if (name) | |
| 190 info->name = UTF8ToUTF16(name); | |
| 191 | |
| 192 const char* description = NULL; | |
| 193 NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description); | |
| 194 if (description) | |
| 195 info->desc = UTF8ToUTF16(description); | |
| 196 | |
| 197 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 198 << "Got info for plugin " << filename.value() | |
| 199 << " Name = \"" << UTF16ToUTF8(info->name) | |
| 200 << "\", Description = \"" << UTF16ToUTF8(info->desc) << "\"."; | |
| 201 } else { | |
| 202 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 203 << "Plugin " << filename.value() | |
| 204 << " has no GetValue() and probably won't work."; | |
| 205 } | |
| 206 | |
| 207 // Intentionally not unloading the plugin here, it can lead to crashes. | |
| 208 | |
| 209 return true; | |
| 210 } | |
| 211 | |
| 212 // static | |
| 213 void PluginLib::ParseMIMEDescription( | |
| 214 const std::string& description, | |
| 215 std::vector<WebPluginMimeType>* mime_types) { | |
| 216 // We parse the description here into WebPluginMimeType structures. | |
| 217 // Naively from the NPAPI docs you'd think you could use | |
| 218 // string-splitting, but the Firefox parser turns out to do something | |
| 219 // different: find the first colon, then the second, then a semi. | |
| 220 // | |
| 221 // See ParsePluginMimeDescription near | |
| 222 // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirU
tils.h#53 | |
| 223 | |
| 224 std::string::size_type ofs = 0; | |
| 225 for (;;) { | |
| 226 WebPluginMimeType mime_type; | |
| 227 | |
| 228 std::string::size_type end = description.find(':', ofs); | |
| 229 if (end == std::string::npos) | |
| 230 break; | |
| 231 mime_type.mime_type = description.substr(ofs, end - ofs); | |
| 232 ofs = end + 1; | |
| 233 | |
| 234 end = description.find(':', ofs); | |
| 235 if (end == std::string::npos) | |
| 236 break; | |
| 237 const std::string extensions = description.substr(ofs, end - ofs); | |
| 238 base::SplitString(extensions, ',', &mime_type.file_extensions); | |
| 239 ofs = end + 1; | |
| 240 | |
| 241 end = description.find(';', ofs); | |
| 242 // It's ok for end to run off the string here. If there's no | |
| 243 // trailing semicolon we consume the remainder of the string. | |
| 244 if (end != std::string::npos) { | |
| 245 mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs)); | |
| 246 } else { | |
| 247 mime_type.description = UTF8ToUTF16(description.substr(ofs)); | |
| 248 } | |
| 249 mime_types->push_back(mime_type); | |
| 250 if (end == std::string::npos) | |
| 251 break; | |
| 252 ofs = end + 1; | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 } // namespace NPAPI | |
| OLD | NEW |