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 |