Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / modules / plugin / base / src / nsPluginsDirUnix.cpp
blob20fd6b9ffb09ccd9cf7f7e491ee57b9221889c2f
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
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/
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.
15 * The Original Code is mozilla.org code.
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.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 nsPluginsDirUNIX.cpp
41 UNIX implementation of the nsPluginsDir/nsPluginsFile classes.
43 by Alex Musil
46 #include "nsplugin.h"
47 #include "nsNPAPIPlugin.h"
48 #include "nsNPAPIPluginInstance.h"
49 #include "nsIServiceManager.h"
50 #include "nsIMemory.h"
51 #include "nsIPluginStreamListener.h"
52 #include "nsPluginsDir.h"
53 #include "nsPluginsDirUtils.h"
54 #include "nsObsoleteModuleLoading.h"
55 #include "prmem.h"
56 #include "prenv.h"
57 #include "prerror.h"
58 #include <sys/stat.h>
59 #include "nsString.h"
60 #include "nsILocalFile.h"
61 #include "nsIPrefBranch.h"
62 #include "nsIPrefService.h"
63 #ifdef AIX
64 #include "nsPluginLogging.h"
65 #include "prprf.h"
66 #define LOG(args) PLUGIN_LOG(PLUGIN_LOG_NORMAL, args)
67 #endif
69 #define LOCAL_PLUGIN_DLL_SUFFIX ".so"
70 #if defined(__hpux)
71 #define DEFAULT_X11_PATH "/usr/lib/X11R6/"
72 #undef LOCAL_PLUGIN_DLL_SUFFIX
73 #define LOCAL_PLUGIN_DLL_SUFFIX ".sl"
74 #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
75 #elif defined(_AIX)
76 #define DEFAULT_X11_PATH "/usr/lib"
77 #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".a"
78 #elif defined(SOLARIS)
79 #define DEFAULT_X11_PATH "/usr/openwin/lib/"
80 #elif defined(LINUX)
81 #define DEFAULT_X11_PATH "/usr/X11R6/lib/"
82 #else
83 #define DEFAULT_X11_PATH ""
84 #endif
86 #if defined(MOZ_WIDGET_GTK2)
88 #define PLUGIN_MAX_LEN_OF_TMP_ARR 512
90 static void DisplayPR_LoadLibraryErrorMessage(const char *libName)
92 char errorMsg[PLUGIN_MAX_LEN_OF_TMP_ARR] = "Cannot get error from NSPR.";
93 if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
94 PR_GetErrorText(errorMsg);
96 fprintf(stderr, "LoadPlugin: failed to initialize shared library %s [%s]\n",
97 libName, errorMsg);
100 static void SearchForSoname(const char* name, char** soname)
102 if (!(name && soname))
103 return;
104 PRDir *fdDir = PR_OpenDir(DEFAULT_X11_PATH);
105 if (!fdDir)
106 return;
108 int n = PL_strlen(name);
109 PRDirEntry *dirEntry;
110 while ((dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH))) {
111 if (!PL_strncmp(dirEntry->name, name, n)) {
112 if (dirEntry->name[n] == '.' && dirEntry->name[n+1] && !dirEntry->name[n+2]) {
113 // name.N, wild guess this is what we need
114 char out[PLUGIN_MAX_LEN_OF_TMP_ARR] = DEFAULT_X11_PATH;
115 PL_strcat(out, dirEntry->name);
116 *soname = PL_strdup(out);
117 break;
122 PR_CloseDir(fdDir);
125 static PRBool LoadExtraSharedLib(const char *name, char **soname, PRBool tryToGetSoname)
127 PRBool ret = PR_TRUE;
128 PRLibSpec tempSpec;
129 PRLibrary *handle;
130 tempSpec.type = PR_LibSpec_Pathname;
131 tempSpec.value.pathname = name;
132 handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);
133 if (!handle) {
134 ret = PR_FALSE;
135 DisplayPR_LoadLibraryErrorMessage(name);
136 if (tryToGetSoname) {
137 SearchForSoname(name, soname);
138 if (*soname) {
139 ret = LoadExtraSharedLib((const char *) *soname, NULL, PR_FALSE);
143 return ret;
146 #define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32
147 #define PREF_PLUGINS_SONAME "plugin.soname.list"
148 #if defined(SOLARIS) || defined(HPUX)
149 #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX ":libXm" LOCAL_PLUGIN_DLL_SUFFIX
150 #else
151 #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX
152 #endif
154 this function looks for
155 user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXext.so");
156 in user's pref.js
157 and loads all libs in specified order
160 static void LoadExtraSharedLibs()
162 // check out if user's prefs.js has libs name
163 nsresult res;
164 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &res));
165 if (NS_SUCCEEDED(res) && (prefs != nsnull)) {
166 char *sonamesListFromPref = PREF_PLUGINS_SONAME;
167 char *sonameList = NULL;
168 PRBool prefSonameListIsSet = PR_TRUE;
169 res = prefs->GetCharPref(sonamesListFromPref, &sonameList);
170 if (!sonameList) {
171 // pref is not set, lets use hardcoded list
172 prefSonameListIsSet = PR_FALSE;
173 sonameList = PL_strdup(DEFAULT_EXTRA_LIBS_LIST);
175 if (sonameList) {
176 char *arrayOfLibs[PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS] = {0};
177 int numOfLibs = 0;
178 char *nextToken;
179 char *p = nsCRT::strtok(sonameList,":",&nextToken);
180 if (p) {
181 while (p && numOfLibs < PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS) {
182 arrayOfLibs[numOfLibs++] = p;
183 p = nsCRT::strtok(nextToken,":",&nextToken);
185 } else // there is just one lib
186 arrayOfLibs[numOfLibs++] = sonameList;
188 char sonameListToSave[PLUGIN_MAX_LEN_OF_TMP_ARR] = "";
189 for (int i=0; i<numOfLibs; i++) {
190 // trim out head/tail white spaces (just in case)
191 PRBool head = PR_TRUE;
192 p = arrayOfLibs[i];
193 while (*p) {
194 if (*p == ' ' || *p == '\t') {
195 if (head) {
196 arrayOfLibs[i] = ++p;
197 } else {
198 *p = 0;
200 } else {
201 head = PR_FALSE;
202 p++;
205 if (!arrayOfLibs[i][0]) {
206 continue; // null string
208 PRBool tryToGetSoname = PR_TRUE;
209 if (PL_strchr(arrayOfLibs[i], '/')) {
210 //assuming it's real name, try to stat it
211 struct stat st;
212 if (stat((const char*) arrayOfLibs[i], &st)) {
213 //get just a file name
214 arrayOfLibs[i] = PL_strrchr(arrayOfLibs[i], '/') + 1;
215 } else
216 tryToGetSoname = PR_FALSE;
218 char *soname = NULL;
219 if (LoadExtraSharedLib(arrayOfLibs[i], &soname, tryToGetSoname)) {
220 //construct soname's list to save in prefs
221 p = soname ? soname : arrayOfLibs[i];
222 int n = PLUGIN_MAX_LEN_OF_TMP_ARR -
223 (PL_strlen(sonameListToSave) + PL_strlen(p));
224 if (n > 0) {
225 PL_strcat(sonameListToSave, p);
226 PL_strcat(sonameListToSave,":");
228 if (soname) {
229 PL_strfree(soname); // it's from strdup
231 if (numOfLibs > 1)
232 arrayOfLibs[i][PL_strlen(arrayOfLibs[i])] = ':'; //restore ":" in sonameList
236 // Check whether sonameListToSave is a empty String, Bug: 329205
237 if (sonameListToSave[0])
238 for (p = &sonameListToSave[PL_strlen(sonameListToSave) - 1]; *p == ':'; p--)
239 *p = 0; //delete tail ":" delimiters
241 if (!prefSonameListIsSet || PL_strcmp(sonameList, sonameListToSave)) {
242 // if user specified some bogus soname I overwrite it here,
243 // otherwise it'll decrease performance by calling popen() in SearchForSoname
244 // every time for each bogus name
245 prefs->SetCharPref(sonamesListFromPref, (const char *)sonameListToSave);
247 PL_strfree(sonameList);
251 #endif //MOZ_WIDGET_GTK2
255 ///////////////////////////////////////////////////////////////////////////
257 /* nsPluginsDir implementation */
259 PRBool nsPluginsDir::IsPluginFile(nsIFile* file)
261 nsCAutoString filename;
262 if (NS_FAILED(file->GetNativeLeafName(filename)))
263 return PR_FALSE;
265 NS_NAMED_LITERAL_CSTRING(dllSuffix, LOCAL_PLUGIN_DLL_SUFFIX);
266 if (filename.Length() > dllSuffix.Length() &&
267 StringEndsWith(filename, dllSuffix))
268 return PR_TRUE;
270 #ifdef LOCAL_PLUGIN_DLL_ALT_SUFFIX
271 NS_NAMED_LITERAL_CSTRING(dllAltSuffix, LOCAL_PLUGIN_DLL_ALT_SUFFIX);
272 if (filename.Length() > dllAltSuffix.Length() &&
273 StringEndsWith(filename, dllAltSuffix))
274 return PR_TRUE;
275 #endif
276 return PR_FALSE;
279 ///////////////////////////////////////////////////////////////////////////
281 /* nsPluginFile implementation */
283 nsPluginFile::nsPluginFile(nsIFile* file)
284 : mPlugin(file)
286 // nada
289 nsPluginFile::~nsPluginFile()
291 // nada
294 #ifdef AIX
296 * This code is necessary for Java 1.4.2 SR2 and later on AIX, as the
297 * loadquery() function is no longer used to determine the path to the
298 * installed JVM, so we need to manually set the LIBPATH variable to
299 * include the proper directories needed to run the Java plug-in.
301 * See Bug 297807 for more information.
303 static char *javaLibPath = NULL;
305 static void SetJavaLibPath(const nsCString& pluginPath)
307 // If javaLibPath is non-NULL, that means we have already set the LIBPATH
308 // variable once this session, so we don't need to do it again.
309 if (javaLibPath)
310 return;
312 nsCAutoString javaDir, newLibPath;
314 PRInt32 pos = pluginPath.RFindChar('/');
315 if (pos == kNotFound || pos == 0)
316 return;
318 javaDir = Substring(pluginPath, 0, pos);
319 LOG(("AIX: Java dir is %s\n", javaDir.get()));
321 // Add jre/bin to new LIBPATH
322 newLibPath += javaDir;
324 // Check for existance of jre/bin/classic dir, and append it to
325 // LIBPATH if found
326 PRFileInfo info;
327 javaDir.AppendLiteral("/classic");
328 if (PR_GetFileInfo(javaDir.get(), &info) == PR_SUCCESS &&
329 info.type == PR_FILE_DIRECTORY)
331 newLibPath.Append(':');
332 newLibPath.Append(javaDir);
335 // Get the current LIBPATH, and append it to the new LIBPATH
336 const char *currentLibPath = PR_GetEnv("LIBPATH");
337 LOG(("AIX: current LIBPATH=%s\n", currentLibPath));
338 if (currentLibPath && *currentLibPath) {
339 newLibPath.Append(':');
340 newLibPath.Append(currentLibPath);
343 // Set the LIBPATH to include the path to the JRE directories.
344 // NOTE: We are leaking javaLibPath here, as it needs to remain in memory
345 // for PR_SetEnv to work properly.
346 javaLibPath = PR_smprintf("LIBPATH=%s", newLibPath.get());
347 if (javaLibPath) {
348 LOG(("AIX: new LIBPATH=%s\n", newLibPath.get()));
349 PR_SetEnv(javaLibPath);
352 #endif
355 * Loads the plugin into memory using NSPR's shared-library loading
356 * mechanism. Handles platform differences in loading shared libraries.
358 nsresult nsPluginFile::LoadPlugin(PRLibrary* &outLibrary)
360 PRLibSpec libSpec;
361 libSpec.type = PR_LibSpec_Pathname;
362 PRBool exists = PR_FALSE;
363 mPlugin->Exists(&exists);
364 if (!exists)
365 return NS_ERROR_FILE_NOT_FOUND;
367 nsresult rv;
368 nsCAutoString path;
369 rv = mPlugin->GetNativePath(path);
370 if (NS_FAILED(rv))
371 return rv;
373 #ifdef AIX
374 nsCAutoString leafName;
375 rv = mPlugin->GetNativeLeafName(leafName);
376 if (NS_FAILED(rv))
377 return rv;
379 if (StringBeginsWith(leafName, NS_LITERAL_CSTRING("libjavaplugin_oji")))
380 SetJavaLibPath(path);
381 #endif
383 libSpec.value.pathname = path.get();
385 #if defined(MOZ_WIDGET_GTK2)
387 ///////////////////////////////////////////////////////////
388 // Normally, Mozilla isn't linked against libXt and libXext
389 // since it's a Gtk/Gdk application. On the other hand,
390 // legacy plug-ins expect the libXt and libXext symbols
391 // to already exist in the global name space. This plug-in
392 // wrapper is linked against libXt and libXext, but since
393 // we never call on any of these libraries, plug-ins still
394 // fail to resolve Xt symbols when trying to do a dlopen
395 // at runtime. Explicitly opening Xt/Xext into the global
396 // namespace before attempting to load the plug-in seems to
397 // work fine.
400 #if defined(SOLARIS) || defined(HPUX)
401 // Acrobat/libXm: Lazy resolving might cause crash later (bug 211587)
402 pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW);
403 #else
404 // Some dlopen() doesn't recover from a failed PR_LD_NOW (bug 223744)
405 pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
406 #endif
407 if (!pLibrary) {
408 LoadExtraSharedLibs();
409 // try reload plugin once more
410 pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
411 if (!pLibrary)
412 DisplayPR_LoadLibraryErrorMessage(libSpec.value.pathname);
414 #else
415 pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
416 #endif // MOZ_WIDGET_GTK2
418 #ifdef NS_DEBUG
419 printf("LoadPlugin() %s returned %lx\n",
420 libSpec.value.pathname, (unsigned long)pLibrary);
421 #endif
423 return NS_OK;
427 * Obtains all of the information currently available for this plugin.
429 nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info)
431 nsresult rv;
432 const char* mimedescr = 0, *name = 0, *description = 0;
434 // No, this doesn't leak. GetGlobalServiceManager() doesn't addref
435 // it's out pointer. Maybe it should.
436 nsIServiceManagerObsolete* mgr;
437 nsServiceManager::GetGlobalServiceManager((nsIServiceManager**)&mgr);
439 nsFactoryProc nsGetFactory =
440 (nsFactoryProc) PR_FindFunctionSymbol(pLibrary, "NSGetFactory");
442 nsCOMPtr<nsIPlugin> plugin;
444 info.fVersion = nsnull;
445 if (nsGetFactory) {
446 // It's an almost-new-style plugin. The "truly new" plugins
447 // are just XPCOM components, but there are some Mozilla
448 // Classic holdovers that live in the plugins directory but
449 // implement nsIPlugin and the factory stuff.
450 static NS_DEFINE_CID(kPluginCID, NS_PLUGIN_CID);
452 nsCOMPtr<nsIFactory> factory;
453 rv = nsGetFactory(mgr, kPluginCID, nsnull, nsnull,
454 getter_AddRefs(factory));
456 if (NS_FAILED(rv)) {
457 // HACK: The symbol lookup for "NSGetFactory" mistakenly returns
458 // a reference to an unrelated function when we have an NPAPI
459 // plugin linked to libxul.so. Give this plugin another shot as
460 // an NPAPI plugin
461 rv = nsNPAPIPlugin::CreatePlugin(0, 0, pLibrary, getter_AddRefs(plugin));
462 if (NS_FAILED(rv))
463 return rv;
464 } else {
465 plugin = do_QueryInterface(factory);
467 } else {
468 // It's old sk00l
469 // if fileName parameter == 0 nsNPAPIPlugin::CreatePlugin() will not call NP_Initialize()
470 rv = nsNPAPIPlugin::CreatePlugin(0, 0, pLibrary, getter_AddRefs(plugin));
471 if (NS_FAILED(rv)) return rv;
474 if (plugin) {
475 const char* (*npGetPluginVersion)() =
476 (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetPluginVersion");
477 if (npGetPluginVersion)
478 info.fVersion = PL_strdup(npGetPluginVersion());
480 plugin->GetMIMEDescription(&mimedescr);
481 #ifdef NS_DEBUG
482 printf("GetMIMEDescription() returned \"%s\"\n", mimedescr);
483 #endif
484 if (NS_FAILED(rv = ParsePluginMimeDescription(mimedescr, info)))
485 return rv;
486 nsCAutoString filename;
487 if (NS_FAILED(rv = mPlugin->GetNativePath(filename)))
488 return rv;
489 info.fFileName = PL_strdup(filename.get());
490 plugin->GetValue(nsPluginVariable_NameString, &name);
491 if (!name)
492 name = PL_strrchr(info.fFileName, '/') + 1;
493 info.fName = PL_strdup(name);
495 plugin->GetValue(nsPluginVariable_DescriptionString, &description);
496 if (!description)
497 description = "";
498 info.fDescription = PL_strdup(description);
500 return NS_OK;
504 nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
506 if (info.fName != nsnull)
507 PL_strfree(info.fName);
509 if (info.fDescription != nsnull)
510 PL_strfree(info.fDescription);
512 for (PRUint32 i = 0; i < info.fVariantCount; i++) {
513 if (info.fMimeTypeArray[i] != nsnull)
514 PL_strfree(info.fMimeTypeArray[i]);
516 if (info.fMimeDescriptionArray[i] != nsnull)
517 PL_strfree(info.fMimeDescriptionArray[i]);
519 if (info.fExtensionArray[i] != nsnull)
520 PL_strfree(info.fExtensionArray[i]);
523 PR_FREEIF(info.fMimeTypeArray);
524 PR_FREEIF(info.fMimeDescriptionArray);
525 PR_FREEIF(info.fExtensionArray);
527 if (info.fFileName != nsnull)
528 PL_strfree(info.fFileName);
530 if (info.fVersion != nsnull)
531 PL_strfree(info.fVersion);
533 return NS_OK;