| 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 | 
|---|