michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsNPAPIPlugin.h" michael@0: #include "nsNPAPIPluginInstance.h" michael@0: #include "nsIMemory.h" michael@0: #include "nsPluginsDir.h" michael@0: #include "nsPluginsDirUtils.h" michael@0: #include "prmem.h" michael@0: #include "prenv.h" michael@0: #include "prerror.h" michael@0: #include "prio.h" michael@0: #include michael@0: #include "nsString.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefService.h" michael@0: michael@0: #define LOCAL_PLUGIN_DLL_SUFFIX ".so" michael@0: #if defined(__hpux) michael@0: #define DEFAULT_X11_PATH "/usr/lib/X11R6/" michael@0: #undef LOCAL_PLUGIN_DLL_SUFFIX michael@0: #define LOCAL_PLUGIN_DLL_SUFFIX ".sl" michael@0: #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so" michael@0: #elif defined(_AIX) michael@0: #define DEFAULT_X11_PATH "/usr/lib" michael@0: #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".a" michael@0: #elif defined(SOLARIS) michael@0: #define DEFAULT_X11_PATH "/usr/openwin/lib/" michael@0: #elif defined(LINUX) michael@0: #define DEFAULT_X11_PATH "/usr/X11R6/lib/" michael@0: #elif defined(__APPLE__) michael@0: #define DEFAULT_X11_PATH "/usr/X11R6/lib" michael@0: #undef LOCAL_PLUGIN_DLL_SUFFIX michael@0: #define LOCAL_PLUGIN_DLL_SUFFIX ".dylib" michael@0: #define LOCAL_PLUGIN_DLL_ALT_SUFFIX ".so" michael@0: #else michael@0: #define DEFAULT_X11_PATH "" michael@0: #endif michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: michael@0: #define PLUGIN_MAX_LEN_OF_TMP_ARR 512 michael@0: michael@0: static void DisplayPR_LoadLibraryErrorMessage(const char *libName) michael@0: { michael@0: char errorMsg[PLUGIN_MAX_LEN_OF_TMP_ARR] = "Cannot get error from NSPR."; michael@0: if (PR_GetErrorTextLength() < (int) sizeof(errorMsg)) michael@0: PR_GetErrorText(errorMsg); michael@0: michael@0: fprintf(stderr, "LoadPlugin: failed to initialize shared library %s [%s]\n", michael@0: libName, errorMsg); michael@0: } michael@0: michael@0: static void SearchForSoname(const char* name, char** soname) michael@0: { michael@0: if (!(name && soname)) michael@0: return; michael@0: PRDir *fdDir = PR_OpenDir(DEFAULT_X11_PATH); michael@0: if (!fdDir) michael@0: return; michael@0: michael@0: int n = strlen(name); michael@0: PRDirEntry *dirEntry; michael@0: while ((dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH))) { michael@0: if (!PL_strncmp(dirEntry->name, name, n)) { michael@0: if (dirEntry->name[n] == '.' && dirEntry->name[n+1] && !dirEntry->name[n+2]) { michael@0: // name.N, wild guess this is what we need michael@0: char out[PLUGIN_MAX_LEN_OF_TMP_ARR] = DEFAULT_X11_PATH; michael@0: PL_strcat(out, dirEntry->name); michael@0: *soname = PL_strdup(out); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: PR_CloseDir(fdDir); michael@0: } michael@0: michael@0: static bool LoadExtraSharedLib(const char *name, char **soname, bool tryToGetSoname) michael@0: { michael@0: bool ret = true; michael@0: PRLibSpec tempSpec; michael@0: PRLibrary *handle; michael@0: tempSpec.type = PR_LibSpec_Pathname; michael@0: tempSpec.value.pathname = name; michael@0: handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL); michael@0: if (!handle) { michael@0: ret = false; michael@0: DisplayPR_LoadLibraryErrorMessage(name); michael@0: if (tryToGetSoname) { michael@0: SearchForSoname(name, soname); michael@0: if (*soname) { michael@0: ret = LoadExtraSharedLib((const char *) *soname, nullptr, false); michael@0: } michael@0: } michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: #define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32 michael@0: #define PREF_PLUGINS_SONAME "plugin.soname.list" michael@0: #if defined(SOLARIS) || defined(HPUX) michael@0: #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX ":libXm" LOCAL_PLUGIN_DLL_SUFFIX michael@0: #else michael@0: #define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX michael@0: #endif michael@0: /* michael@0: this function looks for michael@0: user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXext.so"); michael@0: in user's pref.js michael@0: and loads all libs in specified order michael@0: */ michael@0: michael@0: static void LoadExtraSharedLibs() michael@0: { michael@0: // check out if user's prefs.js has libs name michael@0: nsresult res; michael@0: nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &res)); michael@0: if (NS_SUCCEEDED(res) && (prefs != nullptr)) { michael@0: char *sonameList = nullptr; michael@0: bool prefSonameListIsSet = true; michael@0: res = prefs->GetCharPref(PREF_PLUGINS_SONAME, &sonameList); michael@0: if (!sonameList) { michael@0: // pref is not set, lets use hardcoded list michael@0: prefSonameListIsSet = false; michael@0: sonameList = PL_strdup(DEFAULT_EXTRA_LIBS_LIST); michael@0: } michael@0: if (sonameList) { michael@0: char *arrayOfLibs[PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS] = {0}; michael@0: int numOfLibs = 0; michael@0: char *nextToken; michael@0: char *p = nsCRT::strtok(sonameList,":",&nextToken); michael@0: if (p) { michael@0: while (p && numOfLibs < PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS) { michael@0: arrayOfLibs[numOfLibs++] = p; michael@0: p = nsCRT::strtok(nextToken,":",&nextToken); michael@0: } michael@0: } else // there is just one lib michael@0: arrayOfLibs[numOfLibs++] = sonameList; michael@0: michael@0: char sonameListToSave[PLUGIN_MAX_LEN_OF_TMP_ARR] = ""; michael@0: for (int i=0; i 0) { michael@0: PL_strcat(sonameListToSave, p); michael@0: PL_strcat(sonameListToSave,":"); michael@0: } michael@0: if (soname) { michael@0: PL_strfree(soname); // it's from strdup michael@0: } michael@0: if (numOfLibs > 1) michael@0: arrayOfLibs[i][strlen(arrayOfLibs[i])] = ':'; //restore ":" in sonameList michael@0: } michael@0: } michael@0: michael@0: // Check whether sonameListToSave is a empty String, Bug: 329205 michael@0: if (sonameListToSave[0]) michael@0: for (p = &sonameListToSave[strlen(sonameListToSave) - 1]; *p == ':'; p--) michael@0: *p = 0; //delete tail ":" delimiters michael@0: michael@0: if (!prefSonameListIsSet || PL_strcmp(sonameList, sonameListToSave)) { michael@0: // if user specified some bogus soname I overwrite it here, michael@0: // otherwise it'll decrease performance by calling popen() in SearchForSoname michael@0: // every time for each bogus name michael@0: prefs->SetCharPref(PREF_PLUGINS_SONAME, (const char *)sonameListToSave); michael@0: } michael@0: PL_strfree(sonameList); michael@0: } michael@0: } michael@0: } michael@0: #endif //MOZ_WIDGET_GTK2 michael@0: michael@0: /* nsPluginsDir implementation */ michael@0: michael@0: bool nsPluginsDir::IsPluginFile(nsIFile* file) michael@0: { michael@0: nsAutoCString filename; michael@0: if (NS_FAILED(file->GetNativeLeafName(filename))) michael@0: return false; michael@0: michael@0: #ifdef ANDROID michael@0: // It appears that if you load michael@0: // 'libstagefright_honeycomb.so' on froyo, or michael@0: // 'libstagefright_froyo.so' on honeycomb, we will abort. michael@0: // Since these are just helper libs, we can ignore. michael@0: const char *cFile = filename.get(); michael@0: if (strstr(cFile, "libstagefright") != nullptr) michael@0: return false; michael@0: #endif michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(dllSuffix, LOCAL_PLUGIN_DLL_SUFFIX); michael@0: if (filename.Length() > dllSuffix.Length() && michael@0: StringEndsWith(filename, dllSuffix)) michael@0: return true; michael@0: michael@0: #ifdef LOCAL_PLUGIN_DLL_ALT_SUFFIX michael@0: NS_NAMED_LITERAL_CSTRING(dllAltSuffix, LOCAL_PLUGIN_DLL_ALT_SUFFIX); michael@0: if (filename.Length() > dllAltSuffix.Length() && michael@0: StringEndsWith(filename, dllAltSuffix)) michael@0: return true; michael@0: #endif michael@0: return false; michael@0: } michael@0: michael@0: /* nsPluginFile implementation */ michael@0: michael@0: nsPluginFile::nsPluginFile(nsIFile* file) michael@0: : mPlugin(file) michael@0: { michael@0: } michael@0: michael@0: nsPluginFile::~nsPluginFile() michael@0: { michael@0: } michael@0: michael@0: nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary) michael@0: { michael@0: PRLibSpec libSpec; michael@0: libSpec.type = PR_LibSpec_Pathname; michael@0: bool exists = false; michael@0: mPlugin->Exists(&exists); michael@0: if (!exists) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: michael@0: nsresult rv; michael@0: nsAutoCString path; michael@0: rv = mPlugin->GetNativePath(path); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: libSpec.value.pathname = path.get(); michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: michael@0: // Normally, Mozilla isn't linked against libXt and libXext michael@0: // since it's a Gtk/Gdk application. On the other hand, michael@0: // legacy plug-ins expect the libXt and libXext symbols michael@0: // to already exist in the global name space. This plug-in michael@0: // wrapper is linked against libXt and libXext, but since michael@0: // we never call on any of these libraries, plug-ins still michael@0: // fail to resolve Xt symbols when trying to do a dlopen michael@0: // at runtime. Explicitly opening Xt/Xext into the global michael@0: // namespace before attempting to load the plug-in seems to michael@0: // work fine. michael@0: michael@0: michael@0: #if defined(SOLARIS) || defined(HPUX) michael@0: // Acrobat/libXm: Lazy resolving might cause crash later (bug 211587) michael@0: *outLibrary = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW); michael@0: pLibrary = *outLibrary; michael@0: #else michael@0: // Some dlopen() doesn't recover from a failed PR_LD_NOW (bug 223744) michael@0: *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0); michael@0: pLibrary = *outLibrary; michael@0: #endif michael@0: if (!pLibrary) { michael@0: LoadExtraSharedLibs(); michael@0: // try reload plugin once more michael@0: *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0); michael@0: pLibrary = *outLibrary; michael@0: if (!pLibrary) { michael@0: DisplayPR_LoadLibraryErrorMessage(libSpec.value.pathname); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: #else michael@0: *outLibrary = PR_LoadLibraryWithFlags(libSpec, 0); michael@0: pLibrary = *outLibrary; michael@0: #endif // MOZ_WIDGET_GTK2 michael@0: michael@0: #ifdef DEBUG michael@0: printf("LoadPlugin() %s returned %lx\n", michael@0: libSpec.value.pathname, (unsigned long)pLibrary); michael@0: #endif michael@0: michael@0: if (!pLibrary) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary) michael@0: { michael@0: *outLibrary = nullptr; michael@0: michael@0: info.fVersion = nullptr; michael@0: michael@0: // Sadly we have to load the library for this to work. michael@0: nsresult rv = LoadPlugin(outLibrary); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: const char* (*npGetPluginVersion)() = michael@0: (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetPluginVersion"); michael@0: if (npGetPluginVersion) { michael@0: info.fVersion = PL_strdup(npGetPluginVersion()); michael@0: } michael@0: michael@0: const char* (*npGetMIMEDescription)() = michael@0: (const char* (*)()) PR_FindFunctionSymbol(pLibrary, "NP_GetMIMEDescription"); michael@0: if (!npGetMIMEDescription) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: const char* mimedescr = npGetMIMEDescription(); michael@0: if (!mimedescr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: rv = ParsePluginMimeDescription(mimedescr, info); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString path; michael@0: if (NS_FAILED(rv = mPlugin->GetNativePath(path))) michael@0: return rv; michael@0: info.fFullPath = PL_strdup(path.get()); michael@0: michael@0: nsAutoCString fileName; michael@0: if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName))) michael@0: return rv; michael@0: info.fFileName = PL_strdup(fileName.get()); michael@0: michael@0: NP_GetValueFunc npGetValue = (NP_GetValueFunc)PR_FindFunctionSymbol(pLibrary, "NP_GetValue"); michael@0: if (!npGetValue) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: const char *name = nullptr; michael@0: npGetValue(nullptr, NPPVpluginNameString, &name); michael@0: if (name) { michael@0: info.fName = PL_strdup(name); michael@0: } michael@0: else { michael@0: info.fName = PL_strdup(fileName.get()); michael@0: } michael@0: michael@0: const char *description = nullptr; michael@0: npGetValue(nullptr, NPPVpluginDescriptionString, &description); michael@0: if (description) { michael@0: info.fDescription = PL_strdup(description); michael@0: } michael@0: else { michael@0: info.fDescription = PL_strdup(""); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info) michael@0: { michael@0: if (info.fName != nullptr) michael@0: PL_strfree(info.fName); michael@0: michael@0: if (info.fDescription != nullptr) michael@0: PL_strfree(info.fDescription); michael@0: michael@0: for (uint32_t i = 0; i < info.fVariantCount; i++) { michael@0: if (info.fMimeTypeArray[i] != nullptr) michael@0: PL_strfree(info.fMimeTypeArray[i]); michael@0: michael@0: if (info.fMimeDescriptionArray[i] != nullptr) michael@0: PL_strfree(info.fMimeDescriptionArray[i]); michael@0: michael@0: if (info.fExtensionArray[i] != nullptr) michael@0: PL_strfree(info.fExtensionArray[i]); michael@0: } michael@0: michael@0: PR_FREEIF(info.fMimeTypeArray); michael@0: PR_FREEIF(info.fMimeDescriptionArray); michael@0: PR_FREEIF(info.fExtensionArray); michael@0: michael@0: if (info.fFullPath != nullptr) michael@0: PL_strfree(info.fFullPath); michael@0: michael@0: if (info.fFileName != nullptr) michael@0: PL_strfree(info.fFileName); michael@0: michael@0: if (info.fVersion != nullptr) michael@0: PL_strfree(info.fVersion); michael@0: michael@0: return NS_OK; michael@0: }