r=bryner sr=jst git-svn-id: svn://10.0.0.236/trunk@114265 18797224-902f-48f8-a5cc-f745e15eee43
482 lines
17 KiB
C++
482 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the NPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the NPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
nsPluginsDirUNIX.cpp
|
|
|
|
UNIX implementation of the nsPluginsDir/nsPluginsFile classes.
|
|
|
|
by Alex Musil
|
|
*/
|
|
|
|
#include "xp_core.h"
|
|
#include "nsplugin.h"
|
|
#include "ns4xPlugin.h"
|
|
#include "ns4xPluginInstance.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIMemory.h"
|
|
#include "nsIPluginStreamListener.h"
|
|
#include "nsPluginsDir.h"
|
|
#include "nsObsoleteModuleLoading.h"
|
|
#include "prmem.h"
|
|
#include "prenv.h"
|
|
#include "prerror.h"
|
|
#include <sys/stat.h>
|
|
|
|
#include "nsIPref.h"
|
|
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
|
|
|
|
/* Local helper functions */
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Ouput format from NPP_GetMIMEDescription: "...mime type[;version]:[extension]:[desecription];..."
|
|
// The ambiguity of mime description could cause the browser fail to parse the MIME types
|
|
// correctly.
|
|
// E.g. "mime type::desecription;" // correct w/o ext
|
|
// "mime type:desecription;" // wrong w/o ext
|
|
//
|
|
static nsresult
|
|
ParsePluginMimeDescription(const char *mdesc, nsPluginInfo &info)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
if (!mdesc || !*mdesc)
|
|
return rv;
|
|
|
|
char *mdescDup = PL_strdup(mdesc); // make a dup of intput string we'll change it content
|
|
char anEmptyString[] = "";
|
|
nsAutoVoidArray tmpMimeTypeArr;
|
|
char delimiters[] = {':',':',';'};
|
|
int mimeTypeVariantCount = 0;
|
|
char *p = mdescDup; // make a dup of intput string we'll change it content
|
|
while(p) {
|
|
char *ptrMimeArray[] = {anEmptyString, anEmptyString, anEmptyString};
|
|
|
|
// It's easy to point out ptrMimeArray[0] to the string sounds like
|
|
// "Mime type is not specified, plugin will not function properly."
|
|
// and show this on "about:plugins" page, but we have to mark this particular
|
|
// mime type of given plugin as disable on "about:plugins" page,
|
|
// which feature is not implemented yet.
|
|
// So we'll ignore, without any warnings, an empty description strings,
|
|
// in other words, if after parsing ptrMimeArray[0] == anEmptyString is true.
|
|
// It is possible do not to registry a plugin at all if it returns
|
|
// an empty string on GetMIMEDescription() call,
|
|
// e.g. plugger returns "" if pluggerrc file is not found.
|
|
|
|
char *s = p;
|
|
int i;
|
|
for (i = 0; i < (int) sizeof(delimiters) && (p = PL_strchr(s, delimiters[i])); i++) {
|
|
ptrMimeArray[i] = s; // save start ptr
|
|
*p++ = 0; // overwrite delimiter
|
|
s = p; // move forward
|
|
}
|
|
if (i == 2)
|
|
ptrMimeArray[i] = s;
|
|
// fill out the temp array
|
|
// the order is important, it should be the same in for loop below
|
|
if (ptrMimeArray[0] != anEmptyString) {
|
|
tmpMimeTypeArr.AppendElement((void*) ptrMimeArray[0]);
|
|
tmpMimeTypeArr.AppendElement((void*) ptrMimeArray[1]);
|
|
tmpMimeTypeArr.AppendElement((void*) ptrMimeArray[2]);
|
|
mimeTypeVariantCount++;
|
|
}
|
|
}
|
|
|
|
// fill out info structure
|
|
if (mimeTypeVariantCount) {
|
|
info.fVariantCount = mimeTypeVariantCount;
|
|
// we can do these 3 mallocs at ones, later on code cleanup
|
|
info.fMimeTypeArray = (char **)PR_Malloc(mimeTypeVariantCount * sizeof(char *));
|
|
info.fMimeDescriptionArray = (char **)PR_Malloc(mimeTypeVariantCount * sizeof(char *));
|
|
info.fExtensionArray = (char **)PR_Malloc(mimeTypeVariantCount * sizeof(char *));
|
|
|
|
int j,i;
|
|
for (j = i = 0; i < mimeTypeVariantCount; i++) {
|
|
// the order is important, do not change it
|
|
// we can get rid of PL_strdup here, later on code cleanup
|
|
info.fMimeTypeArray[i] = PL_strdup((char*) tmpMimeTypeArr.ElementAt(j++));
|
|
info.fExtensionArray[i] = PL_strdup((char*) tmpMimeTypeArr.ElementAt(j++));
|
|
info.fMimeDescriptionArray[i] = PL_strdup((char*) tmpMimeTypeArr.ElementAt(j++));
|
|
}
|
|
rv = NS_OK;
|
|
}
|
|
if (mdescDup)
|
|
PR_Free(mdescDup);
|
|
return rv;
|
|
}
|
|
|
|
#define LOCAL_PLUGIN_DLL_SUFFIX ".so"
|
|
#if defined(HPUX11)
|
|
#define DEFAULT_X11_PATH "/usr/lib/X11R6/"
|
|
#undef LOCAL_PLUGIN_DLL_SUFFIX
|
|
#define LOCAL_PLUGIN_DLL_SUFFIX ".sl"
|
|
#elif defined(SOLARIS)
|
|
#define DEFAULT_X11_PATH "/usr/openwin/lib/"
|
|
#elif defined(LINUX)
|
|
#define DEFAULT_X11_PATH "/usr/X11R6/lib/"
|
|
#else
|
|
#define DEFAULT_X11_PATH ""
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
|
|
#define PLUGIN_MAX_LEN_OF_TMP_ARR 512
|
|
|
|
static void DisplayPR_LoadLibraryErrorMessage(const char *libName)
|
|
{
|
|
char errorMsg[PLUGIN_MAX_LEN_OF_TMP_ARR] = "Cannot get error from NSPR.";
|
|
if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
|
|
PR_GetErrorText(errorMsg);
|
|
|
|
fprintf(stderr, "LoadPlugin: failed to initialize shared library %s [%s]\n",
|
|
libName, errorMsg);
|
|
}
|
|
|
|
static void SearchForSoname(const char* name, char** soname)
|
|
{
|
|
if (!(name && soname))
|
|
return;
|
|
PRDir *fdDir = PR_OpenDir(DEFAULT_X11_PATH);
|
|
if (!fdDir)
|
|
return;
|
|
|
|
int n = PL_strlen(name);
|
|
PRDirEntry *dirEntry;
|
|
while ((dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH))) {
|
|
if (!PL_strncmp(dirEntry->name, name, n)) {
|
|
if (dirEntry->name[n] == '.' && dirEntry->name[n+1] && !dirEntry->name[n+2]) {
|
|
// name.N, wild guess this is what we need
|
|
char out[PLUGIN_MAX_LEN_OF_TMP_ARR] = DEFAULT_X11_PATH;
|
|
PL_strcat(out, dirEntry->name);
|
|
*soname = PL_strdup(out);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PR_CloseDir(fdDir);
|
|
}
|
|
|
|
static PRBool LoadExtraSharedLib(const char *name, char **soname, PRBool tryToGetSoname)
|
|
{
|
|
PRBool ret = PR_TRUE;
|
|
PRLibSpec tempSpec;
|
|
PRLibrary *handle;
|
|
tempSpec.type = PR_LibSpec_Pathname;
|
|
tempSpec.value.pathname = name;
|
|
handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);
|
|
if (!handle) {
|
|
ret = PR_FALSE;
|
|
DisplayPR_LoadLibraryErrorMessage(name);
|
|
if (tryToGetSoname) {
|
|
SearchForSoname(name, soname);
|
|
if (*soname) {
|
|
ret = LoadExtraSharedLib((const char *) *soname, NULL, PR_FALSE);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#define PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS 32
|
|
#define PREF_PLUGINS_SONAME "plugin.soname.list"
|
|
#define DEFAULT_EXTRA_LIBS_LIST "libXt" LOCAL_PLUGIN_DLL_SUFFIX ":libXext" LOCAL_PLUGIN_DLL_SUFFIX
|
|
/*
|
|
this function looks for
|
|
user_pref("plugin.soname.list", "/usr/X11R6/lib/libXt.so.6:libXiext.so");
|
|
in user's pref.js
|
|
and loads all libs in specified order
|
|
*/
|
|
|
|
static void LoadExtraSharedLibs()
|
|
{
|
|
// check out if user's prefs.js has libs name
|
|
nsresult res;
|
|
nsCOMPtr<nsIPref> prefs = do_GetService(kPrefServiceCID, &res);
|
|
if (NS_SUCCEEDED(res) && (prefs != nsnull)) {
|
|
char *sonamesListFromPref = PREF_PLUGINS_SONAME;
|
|
char *sonameList = NULL;
|
|
PRBool prefSonameListIsSet = PR_TRUE;
|
|
res = prefs->CopyCharPref(sonamesListFromPref, &sonameList);
|
|
if (!sonameList) {
|
|
// pref is not set, lets use hardcoded list
|
|
prefSonameListIsSet = PR_FALSE;
|
|
sonameList = PL_strdup(DEFAULT_EXTRA_LIBS_LIST);
|
|
}
|
|
if (sonameList) {
|
|
char *arrayOfLibs[PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS] = {0};
|
|
int numOfLibs = 0;
|
|
char *nextToken;
|
|
char *p = nsCRT::strtok(sonameList,":",&nextToken);
|
|
if (p) {
|
|
while (p && numOfLibs < PLUGIN_MAX_NUMBER_OF_EXTRA_LIBS) {
|
|
arrayOfLibs[numOfLibs++] = p;
|
|
p = nsCRT::strtok(nextToken,":",&nextToken);
|
|
}
|
|
} else // there is just one lib
|
|
arrayOfLibs[numOfLibs++] = sonameList;
|
|
|
|
char sonameListToSave[PLUGIN_MAX_LEN_OF_TMP_ARR] = "";
|
|
for (int i=0; i<numOfLibs; i++) {
|
|
// trim out head/tail white spaces (just in case)
|
|
PRBool head = PR_TRUE;
|
|
p = arrayOfLibs[i];
|
|
while (*p) {
|
|
if (*p == ' ' || *p == '\t') {
|
|
if (head) {
|
|
arrayOfLibs[i] = ++p;
|
|
} else {
|
|
*p = 0;
|
|
}
|
|
} else {
|
|
head = PR_FALSE;
|
|
p++;
|
|
}
|
|
}
|
|
if (!arrayOfLibs[i][0]) {
|
|
continue; // null string
|
|
}
|
|
PRBool tryToGetSoname = PR_TRUE;
|
|
if (PL_strchr(arrayOfLibs[i], '/')) {
|
|
//assuming it's real name, try to stat it
|
|
struct stat st;
|
|
if (stat((const char*) arrayOfLibs[i], &st)) {
|
|
//get just a file name
|
|
arrayOfLibs[i] = PL_strrchr(arrayOfLibs[i], '/') + 1;
|
|
} else
|
|
tryToGetSoname = PR_FALSE;
|
|
}
|
|
char *soname = NULL;
|
|
if (LoadExtraSharedLib(arrayOfLibs[i], &soname, tryToGetSoname)) {
|
|
//construct soname's list to save in prefs
|
|
p = soname ? soname : arrayOfLibs[i];
|
|
int n = PLUGIN_MAX_LEN_OF_TMP_ARR -
|
|
(PL_strlen(sonameListToSave) + PL_strlen(p));
|
|
if (n > 0) {
|
|
PL_strcat(sonameListToSave, p);
|
|
PL_strcat(sonameListToSave,":");
|
|
}
|
|
if (soname) {
|
|
PL_strfree(soname); // it's from strdup
|
|
}
|
|
if (numOfLibs > 1)
|
|
arrayOfLibs[i][PL_strlen(arrayOfLibs[i])] = ':'; //restore ":" in sonameList
|
|
}
|
|
}
|
|
for (p = &sonameListToSave[PL_strlen(sonameListToSave) - 1]; *p == ':'; p--)
|
|
*p = 0; //delete tail ":" delimiters
|
|
|
|
if (!prefSonameListIsSet || PL_strcmp(sonameList, sonameListToSave)) {
|
|
// if user specified some bogus soname I overwrite it here,
|
|
// otherwise it'll decrease performance by calling popen() in SearchForSoname
|
|
// every time for each bogus name
|
|
prefs->SetCharPref(sonamesListFromPref, (const char *)sonameListToSave);
|
|
}
|
|
PL_strfree(sonameList);
|
|
}
|
|
}
|
|
}
|
|
#endif //MOZ_WIDGET_GTK
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/* nsPluginsDir implementation */
|
|
|
|
PRBool nsPluginsDir::IsPluginFile(const nsFileSpec& fileSpec)
|
|
{
|
|
const char* pathname = fileSpec.GetCString();
|
|
PRBool ret = PR_FALSE;
|
|
if (pathname) {
|
|
int n = PL_strlen(pathname) - (sizeof(LOCAL_PLUGIN_DLL_SUFFIX) - 1);
|
|
if (n > 0 && !PL_strcmp(&pathname[n], LOCAL_PLUGIN_DLL_SUFFIX)) {
|
|
ret = PR_TRUE; // *.so or *.sl file
|
|
}
|
|
#ifdef NS_DEBUG
|
|
printf("IsPluginFile(%s) == %s\n", pathname, ret?"TRUE":"FALSE");
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/* nsPluginFile implementation */
|
|
|
|
nsPluginFile::nsPluginFile(const nsFileSpec& spec)
|
|
: nsFileSpec(spec)
|
|
{
|
|
// nada
|
|
}
|
|
|
|
nsPluginFile::~nsPluginFile()
|
|
{
|
|
// nada
|
|
}
|
|
|
|
/**
|
|
* Loads the plugin into memory using NSPR's shared-library loading
|
|
* mechanism. Handles platform differences in loading shared libraries.
|
|
*/
|
|
nsresult nsPluginFile::LoadPlugin(PRLibrary* &outLibrary)
|
|
{
|
|
PRLibSpec libSpec;
|
|
libSpec.type = PR_LibSpec_Pathname;
|
|
libSpec.value.pathname = this->GetCString();
|
|
|
|
pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Normally, Mozilla isn't linked against libXt and libXext
|
|
// since it's a Gtk/Gdk application. On the other hand,
|
|
// legacy plug-ins expect the libXt and libXext symbols
|
|
// to already exist in the global name space. This plug-in
|
|
// wrapper is linked against libXt and libXext, but since
|
|
// we never call on any of these libraries, plug-ins still
|
|
// fail to resolve Xt symbols when trying to do a dlopen
|
|
// at runtime. Explicitly opening Xt/Xext into the global
|
|
// namespace before attempting to load the plug-in seems to
|
|
// work fine.
|
|
if (!pLibrary) {
|
|
LoadExtraSharedLibs();
|
|
// try reload plugin ones more
|
|
pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
|
|
if (!pLibrary)
|
|
DisplayPR_LoadLibraryErrorMessage(libSpec.value.pathname);
|
|
}
|
|
#endif
|
|
|
|
#ifdef NS_DEBUG
|
|
printf("LoadPlugin() %s returned %lx\n",
|
|
libSpec.value.pathname, (unsigned long)pLibrary);
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* Obtains all of the information currently available for this plugin.
|
|
*/
|
|
nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info)
|
|
{
|
|
nsresult rv;
|
|
const char* mimedescr = 0, *name = 0, *description = 0;
|
|
|
|
// No, this doesn't leak. GetGlobalServiceManager() doesn't addref
|
|
// it's out pointer. Maybe it should.
|
|
nsIServiceManagerObsolete* mgr;
|
|
nsServiceManager::GetGlobalServiceManager((nsIServiceManager**)&mgr);
|
|
|
|
nsFactoryProc nsGetFactory =
|
|
(nsFactoryProc) PR_FindSymbol(pLibrary, "NSGetFactory");
|
|
|
|
nsCOMPtr<nsIPlugin> plugin;
|
|
|
|
if (nsGetFactory) {
|
|
// It's an almost-new-style plugin. The "truly new" plugins
|
|
// are just XPCOM components, but there are some Mozilla
|
|
// Classic holdovers that live in the plugins directory but
|
|
// implement nsIPlugin and the factory stuff.
|
|
static NS_DEFINE_CID(kPluginCID, NS_PLUGIN_CID);
|
|
|
|
nsCOMPtr<nsIFactory> factory;
|
|
rv = nsGetFactory(mgr, kPluginCID, nsnull, nsnull,
|
|
getter_AddRefs(factory));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
plugin = do_QueryInterface(factory);
|
|
} else {
|
|
// It's old sk00l
|
|
// if fileName parameter == 0 ns4xPlugin::CreatePlugin() will not call NP_Initialize()
|
|
rv = ns4xPlugin::CreatePlugin(mgr, 0, 0, pLibrary,
|
|
getter_AddRefs(plugin));
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
if (plugin) {
|
|
plugin->GetMIMEDescription(&mimedescr);
|
|
#ifdef NS_DEBUG
|
|
printf("GetMIMEDescription() returned \"%s\"\n", mimedescr);
|
|
#endif
|
|
if (NS_FAILED(rv = ParsePluginMimeDescription(mimedescr, info)))
|
|
return rv;
|
|
info.fFileName = PL_strdup(this->GetCString());
|
|
plugin->GetValue(nsPluginVariable_NameString, &name);
|
|
if (!name)
|
|
name = PL_strrchr(info.fFileName, '/') + 1;
|
|
info.fName = PL_strdup(name);
|
|
|
|
plugin->GetValue(nsPluginVariable_DescriptionString, &description);
|
|
if (!description)
|
|
description = "";
|
|
info.fDescription = PL_strdup(description);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
|
|
{
|
|
if(info.fName != nsnull)
|
|
PL_strfree(info.fName);
|
|
|
|
if(info.fDescription != nsnull)
|
|
PL_strfree(info.fDescription);
|
|
|
|
for(PRUint32 i = 0; i < info.fVariantCount; i++) {
|
|
if (info.fMimeTypeArray[i] != nsnull)
|
|
PL_strfree(info.fMimeTypeArray[i]);
|
|
|
|
if (info.fMimeDescriptionArray[i] != nsnull)
|
|
PL_strfree(info.fMimeDescriptionArray[i]);
|
|
|
|
if(info.fExtensionArray[i] != nsnull)
|
|
PL_strfree(info.fExtensionArray[i]);
|
|
}
|
|
|
|
PR_FREEIF(info.fMimeTypeArray);
|
|
PR_FREEIF(info.fMimeDescriptionArray);
|
|
PR_FREEIF(info.fExtensionArray);
|
|
|
|
if(info.fFileName != nsnull)
|
|
PL_strfree(info.fFileName);
|
|
|
|
return NS_OK;
|
|
}
|