Back to index

lightning-sunbird  0.9+nobinonly
nsPluginsDirUnix.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039        nsPluginsDirUNIX.cpp
00040        
00041        UNIX implementation of the nsPluginsDir/nsPluginsFile classes.
00042        
00043        by Alex Musil
00044  */
00045 
00046 #include "nsplugin.h"
00047 #include "ns4xPlugin.h"
00048 #include "ns4xPluginInstance.h"
00049 #include "nsIServiceManager.h"
00050 #include "nsIMemory.h"
00051 #include "nsIPluginStreamListener.h"
00052 #include "nsPluginsDir.h"
00053 #include "nsPluginsDirUtils.h"
00054 #include "nsObsoleteModuleLoading.h"
00055 #include "prmem.h"
00056 #include "prenv.h"
00057 #include "prerror.h"
00058 #include <sys/stat.h>
00059 #include "nsString.h"
00060 #include "nsILocalFile.h"
00061 #include "nsIPrefBranch.h"
00062 #include "nsIPrefService.h"
00063 #ifdef AIX
00064 #include "nsPluginLogging.h"
00065 #include "prprf.h"
00066 #define LOG(args)  PLUGIN_LOG(PLUGIN_LOG_NORMAL, args)
00067 #endif
00068 
00069 #define LOCAL_PLUGIN_DLL_SUFFIX ".so"
00070 #if defined(__hpux)
00071 #define DEFAULT_X11_PATH "/usr/lib/X11R6/"
00072 #undef LOCAL_PLUGIN_DLL_SUFFIX
00073 #define LOCAL_PLUGIN_DLL_SUFFIX ".sl"
00074 #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so"
00075 #elif defined(_AIX)
00076 #define DEFAULT_X11_PATH "/usr/lib"
00077 #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".a"
00078 #elif defined(SOLARIS)
00079 #define DEFAULT_X11_PATH "/usr/openwin/lib/"
00080 #elif defined(LINUX)
00081 #define DEFAULT_X11_PATH "/usr/X11R6/lib/"
00082 #else
00083 #define DEFAULT_X11_PATH ""
00084 #endif
00085 
00086 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
00087 
00088 #define PLUGIN_MAX_LEN_OF_TMP_ARR 512
00089 
00090 static void DisplayPR_LoadLibraryErrorMessage(const char *libName)
00091 {
00092     char errorMsg[PLUGIN_MAX_LEN_OF_TMP_ARR] = "Cannot get error from NSPR.";
00093     if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
00094         PR_GetErrorText(errorMsg);
00095 
00096     fprintf(stderr, "LoadPlugin: failed to initialize shared library %s [%s]\n",
00097         libName, errorMsg);
00098 }
00099 
00100 static void SearchForSoname(const char* name, char** soname)
00101 {
00102     if (!(name && soname))
00103         return;
00104     PRDir *fdDir = PR_OpenDir(DEFAULT_X11_PATH);
00105     if (!fdDir)
00106         return;       
00107 
00108     int n = PL_strlen(name);
00109     PRDirEntry *dirEntry;
00110     while ((dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH))) {
00111         if (!PL_strncmp(dirEntry->name, name, n)) {
00112             if (dirEntry->name[n] == '.' && dirEntry->name[n+1] && !dirEntry->name[n+2]) {
00113                 // name.N, wild guess this is what we need
00114                 char out[PLUGIN_MAX_LEN_OF_TMP_ARR] = DEFAULT_X11_PATH;
00115                 PL_strcat(out, dirEntry->name);
00116                 *soname = PL_strdup(out);
00117                break;
00118             }
00119         }
00120     }
00121 
00122     PR_CloseDir(fdDir);
00123 }
00124 
00125 static PRBool LoadExtraSharedLib(const char *name, char **soname, PRBool tryToGetSoname)
00126 {
00127     PRBool ret = PR_TRUE;
00128     PRLibSpec tempSpec;
00129     PRLibrary *handle;
00130     tempSpec.type = PR_LibSpec_Pathname;
00131     tempSpec.value.pathname = name;
00132     handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);
00133     if (!handle) {
00134         ret = PR_FALSE;
00135         DisplayPR_LoadLibraryErrorMessage(name);
00136         if (tryToGetSoname) {
00137             SearchForSoname(name, soname);
00138             if (*soname) {
00139                 ret = LoadExtraSharedLib((const char *) *soname, NULL, PR_FALSE);
00140             }
00141         }
00142     }
00143     return ret;
00144 }
00145 
00146 #define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32
00147 #define PREF_PLUGINS_SONAME "plugin.soname.list"
00148 #if defined(SOLARIS) || defined(HPUX)
00149 #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX ":libXm" LOCAL_PLUGIN_DLL_SUFFIX
00150 #else
00151 #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX
00152 #endif
00153 /*
00154  this function looks for
00155  user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXext.so");
00156  in user's pref.js
00157  and loads all libs in specified order
00158 */
00159 
00160 static void LoadExtraSharedLibs()
00161 {
00162     // check out if user's prefs.js has libs name
00163     nsresult res;
00164     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &res));
00165     if (NS_SUCCEEDED(res) && (prefs != nsnull)) {
00166         char *sonamesListFromPref = PREF_PLUGINS_SONAME;
00167         char *sonameList = NULL;
00168         PRBool prefSonameListIsSet = PR_TRUE;
00169         res = prefs->GetCharPref(sonamesListFromPref, &sonameList);
00170         if (!sonameList) {
00171             // pref is not set, lets use hardcoded list
00172             prefSonameListIsSet = PR_FALSE;
00173             sonameList = PL_strdup(DEFAULT_EXTRA_LIBS_LIST);
00174         }
00175         if (sonameList) {
00176             char *arrayOfLibs[PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS] = {0};
00177             int numOfLibs = 0;
00178             char *nextToken;
00179             char *p = nsCRT::strtok(sonameList,":",&nextToken);
00180             if (p) {
00181                 while (p && numOfLibs < PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS) {
00182                     arrayOfLibs[numOfLibs++] = p;
00183                     p = nsCRT::strtok(nextToken,":",&nextToken);
00184                 }
00185             } else // there is just one lib
00186                 arrayOfLibs[numOfLibs++] = sonameList;
00187 
00188             char sonameListToSave[PLUGIN_MAX_LEN_OF_TMP_ARR] = "";
00189             for (int i=0; i<numOfLibs; i++) {
00190                 // trim out head/tail white spaces (just in case)
00191                 PRBool head = PR_TRUE;
00192                 p = arrayOfLibs[i];
00193                 while (*p) {
00194                     if (*p == ' ' || *p == '\t') {
00195                         if (head) {
00196                             arrayOfLibs[i] = ++p;
00197                         } else {
00198                             *p = 0;
00199                         }
00200                     } else {
00201                         head = PR_FALSE;
00202                         p++;
00203                     }
00204                 }
00205                 if (!arrayOfLibs[i][0]) {
00206                     continue; // null string
00207                 }
00208                 PRBool tryToGetSoname = PR_TRUE;
00209                 if (PL_strchr(arrayOfLibs[i], '/')) {
00210                     //assuming it's real name, try to stat it
00211                     struct stat st;
00212                     if (stat((const char*) arrayOfLibs[i], &st)) {
00213                         //get just a file name
00214                         arrayOfLibs[i] = PL_strrchr(arrayOfLibs[i], '/') + 1;
00215                     } else
00216                         tryToGetSoname = PR_FALSE;
00217                 }
00218                 char *soname = NULL;
00219                 if (LoadExtraSharedLib(arrayOfLibs[i], &soname, tryToGetSoname)) {
00220                     //construct soname's list to save in prefs
00221                     p = soname ? soname : arrayOfLibs[i];
00222                     int n = PLUGIN_MAX_LEN_OF_TMP_ARR -
00223                         (PL_strlen(sonameListToSave) + PL_strlen(p));
00224                     if (n > 0) {
00225                         PL_strcat(sonameListToSave, p);
00226                         PL_strcat(sonameListToSave,":");
00227                     }
00228                     if (soname) {
00229                         PL_strfree(soname); // it's from strdup
00230                     }
00231                     if (numOfLibs > 1)
00232                         arrayOfLibs[i][PL_strlen(arrayOfLibs[i])] = ':'; //restore ":" in sonameList
00233                 }
00234             }
00235 
00236             // Check whether sonameListToSave is a empty String, Bug: 329205
00237             if (sonameListToSave[0]) 
00238                 for (p = &sonameListToSave[PL_strlen(sonameListToSave) - 1]; *p == ':'; p--)
00239                     *p = 0; //delete tail ":" delimiters
00240 
00241             if (!prefSonameListIsSet || PL_strcmp(sonameList, sonameListToSave)) {
00242                 // if user specified some bogus soname I overwrite it here,
00243                 // otherwise it'll decrease performance by calling popen() in SearchForSoname
00244                 // every time for each bogus name
00245                 prefs->SetCharPref(sonamesListFromPref, (const char *)sonameListToSave);
00246             }
00247             PL_strfree(sonameList);
00248         }
00249     }
00250 }
00251 #endif //MOZ_WIDGET_GTK || MOZ_WIDGET_GTK2
00252 
00253 
00254 
00256 
00257 /* nsPluginsDir implementation */
00258 
00259 PRBool nsPluginsDir::IsPluginFile(nsIFile* file)
00260 {
00261     nsCAutoString filename;
00262     if (NS_FAILED(file->GetNativeLeafName(filename)))
00263         return PR_FALSE;
00264 
00265     NS_NAMED_LITERAL_CSTRING(dllSuffix, LOCAL_PLUGIN_DLL_SUFFIX);
00266     if (filename.Length() > dllSuffix.Length() &&
00267         StringEndsWith(filename, dllSuffix))
00268         return PR_TRUE;
00269     
00270 #ifdef LOCAL_PLUGIN_DLL_ALT_SUFFIX
00271     NS_NAMED_LITERAL_CSTRING(dllAltSuffix, LOCAL_PLUGIN_DLL_ALT_SUFFIX);
00272     if (filename.Length() > dllAltSuffix.Length() &&
00273         StringEndsWith(filename, dllAltSuffix))
00274         return PR_TRUE;
00275 #endif
00276     return PR_FALSE;
00277 }
00278 
00280 
00281 /* nsPluginFile implementation */
00282 
00283 nsPluginFile::nsPluginFile(nsIFile* file)
00284 : mPlugin(file)
00285 {
00286     // nada
00287 }
00288 
00289 nsPluginFile::~nsPluginFile()
00290 {
00291     // nada
00292 }
00293 
00294 #ifdef AIX
00295 
00303 static char *javaLibPath = NULL;
00304 
00305 static void SetJavaLibPath(const nsCString& pluginPath)
00306 {
00307     // If javaLibPath is non-NULL, that means we have already set the LIBPATH
00308     // variable once this session, so we don't need to do it again.
00309     if (javaLibPath)
00310         return;
00311 
00312     nsCAutoString javaDir, newLibPath;
00313 
00314     PRInt32 pos = pluginPath.RFindChar('/');
00315     if (pos == kNotFound || pos == 0)
00316         return;
00317 
00318     javaDir = Substring(pluginPath, 0, pos);
00319     LOG(("AIX: Java dir is %s\n", javaDir.get()));
00320 
00321     // Add jre/bin to new LIBPATH
00322     newLibPath += javaDir;
00323 
00324     // Check for existance of jre/bin/classic dir, and append it to
00325     // LIBPATH if found
00326     PRFileInfo info;
00327     javaDir.AppendLiteral("/classic");
00328     if (PR_GetFileInfo(javaDir.get(), &info) == PR_SUCCESS &&
00329         info.type == PR_FILE_DIRECTORY)
00330     {
00331         newLibPath.Append(':');
00332         newLibPath.Append(javaDir);
00333     }
00334 
00335     // Get the current LIBPATH, and append it to the new LIBPATH
00336     const char *currentLibPath = PR_GetEnv("LIBPATH");
00337     LOG(("AIX: current LIBPATH=%s\n", currentLibPath));
00338     if (currentLibPath && *currentLibPath) {
00339         newLibPath.Append(':');
00340         newLibPath.Append(currentLibPath);
00341     }
00342 
00343     // Set the LIBPATH to include the path to the JRE directories.
00344     // NOTE: We are leaking javaLibPath here, as it needs to remain in memory
00345     // for PR_SetEnv to work properly.
00346     javaLibPath = PR_smprintf("LIBPATH=%s", newLibPath.get());
00347     if (javaLibPath) {
00348         LOG(("AIX: new LIBPATH=%s\n", newLibPath.get()));
00349         PR_SetEnv(javaLibPath);
00350     }
00351 }
00352 #endif
00353 
00358 nsresult nsPluginFile::LoadPlugin(PRLibrary* &outLibrary)
00359 {
00360     PRLibSpec libSpec;
00361     libSpec.type = PR_LibSpec_Pathname;
00362     PRBool exists = PR_FALSE;
00363     mPlugin->Exists(&exists);
00364     if (!exists)
00365         return NS_ERROR_FILE_NOT_FOUND;
00366 
00367     nsresult rv;
00368     nsCAutoString path;
00369     rv = mPlugin->GetNativePath(path);
00370     if (NS_FAILED(rv))
00371         return rv;
00372 
00373 #ifdef AIX
00374     nsCAutoString leafName;
00375     rv = mPlugin->GetNativeLeafName(leafName);
00376     if (NS_FAILED(rv))
00377         return rv;
00378 
00379     if (StringBeginsWith(leafName, NS_LITERAL_CSTRING("libjavaplugin_oji")))
00380         SetJavaLibPath(path);
00381 #endif
00382 
00383     libSpec.value.pathname = path.get();
00384 
00385 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
00386 
00388     // Normally, Mozilla isn't linked against libXt and libXext
00389     // since it's a Gtk/Gdk application.  On the other hand,
00390     // legacy plug-ins expect the libXt and libXext symbols
00391     // to already exist in the global name space.  This plug-in
00392     // wrapper is linked against libXt and libXext, but since
00393     // we never call on any of these libraries, plug-ins still
00394     // fail to resolve Xt symbols when trying to do a dlopen
00395     // at runtime.  Explicitly opening Xt/Xext into the global
00396     // namespace before attempting to load the plug-in seems to
00397     // work fine.
00398 
00399 
00400 #if defined(SOLARIS) || defined(HPUX)
00401     // Acrobat/libXm: Lazy resolving might cause crash later (bug 211587)
00402     pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW);
00403 #else
00404     // Some dlopen() doesn't recover from a failed PR_LD_NOW (bug 223744)
00405     pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
00406 #endif
00407     if (!pLibrary) {
00408         LoadExtraSharedLibs();
00409         // try reload plugin once more
00410         pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
00411         if (!pLibrary)
00412             DisplayPR_LoadLibraryErrorMessage(libSpec.value.pathname);
00413     }
00414 #else
00415     pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
00416 #endif  // MOZ_WIDGET_GTK || MOZ_WIDGET_GTK2
00417 
00418 #ifdef NS_DEBUG
00419     printf("LoadPlugin() %s returned %lx\n", 
00420            libSpec.value.pathname, (unsigned long)pLibrary);
00421 #endif
00422     
00423     return NS_OK;
00424 }
00425 
00429 nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info)
00430 {
00431     nsresult rv;
00432     const char* mimedescr = 0, *name = 0, *description = 0;
00433 
00434     // No, this doesn't leak. GetGlobalServiceManager() doesn't addref
00435     // it's out pointer. Maybe it should.
00436     nsIServiceManagerObsolete* mgr;
00437     nsServiceManager::GetGlobalServiceManager((nsIServiceManager**)&mgr);
00438 
00439     nsFactoryProc nsGetFactory =
00440         (nsFactoryProc) PR_FindSymbol(pLibrary, "NSGetFactory");
00441 
00442     nsCOMPtr<nsIPlugin> plugin;
00443 
00444     if (nsGetFactory) {
00445         // It's an almost-new-style plugin. The "truly new" plugins
00446         // are just XPCOM components, but there are some Mozilla
00447         // Classic holdovers that live in the plugins directory but
00448         // implement nsIPlugin and the factory stuff.
00449         static NS_DEFINE_CID(kPluginCID, NS_PLUGIN_CID);
00450 
00451         nsCOMPtr<nsIFactory> factory;
00452         rv = nsGetFactory(mgr, kPluginCID, nsnull, nsnull, 
00453                        getter_AddRefs(factory));
00454         if (NS_FAILED(rv)) return rv;
00455 
00456         plugin = do_QueryInterface(factory);
00457     } else {
00458         // It's old sk00l
00459         // if fileName parameter == 0 ns4xPlugin::CreatePlugin() will not call NP_Initialize()
00460         rv = ns4xPlugin::CreatePlugin(mgr, 0, 0, pLibrary, 
00461                                   getter_AddRefs(plugin));
00462         if (NS_FAILED(rv)) return rv;
00463     }
00464 
00465     if (plugin) {
00466         plugin->GetMIMEDescription(&mimedescr);
00467 #ifdef NS_DEBUG
00468         printf("GetMIMEDescription() returned \"%s\"\n", mimedescr);
00469 #endif
00470        if (NS_FAILED(rv = ParsePluginMimeDescription(mimedescr, info)))
00471             return rv;
00472         nsCAutoString filename;
00473         if (NS_FAILED(rv = mPlugin->GetNativePath(filename)))
00474             return rv;
00475         info.fFileName = PL_strdup(filename.get());
00476         plugin->GetValue(nsPluginVariable_NameString, &name);
00477         if (!name)
00478             name = PL_strrchr(info.fFileName, '/') + 1;
00479         info.fName = PL_strdup(name);
00480 
00481         plugin->GetValue(nsPluginVariable_DescriptionString, &description);
00482         if (!description)
00483             description = "";
00484         info.fDescription = PL_strdup(description);
00485     }
00486     return NS_OK;
00487 }
00488 
00489 
00490 nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
00491 {
00492     if (info.fName != nsnull)
00493         PL_strfree(info.fName);
00494 
00495     if (info.fDescription != nsnull)
00496         PL_strfree(info.fDescription);
00497 
00498     for (PRUint32 i = 0; i < info.fVariantCount; i++) {
00499         if (info.fMimeTypeArray[i] != nsnull)
00500             PL_strfree(info.fMimeTypeArray[i]);
00501 
00502         if (info.fMimeDescriptionArray[i] != nsnull)
00503             PL_strfree(info.fMimeDescriptionArray[i]);
00504 
00505         if (info.fExtensionArray[i] != nsnull)
00506             PL_strfree(info.fExtensionArray[i]);
00507     }
00508 
00509     PR_FREEIF(info.fMimeTypeArray);
00510     PR_FREEIF(info.fMimeDescriptionArray);
00511     PR_FREEIF(info.fExtensionArray);
00512 
00513     if (info.fFileName != nsnull)
00514         PL_strfree(info.fFileName);
00515 
00516     return NS_OK;
00517 }