1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Alex Musil
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsNPAPIPlugin.h"
40 : #include "nsNPAPIPluginInstance.h"
41 : #include "nsIMemory.h"
42 : #include "nsIPluginStreamListener.h"
43 : #include "nsPluginsDir.h"
44 : #include "nsPluginsDirUtils.h"
45 : #include "prmem.h"
46 : #include "prenv.h"
47 : #include "prerror.h"
48 : #include <sys/stat.h>
49 : #include "nsString.h"
50 : #include "nsILocalFile.h"
51 : #include "nsIPrefBranch.h"
52 : #include "nsIPrefService.h"
53 :
54 : #define LOCAL_PLUGIN_DLL_SUFFIX ".so"
55 : #if defined(__hpux)
56 : #define DEFAULT_X11_PATH "/usr/lib/X11R6/"
57 : #undef LOCAL_PLUGIN_DLL_SUFFIX
58 : #define LOCAL_PLUGIN_DLL_SUFFIX ".sl"
59 : #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
60 : #elif defined(_AIX)
61 : #define DEFAULT_X11_PATH "/usr/lib"
62 : #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".a"
63 : #elif defined(SOLARIS)
64 : #define DEFAULT_X11_PATH "/usr/openwin/lib/"
65 : #elif defined(LINUX)
66 : #define DEFAULT_X11_PATH "/usr/X11R6/lib/"
67 : #elif defined(__APPLE__)
68 : #define DEFAULT_X11_PATH "/usr/X11R6/lib"
69 : #undef LOCAL_PLUGIN_DLL_SUFFIX
70 : #define LOCAL_PLUGIN_DLL_SUFFIX ".dylib"
71 : #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
72 : #else
73 : #define DEFAULT_X11_PATH ""
74 : #endif
75 :
76 : #if defined(MOZ_WIDGET_GTK2)
77 :
78 : #define PLUGIN_MAX_LEN_OF_TMP_ARR 512
79 :
80 0 : static void DisplayPR_LoadLibraryErrorMessage(const char *libName)
81 : {
82 0 : char errorMsg[PLUGIN_MAX_LEN_OF_TMP_ARR] = "Cannot get error from NSPR.";
83 0 : if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
84 0 : PR_GetErrorText(errorMsg);
85 :
86 : fprintf(stderr, "LoadPlugin: failed to initialize shared library %s [%s]\n",
87 0 : libName, errorMsg);
88 0 : }
89 :
90 0 : static void SearchForSoname(const char* name, char** soname)
91 : {
92 0 : if (!(name && soname))
93 0 : return;
94 0 : PRDir *fdDir = PR_OpenDir(DEFAULT_X11_PATH);
95 0 : if (!fdDir)
96 0 : return;
97 :
98 0 : int n = PL_strlen(name);
99 : PRDirEntry *dirEntry;
100 0 : while ((dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH))) {
101 0 : if (!PL_strncmp(dirEntry->name, name, n)) {
102 0 : if (dirEntry->name[n] == '.' && dirEntry->name[n+1] && !dirEntry->name[n+2]) {
103 : // name.N, wild guess this is what we need
104 0 : char out[PLUGIN_MAX_LEN_OF_TMP_ARR] = DEFAULT_X11_PATH;
105 0 : PL_strcat(out, dirEntry->name);
106 0 : *soname = PL_strdup(out);
107 0 : break;
108 : }
109 : }
110 : }
111 :
112 0 : PR_CloseDir(fdDir);
113 : }
114 :
115 0 : static bool LoadExtraSharedLib(const char *name, char **soname, bool tryToGetSoname)
116 : {
117 0 : bool ret = true;
118 : PRLibSpec tempSpec;
119 : PRLibrary *handle;
120 0 : tempSpec.type = PR_LibSpec_Pathname;
121 0 : tempSpec.value.pathname = name;
122 0 : handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);
123 0 : if (!handle) {
124 0 : ret = false;
125 0 : DisplayPR_LoadLibraryErrorMessage(name);
126 0 : if (tryToGetSoname) {
127 0 : SearchForSoname(name, soname);
128 0 : if (*soname) {
129 0 : ret = LoadExtraSharedLib((const char *) *soname, NULL, false);
130 : }
131 : }
132 : }
133 0 : return ret;
134 : }
135 :
136 : #define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32
137 : #define PREF_PLUGINS_SONAME "plugin.soname.list"
138 : #if defined(SOLARIS) || defined(HPUX)
139 : #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX ":libXm" LOCAL_PLUGIN_DLL_SUFFIX
140 : #else
141 : #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX
142 : #endif
143 : /*
144 : this function looks for
145 : user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXext.so");
146 : in user's pref.js
147 : and loads all libs in specified order
148 : */
149 :
150 0 : static void LoadExtraSharedLibs()
151 : {
152 : // check out if user's prefs.js has libs name
153 : nsresult res;
154 0 : nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &res));
155 0 : if (NS_SUCCEEDED(res) && (prefs != nsnull)) {
156 0 : char *sonameList = NULL;
157 0 : bool prefSonameListIsSet = true;
158 0 : res = prefs->GetCharPref(PREF_PLUGINS_SONAME, &sonameList);
159 0 : if (!sonameList) {
160 : // pref is not set, lets use hardcoded list
161 0 : prefSonameListIsSet = false;
162 0 : sonameList = PL_strdup(DEFAULT_EXTRA_LIBS_LIST);
163 : }
164 0 : if (sonameList) {
165 0 : char *arrayOfLibs[PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS] = {0};
166 0 : int numOfLibs = 0;
167 : char *nextToken;
168 0 : char *p = nsCRT::strtok(sonameList,":",&nextToken);
169 0 : if (p) {
170 0 : while (p && numOfLibs < PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS) {
171 0 : arrayOfLibs[numOfLibs++] = p;
172 0 : p = nsCRT::strtok(nextToken,":",&nextToken);
173 : }
174 : } else // there is just one lib
175 0 : arrayOfLibs[numOfLibs++] = sonameList;
176 :
177 0 : char sonameListToSave[PLUGIN_MAX_LEN_OF_TMP_ARR] = "";
178 0 : for (int i=0; i<numOfLibs; i++) {
179 : // trim out head/tail white spaces (just in case)
180 0 : bool head = true;
181 0 : p = arrayOfLibs[i];
182 0 : while (*p) {
183 0 : if (*p == ' ' || *p == '\t') {
184 0 : if (head) {
185 0 : arrayOfLibs[i] = ++p;
186 : } else {
187 0 : *p = 0;
188 : }
189 : } else {
190 0 : head = false;
191 0 : p++;
192 : }
193 : }
194 0 : if (!arrayOfLibs[i][0]) {
195 0 : continue; // null string
196 : }
197 0 : bool tryToGetSoname = true;
198 0 : if (PL_strchr(arrayOfLibs[i], '/')) {
199 : //assuming it's real name, try to stat it
200 : struct stat st;
201 0 : if (stat((const char*) arrayOfLibs[i], &st)) {
202 : //get just a file name
203 0 : arrayOfLibs[i] = PL_strrchr(arrayOfLibs[i], '/') + 1;
204 : } else
205 0 : tryToGetSoname = false;
206 : }
207 0 : char *soname = NULL;
208 0 : if (LoadExtraSharedLib(arrayOfLibs[i], &soname, tryToGetSoname)) {
209 : //construct soname's list to save in prefs
210 0 : p = soname ? soname : arrayOfLibs[i];
211 : int n = PLUGIN_MAX_LEN_OF_TMP_ARR -
212 0 : (PL_strlen(sonameListToSave) + PL_strlen(p));
213 0 : if (n > 0) {
214 0 : PL_strcat(sonameListToSave, p);
215 0 : PL_strcat(sonameListToSave,":");
216 : }
217 0 : if (soname) {
218 0 : PL_strfree(soname); // it's from strdup
219 : }
220 0 : if (numOfLibs > 1)
221 0 : arrayOfLibs[i][PL_strlen(arrayOfLibs[i])] = ':'; //restore ":" in sonameList
222 : }
223 : }
224 :
225 : // Check whether sonameListToSave is a empty String, Bug: 329205
226 0 : if (sonameListToSave[0])
227 0 : for (p = &sonameListToSave[PL_strlen(sonameListToSave) - 1]; *p == ':'; p--)
228 0 : *p = 0; //delete tail ":" delimiters
229 :
230 0 : if (!prefSonameListIsSet || PL_strcmp(sonameList, sonameListToSave)) {
231 : // if user specified some bogus soname I overwrite it here,
232 : // otherwise it'll decrease performance by calling popen() in SearchForSoname
233 : // every time for each bogus name
234 0 : prefs->SetCharPref(PREF_PLUGINS_SONAME, (const char *)sonameListToSave);
235 : }
236 0 : PL_strfree(sonameList);
237 : }
238 : }
239 0 : }
240 : #endif //MOZ_WIDGET_GTK2
241 :
242 : /* nsPluginsDir implementation */
243 :
244 173 : bool nsPluginsDir::IsPluginFile(nsIFile* file)
245 : {
246 346 : nsCAutoString filename;
247 173 : if (NS_FAILED(file->GetNativeLeafName(filename)))
248 0 : return false;
249 :
250 : #ifdef ANDROID
251 : // It appears that if you load
252 : // 'libstagefright_honeycomb.so' on froyo, or
253 : // 'libstagefright_froyo.so' on honeycomb, we will abort.
254 : // Since these are just helper libs, we can ignore.
255 : const char *cFile = filename.get();
256 : if (strstr(cFile, "libstagefright") != NULL)
257 : return false;
258 : #endif
259 :
260 346 : NS_NAMED_LITERAL_CSTRING(dllSuffix, LOCAL_PLUGIN_DLL_SUFFIX);
261 346 : if (filename.Length() > dllSuffix.Length() &&
262 173 : StringEndsWith(filename, dllSuffix))
263 173 : return true;
264 :
265 : #ifdef LOCAL_PLUGIN_DLL_ALT_SUFFIX
266 : NS_NAMED_LITERAL_CSTRING(dllAltSuffix, LOCAL_PLUGIN_DLL_ALT_SUFFIX);
267 : if (filename.Length() > dllAltSuffix.Length() &&
268 : StringEndsWith(filename, dllAltSuffix))
269 : return true;
270 : #endif
271 0 : return false;
272 : }
273 :
274 : /* nsPluginFile implementation */
275 :
276 173 : nsPluginFile::nsPluginFile(nsIFile* file)
277 173 : : mPlugin(file)
278 : {
279 173 : }
280 :
281 173 : nsPluginFile::~nsPluginFile()
282 : {
283 346 : }
284 :
285 173 : nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
286 : {
287 : PRLibSpec libSpec;
288 173 : libSpec.type = PR_LibSpec_Pathname;
289 173 : bool exists = false;
290 173 : mPlugin->Exists(&exists);
291 173 : if (!exists)
292 0 : return NS_ERROR_FILE_NOT_FOUND;
293 :
294 : nsresult rv;
295 346 : nsCAutoString path;
296 173 : rv = mPlugin->GetNativePath(path);
297 173 : if (NS_FAILED(rv))
298 0 : return rv;
299 :
300 173 : libSpec.value.pathname = path.get();
301 :
302 : #if defined(MOZ_WIDGET_GTK2)
303 :
304 : // Normally, Mozilla isn't linked against libXt and libXext
305 : // since it's a Gtk/Gdk application. On the other hand,
306 : // legacy plug-ins expect the libXt and libXext symbols
307 : // to already exist in the global name space. This plug-in
308 : // wrapper is linked against libXt and libXext, but since
309 : // we never call on any of these libraries, plug-ins still
310 : // fail to resolve Xt symbols when trying to do a dlopen
311 : // at runtime. Explicitly opening Xt/Xext into the global
312 : // namespace before attempting to load the plug-in seems to
313 : // work fine.
314 :
315 :
316 : #if defined(SOLARIS) || defined(HPUX)
317 : // Acrobat/libXm: Lazy resolving might cause crash later (bug 211587)
318 : *outLibrary = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW);
319 : pLibrary = *outLibrary;
320 : #else
321 : // Some dlopen() doesn't recover from a failed PR_LD_NOW (bug 223744)
322 173 : *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
323 173 : pLibrary = *outLibrary;
324 : #endif
325 173 : if (!pLibrary) {
326 0 : LoadExtraSharedLibs();
327 : // try reload plugin once more
328 0 : *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
329 0 : pLibrary = *outLibrary;
330 0 : if (!pLibrary) {
331 0 : DisplayPR_LoadLibraryErrorMessage(libSpec.value.pathname);
332 0 : return NS_ERROR_FAILURE;
333 : }
334 : }
335 : #else
336 : *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
337 : pLibrary = *outLibrary;
338 : #endif // MOZ_WIDGET_GTK2
339 :
340 : #ifdef NS_DEBUG
341 : printf("LoadPlugin() %s returned %lx\n",
342 173 : libSpec.value.pathname, (unsigned long)pLibrary);
343 : #endif
344 :
345 173 : return NS_OK;
346 : }
347 :
348 173 : nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
349 : {
350 173 : *outLibrary = nsnull;
351 :
352 173 : info.fVersion = nsnull;
353 :
354 : // Sadly we have to load the library for this to work.
355 173 : nsresult rv = LoadPlugin(outLibrary);
356 173 : if (NS_FAILED(rv))
357 0 : return rv;
358 :
359 : const char* (*npGetPluginVersion)() =
360 173 : (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetPluginVersion");
361 173 : if (npGetPluginVersion) {
362 173 : info.fVersion = PL_strdup(npGetPluginVersion());
363 : }
364 :
365 : const char* (*npGetMIMEDescription)() =
366 173 : (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetMIMEDescription");
367 173 : if (!npGetMIMEDescription) {
368 0 : return NS_ERROR_FAILURE;
369 : }
370 :
371 173 : const char* mimedescr = npGetMIMEDescription();
372 173 : if (!mimedescr) {
373 0 : return NS_ERROR_FAILURE;
374 : }
375 :
376 173 : rv = ParsePluginMimeDescription(mimedescr, info);
377 173 : if (NS_FAILED(rv)) {
378 0 : return rv;
379 : }
380 :
381 346 : nsCAutoString path;
382 173 : if (NS_FAILED(rv = mPlugin->GetNativePath(path)))
383 0 : return rv;
384 173 : info.fFullPath = PL_strdup(path.get());
385 :
386 346 : nsCAutoString fileName;
387 173 : if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName)))
388 0 : return rv;
389 173 : info.fFileName = PL_strdup(fileName.get());
390 :
391 173 : NP_GetValueFunc npGetValue = (NP_GetValueFunc)PR_FindFunctionSymbol(pLibrary, "NP_GetValue");
392 173 : if (!npGetValue) {
393 0 : return NS_ERROR_FAILURE;
394 : }
395 :
396 173 : const char *name = NULL;
397 173 : npGetValue(NULL, NPPVpluginNameString, &name);
398 173 : if (name) {
399 173 : info.fName = PL_strdup(name);
400 : }
401 : else {
402 0 : info.fName = PL_strdup(fileName.get());
403 : }
404 :
405 173 : const char *description = NULL;
406 173 : npGetValue(NULL, NPPVpluginDescriptionString, &description);
407 173 : if (description) {
408 173 : info.fDescription = PL_strdup(description);
409 : }
410 : else {
411 0 : info.fDescription = PL_strdup("");
412 : }
413 :
414 173 : return NS_OK;
415 : }
416 :
417 173 : nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
418 : {
419 173 : if (info.fName != nsnull)
420 173 : PL_strfree(info.fName);
421 :
422 173 : if (info.fDescription != nsnull)
423 173 : PL_strfree(info.fDescription);
424 :
425 346 : for (PRUint32 i = 0; i < info.fVariantCount; i++) {
426 173 : if (info.fMimeTypeArray[i] != nsnull)
427 173 : PL_strfree(info.fMimeTypeArray[i]);
428 :
429 173 : if (info.fMimeDescriptionArray[i] != nsnull)
430 173 : PL_strfree(info.fMimeDescriptionArray[i]);
431 :
432 173 : if (info.fExtensionArray[i] != nsnull)
433 173 : PL_strfree(info.fExtensionArray[i]);
434 : }
435 :
436 173 : PR_FREEIF(info.fMimeTypeArray);
437 173 : PR_FREEIF(info.fMimeDescriptionArray);
438 173 : PR_FREEIF(info.fExtensionArray);
439 :
440 173 : if (info.fFullPath != nsnull)
441 173 : PL_strfree(info.fFullPath);
442 :
443 173 : if (info.fFileName != nsnull)
444 173 : PL_strfree(info.fFileName);
445 :
446 173 : if (info.fVersion != nsnull)
447 173 : PL_strfree(info.fVersion);
448 :
449 173 : return NS_OK;
450 : }
|