Milán Major

netscape-revival

1 branch
Code

lib/plugin/npwplat.cpp

// npwplat.cpp

#include "xp_mem.h"
#include "stdafx.h"
#include <afxole.h>
#include <afxpriv.h>
#include <afxwin.h>
#include <errno.h>
#include <direct.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef WIN32
    #include <io.h>
    #include <winver.h>
#else
    #include <dos.h>
    #include <ver.h>
#endif
#include "npwplat.h"
#include "nppg.h"
#include "plgindll.h"
#include "np.h"
#include "helper.h"

NPPMgtBlk* g_pRegisteredPluginList = NULL;

// Tests that the directory specified by "dir" exists.
// Returns true if it does.
static XP_Bool wfe_CheckDir(const char * dir)
{
    int ret;
    XP_StatStruct statinfo; 

    ret = _stat((char *) dir, &statinfo);
    if(ret == -1)
        return(FALSE);
    return(TRUE);
}

// Get the name of the directory where plugins live.  "csDirname" is where the
// name of the dir is written to, and the trailing slash is removed.
// It always returns NPERR_NO_ERROR, since the Nav had to be started to get
// here, and the plugin dir is under where Nav started from.
NPError wfe_GetPluginsDirectory(CString& csDirname)
{
    char ca_default[_MAX_PATH];
    ::GetModuleFileName(AfxGetApp()->m_hInstance, ca_default, _MAX_PATH);
    char *cp_lastslash = ::strrchr(ca_default, '\\');
    *cp_lastslash = NULL;
    csDirname = ca_default;
    csDirname += "\\plugins";
    return NPERR_NO_ERROR;
}

// Fetches the "MIME type" string from the DLL VERSIONINFO structure.
// "pVersionInfo" is ptr to the VERSIONINFO data, and "pNPMgtBlk" is a handle
// to a plugin management data structure created during fe_RegisterPlugin().
// The string is copied from the VERSIONINFO data into the
// "pNPMgtBlk->szMIMEType" member.
// An error is returned if out of memory.  The MIME type string IS
// required, so an error is returned if the string does not exist.
NPError wfe_GetPluginMIMEType(VS_FIXEDFILEINFO* pVersionInfo,
                              NPPMgtBlk* pNPMgtBlk)
{
    // get the mime type of this plugin
	static char szMimeTypeBlock[] = "StringFileInfo\\040904E4\\MIMEType";
    void* lpBuffer = NULL;
    UINT  uValueSize = 0;
    if(::VerQueryValue(pVersionInfo, szMimeTypeBlock,
                     &lpBuffer, &uValueSize) == 0) // couldn't find MIME type
        return NPERR_GENERIC_ERROR;

    pNPMgtBlk->szMIMEType = new char[uValueSize];
    if(pNPMgtBlk->szMIMEType == NULL)
        return NPERR_OUT_OF_MEMORY_ERROR;
     
    strcpy(pNPMgtBlk->szMIMEType, (const char*)lpBuffer);

    return NPERR_NO_ERROR;
}

// Fetches the "File Extents" string from the DLL VERSIONINFO structure.
// "pVersionInfo" is ptr to the VERSIONINFO data, and "pNPMgtBlk" is a handle
// to a plugin management data structure created during fe_RegisterPlugin().
// The string is copied from the VERSIONINFO data into the
// "pNPMgtBlk->szFileExtents" member.
// An error is returned if out of memory, but the extents string is not
// required, so it is not an error if the string does not exist.
NPError wfe_GetPluginExtents(VS_FIXEDFILEINFO* pVersionInfo,
                             NPPMgtBlk* pNPMgtBlk)
{   // get the file extent list of this plugin
    pNPMgtBlk->szFileExtents = NULL;
    
	static char szFileExtentsBlock[] = "StringFileInfo\\040904E4\\FileExtents";
    void* lpBuffer = NULL;
    UINT  uValueSize = 0;

    if(::VerQueryValue(pVersionInfo, szFileExtentsBlock,
                     &lpBuffer, &uValueSize) == 0)
        // couldn't find file extent
        return NPERR_NO_ERROR;
    
    if((*((char*)lpBuffer) == '\0') || (uValueSize == 0))   // no extents
        return NPERR_NO_ERROR;

    // alloc char array
    pNPMgtBlk->szFileExtents = new char[uValueSize];
    if(pNPMgtBlk->szFileExtents == NULL)
        return NPERR_OUT_OF_MEMORY_ERROR;
    strcpy(pNPMgtBlk->szFileExtents, (const char*)lpBuffer);
        
    return NPERR_NO_ERROR;
}

// Fetches the "File Open Template" string from the DLL VERSIONINFO structure.
// "pVersionInfo" is ptr to the VERSIONINFO data, and "pNPMgtBlk" is a handle
// to a plugin management data structure created during fe_RegisterPlugin().
// The string is copied from the VERSIONINFO data into the
// "pNPMgtBlk->szFileOpenName" member.
// An error is returned if out of memory, but the template string is not
// required, so it is not an error if the string does not exist.
NPError wfe_GetPluginFileOpenTemplate(VS_FIXEDFILEINFO* pVersionInfo,
                                      NPPMgtBlk* pNPMgtBlk)
{   // get the file open template this plugin
    pNPMgtBlk->szFileOpenName = NULL;
    
	static char szFileOpenBlock[] = "StringFileInfo\\040904E4\\FileOpenName";
    void* lpBuffer = NULL;
    UINT  uValueSize = 0;

    if(::VerQueryValue(pVersionInfo, szFileOpenBlock,
                     &lpBuffer, &uValueSize) == 0)
        // couldn't find file extent
        return NPERR_NO_ERROR;
    
    if((*((char*)lpBuffer) == '\0') || (uValueSize == 0))   // no extents
        return NPERR_NO_ERROR;

    // alloc char array
    pNPMgtBlk->szFileOpenName = new char[uValueSize];
    if(pNPMgtBlk->szFileOpenName == NULL)
        return NPERR_OUT_OF_MEMORY_ERROR;
    strcpy(pNPMgtBlk->szFileOpenName, (const char*)lpBuffer);

    return NPERR_NO_ERROR;
}

// Fetches the plugin name and description from the resource info.
// Uses predefined version information strings
NPError wfe_GetPluginNameAndDescription(VS_FIXEDFILEINFO* pVersionInfo,
										CString&		  strName,
										CString&		  strDescription)
{
	static char szNameBlock[] = "StringFileInfo\\040904E4\\ProductName";
	static char szDescBlock[] = "StringFileInfo\\040904E4\\FileDescription";
    void* lpBuffer = NULL;
    UINT  uValueSize = 0;

	// Get the product name
    if(::VerQueryValue(pVersionInfo, szNameBlock, &lpBuffer, &uValueSize) > 0)
		strName = (LPSTR)lpBuffer;

	// Get the file description
    if(::VerQueryValue(pVersionInfo, szDescBlock, &lpBuffer, &uValueSize) > 0)
		strDescription = (LPSTR)lpBuffer;

    return NPERR_NO_ERROR;
}

// Loads plugin properties from the DLL VERSIONINFO data structure.
// MIME type and version are required, but file extents and string
// file open templates are optional.  "pPluginFilespec" is a string
// containing the file name of a DLL.  "pNPMgtBlk" is a handle to a
// plugin management data structure created during fe_RegisterPlugin().  
// Returns an error if "pPluginFilespec" did not have the required MIME
// type and version fields.
//
// Also returns the plugin name and description
NPError wfe_GetPluginProperties(char* 		pPluginFilespec,
								NPPMgtBlk* 	pNPMgtBlk,
								CString&	strPluginName,
								CString&	strPluginDescription)
{
    // prepare to read the version info tags
    DWORD dwHandle = NULL;
    DWORD dwVerInfoSize = ::GetFileVersionInfoSize(pPluginFilespec, &dwHandle);
    if(dwVerInfoSize == NULL)
        return NPERR_GENERIC_ERROR;

    VS_FIXEDFILEINFO* pVersionInfo =
        (VS_FIXEDFILEINFO*)new char[dwVerInfoSize];
    if(pVersionInfo == NULL)
        return NPERR_OUT_OF_MEMORY_ERROR;

    if(::GetFileVersionInfo(pPluginFilespec, dwHandle,
                          dwVerInfoSize, pVersionInfo) == 0)
    {
        delete [] (char*)pVersionInfo;
        return NPERR_GENERIC_ERROR;
    }

    NPError result;
    // get the mime type of this plugin
    if((result = wfe_GetPluginMIMEType(pVersionInfo, pNPMgtBlk))
        != NPERR_NO_ERROR)
    {
        delete [] (char*)pVersionInfo;
        return result;
    }

    // get the file extent list of this plugin
    if((result = wfe_GetPluginExtents(pVersionInfo, pNPMgtBlk))
        != NPERR_NO_ERROR)
    
    {
        delete [] pNPMgtBlk->szMIMEType;
        delete [] (char*)pVersionInfo;
        return result;
    }

    // get the file type template string of this plugin
    if((result = wfe_GetPluginFileOpenTemplate(pVersionInfo, pNPMgtBlk))
        != NPERR_NO_ERROR)
    
    {
        delete [] pNPMgtBlk->szFileExtents;
        delete [] pNPMgtBlk->szMIMEType;
        delete [] (char*)pVersionInfo;
        return result;
    }

    // get the version of this plugin
    void* lpBuffer = NULL;
    UINT  uValueSize = 0;
    if(::VerQueryValue(pVersionInfo, "\\", &lpBuffer, &uValueSize) == 0)
    {   // couldn't find versioninfo
        delete [] pNPMgtBlk->szFileExtents;
        delete [] pNPMgtBlk->szMIMEType;
        delete [] (char*)pVersionInfo;
        return NPERR_GENERIC_ERROR;
    }
    pNPMgtBlk->versionMS  = ((VS_FIXEDFILEINFO*)lpBuffer)->dwProductVersionMS;
    pNPMgtBlk->versionLS  = ((VS_FIXEDFILEINFO*)lpBuffer)->dwProductVersionLS;

	// get the plugin name and description. It's not fatal if these fail
	wfe_GetPluginNameAndDescription(pVersionInfo, strPluginName, strPluginDescription);

    delete [] (char*)pVersionInfo;

    return NPERR_NO_ERROR;
}

// Given a file open filter, e.g. Text Documents(*.txt), extracts the
// description and returns a copy of it
static LPSTR
ExtractDescription(LPCSTR lpszFileOpenFilter)
{
	ASSERT(lpszFileOpenFilter);

	LPSTR lpszDescription = XP_STRDUP(lpszFileOpenFilter);

	// Strip off an filter pattern at the end
	LPSTR lpszPattern = strrchr(lpszDescription, '(');

	if (lpszPattern)
		*lpszPattern = '\0';

	return lpszDescription;
}

// Registers one plugin.  "pPluginFilespec" is a string containing the file
// name of a DLL.
// This function can be called more than once so a plugin which has copied
// into the plugins subdir AFTER the Nav has started can be registered
// and loaded.
// The DLL is a plugin candidate, and is qualified during
// this function, by checking the DLL VERSIONINFO data structure for a
// MIME type string.  If this string is found successfully, the handle
// of the management block is used to register the plugin for its MIME type.
// The extents which are to be associated with this MIME type are also grokked
// from the VERSIONINFO data structure, once the candidate is accepted.
// This function also takes the opportunity to overrided any helper app, by
// setting a helper app data structure member to allow the plugin to handle
// the MIME type instead of the helper, (pApp->how_handle = HANDLE_VIA_PLUGIN).
// Returns an error if "pPluginFilespec" was not registered.
NPError fe_RegisterPlugin(char* pPluginFilespec)
{
	CString	strName, strDescription;

    // initialize a new plugin list mgt block
    NPPMgtBlk* pNPMgtBlk = new NPPMgtBlk;
    if(pNPMgtBlk == NULL)   // fatal, can't continue
        return NPERR_OUT_OF_MEMORY_ERROR;
        
    pNPMgtBlk->pPluginFuncs = NULL;
    pNPMgtBlk->pLibrary		= NULL;
    pNPMgtBlk->uRefCount    = 0;
    pNPMgtBlk->next         = NULL;
    
    // determine the MIME type and version of this plugin
    if(wfe_GetPluginProperties(pPluginFilespec, pNPMgtBlk, strName, strDescription) != NPERR_NO_ERROR)
    {
        delete pNPMgtBlk;
        return NPERR_GENERIC_ERROR;
    }

    // if a plugin is already registered for this MIME type, return.  this
    // allows downloading and registering new plugins without exiting the nav
    for(NPPMgtBlk* pListBlk = g_pRegisteredPluginList; pListBlk != NULL;
        pListBlk = pListBlk->next) {
            BOOL bSameFile =
                (strcmp(pListBlk->pPluginFilename, pPluginFilespec) == 0);
            BOOL bSameMIMEtype =
                (strstr(pNPMgtBlk->szMIMEType, pListBlk->szMIMEType) != NULL);
            if(bSameFile && bSameMIMEtype) {
                // the plugin DLL's filename and the MIME type it's registering for
                // are the same, don't reregister
                delete pNPMgtBlk;
                return NPERR_GENERIC_ERROR;
            }
    }    

    pNPMgtBlk->pPluginFilename = XP_STRDUP(pPluginFilespec);
    if(pNPMgtBlk->pPluginFilename == NULL) // fatal, can't continue
    {
        delete pNPMgtBlk;
        return NPERR_OUT_OF_MEMORY_ERROR;
    }

	// Register the plugin file with the XP plugin code
	NPL_RegisterPluginFile(strName, pNPMgtBlk->pPluginFilename, strDescription, pNPMgtBlk);

    // Iterate through the filename extensions.  These are a list which are
    // delimited via the '|' character.  The list of MIME Types,File Extents, 
    // and Open Names must all coordinate
    char *pStartMIME, *pEndMIME, *pStartExt, *pEndExt, *pStartName, *pEndName;

    pStartMIME = pNPMgtBlk->szMIMEType;
    pStartExt = pNPMgtBlk->szFileExtents;
    pStartName = pNPMgtBlk->szFileOpenName;

    pEndMIME = strchr(pStartMIME ,'|');
    while (pEndMIME) {
        pEndExt = strchr(pStartExt,'|');
        pEndName = strchr(pStartName,'|');
        if (pEndMIME) *pEndMIME = 0;
           else return NPERR_GENERIC_ERROR;
        if (pEndExt)  *pEndExt = 0;
           else return NPERR_GENERIC_ERROR;
        if (pEndName) *pEndName = 0;
           else return NPERR_GENERIC_ERROR;

		// Register the MIME type with the XP plugin code. We need to pass in
		// a description. If there's a file open template specified, then use the
		// description from there. Otherwise use the MIME type
		LPSTR	lpszDescription = NULL;

		if (pStartName)
			lpszDescription = ExtractDescription(pStartName);

		NPL_RegisterPluginType(pStartMIME, (LPCSTR)pStartExt, lpszDescription ?
			lpszDescription : pStartMIME, (void *)pStartName, pNPMgtBlk, TRUE);

		if (lpszDescription)
			XP_FREE(lpszDescription);

        CHelperApp *pApp;
        if(theApp.m_HelperListByType.Lookup(pStartMIME, (CObject *&)pApp))  {
            //  We've a match.
            //  Make sure the app is marked to handle the load
            //      via a plugin
            pApp->how_handle = HANDLE_VIA_PLUGIN;
        }

        if ((pEndMIME+1) && (pEndExt+1) && (pEndName+1)) {
            pStartMIME = pEndMIME+1;
            pStartExt = pEndExt+1;
            pStartName = pEndName+1;
        }
        pEndMIME = strchr(pStartMIME ,'|');    
    }

	// Register the MIME type with the XP plugin code. We need to pass in
	// a description. If there's a file open template specified, then use the
	// description from there. Otherwise use the MIME type
	LPSTR	lpszDescription = NULL;

	if (pStartName)
		lpszDescription = ExtractDescription(pStartName);

	NPL_RegisterPluginType(pStartMIME, (LPCSTR)pStartExt, lpszDescription ?
		lpszDescription : pStartMIME, (void *)pStartName, pNPMgtBlk, TRUE);

	if (lpszDescription)
		XP_FREE(lpszDescription);

    CHelperApp *pApp;
    if(theApp.m_HelperListByType.Lookup(pStartMIME, (CObject *&)pApp))  {
        //  We've a match.
        //  Make sure the app is marked to handle the load
        //      via a plugin
        pApp->how_handle = HANDLE_VIA_PLUGIN;
    }

    // insert the plugin mgt blk at the head of the list of registered plugins.
    // this means they are listed in the reverse order from which they were
    // created, which doesn't matter.
    pNPMgtBlk->next = g_pRegisteredPluginList;
    g_pRegisteredPluginList = pNPMgtBlk;

    return NPERR_NO_ERROR;
}

// FE_RegisterPlugins is called from navigator main via npglue's NP_Init(). Finds all
// plugins and begins tracking them using a NPPMgtBlk.  Uses the NPPMgtBlk
// block handle to register the plugin with the xp plugin glue. Looks
// in the directory under the netscape.exe dir, named "plugins" and all
// subdirectories in recursive way (see fe_RegisterPlugins).  If the
// directory doesn't exist, no warning dialog is shown.
// There are no input or return vals.
void fe_RegisterPlugins(char* pszPluginDir)
{
    CString csPluginSpec;
    csPluginSpec = pszPluginDir; 

#ifdef JAVA
    // add directory to the java path no matter what
    LJ_AddToClassPath(pszPluginDir);
#endif

    csPluginSpec += "\\*.*"; 

#ifndef _WIN32
    _find_t fileinfo;
    unsigned result = _dos_findfirst((LPSTR)((LPCSTR)csPluginSpec), _A_NORMAL | _A_SUBDIR, &fileinfo );

    if (result == 0) {

        result = _dos_findnext(&fileinfo); // skip "."
        result = _dos_findnext(&fileinfo); // skip ".."

        CString csFileSpec;

        while(result == 0)
        {
            csFileSpec = pszPluginDir;
            csFileSpec += "\\";
            csFileSpec += fileinfo.name;

            // we got a subdir, call recursively this function to load plugin
            if (fileinfo.attrib & _A_SUBDIR ) {
                fe_RegisterPlugins((LPSTR)(LPCSTR)csFileSpec);
            }
            else {
                size_t len = strlen(fileinfo.name);

                // it's a file, see if it can be a plugin file
                if ( len > 6 && // at least "np.dll"
                        (fileinfo.name[0] == 'n' || fileinfo.name[0] == 'N') && 
                        (fileinfo.name[1] == 'p' || fileinfo.name[1] == 'P') &&
                        !_stricmp(fileinfo.name + len - 4, ".dll"))
                    fe_RegisterPlugin((LPSTR)((LPCSTR)csFileSpec));
            }

            result = _dos_findnext(&fileinfo);

        }
    }
#else   /* _WIN32 */
    _finddata_t fileinfo;
    unsigned handle = _findfirst((LPSTR)((LPCSTR)csPluginSpec), &fileinfo );
    unsigned result = 0;

    if (handle != -1) {
        result = _findnext(handle, &fileinfo); // skip "."
        result = _findnext(handle, &fileinfo); // skip ".."
    

        CString csFileSpec;

        while((result != -1) && (handle != -1))
        {
            csFileSpec = pszPluginDir;
            csFileSpec += "\\";
            csFileSpec += fileinfo.name;

            // we got a subdir, call recursively this function to load plugin
            if (fileinfo.attrib & _A_SUBDIR ) {
                fe_RegisterPlugins((LPSTR)(LPCSTR)csFileSpec);
            }
            else {
                size_t len = strlen(fileinfo.name);

                // it's a file, see if it can be a plugin file
                if ( len > 6 && // at least "np.dll"
                        (fileinfo.name[0] == 'n' || fileinfo.name[0] == 'N') && 
                        (fileinfo.name[1] == 'p' || fileinfo.name[1] == 'P') &&
                        !_stricmp(fileinfo.name + len - 4, ".dll"))
                    fe_RegisterPlugin((LPSTR)((LPCSTR)csFileSpec));
            }
            result = _findnext(handle, &fileinfo);
        }
        _findclose(handle);
    }
#endif  /* _WIN32 */
}

// called from the navigator main, dispatch to the previous function
void FE_RegisterPlugins()
{
    CString csPluginDir;
    // get the main plugins directory and start the process
    wfe_GetPluginsDirectory(csPluginDir);
    fe_RegisterPlugins((LPSTR)(LPCSTR)csPluginDir);
}
    
// saves the current path in the variable passed.  ppPathSave is the
// address of the ptr which stores the path string.  returns out of memory
// error if allocation for the string returned by _getcwd() fails.  returns
// generic error if the "one deep" stack is already full.
NPError wfe_PushPath(LPSTR *ppPathSave)
{
    // only allow "one deep" stack
    if(*ppPathSave != NULL)
        return NPERR_GENERIC_ERROR;

    // save the CWD
    *ppPathSave = _getcwd(NULL, 0);
    if(*ppPathSave == NULL)
        return NPERR_OUT_OF_MEMORY_ERROR;

    return NPERR_NO_ERROR;
}

// calculate the drive letter value for _chdrive().  ascii in, int out 
int wfe_MapAsciiToDriveNum(char cAsciiNum)
{
    int iDriveLetter = cAsciiNum;
    if(islower(cAsciiNum))
        iDriveLetter = _toupper(cAsciiNum);
    // plus one because for _chdrive(),  A = 1, B = 2, etc
    return iDriveLetter - 'A' + 1;
}

// restores the path from the variable passed.  pDirSave stores the pointer
// to the path string.  returns an error if a path has never been pushed.
NPError wfe_PopPath(LPSTR pPathSave)
{
    // only allows "one deep" stack
    if(pPathSave == NULL)
        return NPERR_GENERIC_ERROR;

    // restore the drive
    int chdriveErr = _chdrive(wfe_MapAsciiToDriveNum(pPathSave[0]));

    // restore the CWD
    int chdirErr = _chdir(pPathSave);
    free(pPathSave);

    return NPERR_NO_ERROR;
}

// Load a plugin dll.  "pluginType" is a handle to a plugin management data
// structure created during FE_RegisterPlugins().  "pNavigatorFuncs" is
// a table of entry points into Navigator, which are the services provided
// by Navigator to plugins, such as requestread() and GetUrl().  These entry
// points are stored and called by the plugin when services are needed.
// The return val is a table of functions which are entry points to the
// newly loaded plugin, such as NewStream() and Write().
NPPluginFuncs* FE_LoadPlugin(void* pluginType, NPNetscapeFuncs* pNavigatorFuncs)
{
    if(pluginType == NULL)
        return NULL;

    NPPMgtBlk* pNPMgtBlock = (NPPMgtBlk*)pluginType;

    CString csPluginDir;
    wfe_GetPluginsDirectory(csPluginDir);

    LPSTR pPathSave = NULL;   // storage for one dir spec

    wfe_PushPath(&pPathSave);  // save the current drive and dir

    // change the default dir so that implib'd plugins won't fail
    if(_chdir(csPluginDir) != 0) {
        wfe_PopPath(pPathSave);  // restore the path
        return NULL;
    }

    // must change the drive as well as the dir path
    if(isalpha(csPluginDir[0]) && csPluginDir[1] == ':') {
        if(_chdrive(wfe_MapAsciiToDriveNum(csPluginDir[0])) != 0) {
            wfe_PopPath(pPathSave);  // restore the path
            return NULL;
        }
    }

	pNPMgtBlock->pLibrary = PR_LoadLibrary(pNPMgtBlock->pPluginFilename);
    //pNPMgtBlock->hDLLInstance = ::LoadLibrary(pNPMgtBlock->pPluginFilename);

#if 0
#ifdef _WIN32
    if(pNPMgtBlock->hDLLInstance == NULL) {
        DWORD err = ::GetLastError();
#else
    if(pNPMgtBlock->hDLLInstance < HINSTANCE_ERROR) {
#endif
#endif
	// the cross platform code should take care of the 16/32 bit issues
    if(pNPMgtBlock->pLibrary == NULL)
        return NULL;
    
    NP_GETENTRYPOINTS getentrypoints =
		(NP_GETENTRYPOINTS)PR_FindSymbol("NP_GetEntryPoints", pNPMgtBlock->pLibrary);
        //(NP_GETENTRYPOINTS)::GetProcAddress(pNPMgtBlock->hDLLInstance,
        //                                   "NP_GetEntryPoints");
    if(getentrypoints == NULL)
    {
		PR_UnloadLibrary(pNPMgtBlock->pLibrary);
        //::FreeLibrary(pNPMgtBlock->hDLLInstance);
        wfe_PopPath(pPathSave);  // restore the path
        return NULL;
    }
    
    if(pNPMgtBlock->pPluginFuncs == NULL)
    {
        pNPMgtBlock->pPluginFuncs = new NPPluginFuncs;
        pNPMgtBlock->pPluginFuncs->size = sizeof NPPluginFuncs;
	pNPMgtBlock->pPluginFuncs->javaClass = NULL;
        if(pNPMgtBlock->pPluginFuncs == NULL)   // fatal, can't continue
        {
			PR_UnloadLibrary(pNPMgtBlock->pLibrary);
            //::FreeLibrary(pNPMgtBlock->hDLLInstance);
            wfe_PopPath(pPathSave);  // restore the path
            return NULL;
        }
    }

    if(getentrypoints(pNPMgtBlock->pPluginFuncs) != NPERR_NO_ERROR)
    {
		PR_UnloadLibrary(pNPMgtBlock->pLibrary);
        //::FreeLibrary(pNPMgtBlock->hDLLInstance);
        delete pNPMgtBlock->pPluginFuncs;
        pNPMgtBlock->pPluginFuncs = NULL;
        wfe_PopPath(pPathSave);  // restore the path
        return NULL;
    }

    // if the plugin's major ver level is lower than the Navigator's,
    // then they are incompatible, and should return an error
    if(HIBYTE(pNPMgtBlock->pPluginFuncs->version) < NP_VERSION_MAJOR)
    {
		PR_UnloadLibrary(pNPMgtBlock->pLibrary);
        //::FreeLibrary(pNPMgtBlock->hDLLInstance);
        delete pNPMgtBlock->pPluginFuncs;
        pNPMgtBlock->pPluginFuncs = NULL;
        wfe_PopPath(pPathSave);  // restore the path
        return NULL;
    }

    // if this DLL is not already loaded, call its initialization entry point
    if(pNPMgtBlock->uRefCount == 0) {
        // the NP_Initialize entry point was misnamed as NP_PluginInit, early
        // in plugin project development.  Its correct name is documented
        // now, and new developers expect it to work.  However, I don't want
        // to  break the plugins already in the field, so I'll accept either
        // name
        NP_PLUGININIT npinit =
			(NP_PLUGININIT)PR_FindSymbol("NP_Initialize", pNPMgtBlock->pLibrary);
            //(NP_PLUGININIT)::GetProcAddress(pNPMgtBlock->hDLLInstance,
            //                                "NP_Initialize");
        if(!npinit) {
            npinit =
				(NP_PLUGININIT)PR_FindSymbol("NP_PluginInit", pNPMgtBlock->pLibrary);
                //(NP_PLUGININIT)::GetProcAddress(pNPMgtBlock->hDLLInstance,
                //                                "NP_PluginInit");
        }

        if(npinit == NULL) {
			PR_UnloadLibrary(pNPMgtBlock->pLibrary);
            //::FreeLibrary(pNPMgtBlock->hDLLInstance);
            delete pNPMgtBlock->pPluginFuncs;
            pNPMgtBlock->pPluginFuncs = NULL;
            wfe_PopPath(pPathSave);  // restore the path
            return NULL;
        }

        if(npinit(pNavigatorFuncs) != NPERR_NO_ERROR) {
			PR_UnloadLibrary(pNPMgtBlock->pLibrary);
            //::FreeLibrary(pNPMgtBlock->hDLLInstance);
            delete pNPMgtBlock->pPluginFuncs;
            pNPMgtBlock->pPluginFuncs = NULL;
            wfe_PopPath(pPathSave);  // restore the path
            return NULL;
        }
    }
    
    pNPMgtBlock->uRefCount++;   // all is well, remember it was loaded

    // can't pop the path until lame plugins have been completely loaded/init'd
    wfe_PopPath(pPathSave);  // restore the path
    return pNPMgtBlock->pPluginFuncs;
}

// Unloads a plugin DLL.  "pluginType" is a handle to a plugin management data
// structure created during FE_RegisterPlugins().  There is no return val.
void FE_UnloadPlugin(void* pluginType)
{
    NPPMgtBlk* pNPMgtBlk = (NPPMgtBlk*)pluginType;
    if(pNPMgtBlk == NULL)
        return;

    pNPMgtBlk->uRefCount--;   // another one bites the dust

    // if this DLL is the last one, call its shutdown entry point
    if(pNPMgtBlk->uRefCount == 0) {
        // the NP_Shutdown entry point was misnamed as NP_PluginShutdown, early
        // in plugin project development.  Its correct name is documented
        // now, and new developers expect it to work.  However, I don't want
        // to  break the plugins already in the field, so I'll accept either
        // name
        NP_PLUGINSHUTDOWN npshutdown =
			(NP_PLUGINSHUTDOWN)PR_FindSymbol("NP_Shutdown", pNPMgtBlk->pLibrary);
            //(NP_PLUGINSHUTDOWN)::GetProcAddress(pNPMgtBlk->hDLLInstance,
            //                                    "NP_Shutdown");
        if(!npshutdown) {
            npshutdown =
				(NP_PLUGINSHUTDOWN)PR_FindSymbol("NP_PluginShutdown", pNPMgtBlk->pLibrary);
                //(NP_PLUGINSHUTDOWN)::GetProcAddress(pNPMgtBlk->hDLLInstance,
                //                                    "NP_PluginShutdown");
        }

        if(npshutdown != NULL) {
            NPError result = npshutdown();
        }
    }

	PR_UnloadLibrary(pNPMgtBlk->pLibrary);
    pNPMgtBlk->pLibrary = NULL;
    //::FreeLibrary(pNPMgtBlk->hDLLInstance);
    //pNPMgtBlk->hDLLInstance = NULL;
    
    delete pNPMgtBlk->pPluginFuncs;
    pNPMgtBlk->pPluginFuncs = NULL;
}

// Removes a plugin DLL from memory, and frees its NPPMgtBlk management block.
// "pluginType" is a handle to the plugin management data structure created
// during FE_RegisterPlugins().  There is no return val.
void FE_UnregisterPlugin(void* pluginType)
{
    NPPMgtBlk* pNPMgtBlk = (NPPMgtBlk*)pluginType;
    if(pNPMgtBlk == NULL)
        return;
    
    if(pNPMgtBlk->pLibrary != NULL)
		PR_UnloadLibrary(pNPMgtBlk->pLibrary);
        //::FreeLibrary(pNPMgtBlk->hDLLInstance);
    
    delete [] (char*)*pNPMgtBlk->pPluginFilename;
    delete [] pNPMgtBlk->pPluginFilename;
    delete [] pNPMgtBlk->szMIMEType;
    delete pNPMgtBlk->pPluginFuncs;
    delete pNPMgtBlk;
}

// Provides the entry point used by xp plugin code, (NPGLUE.C), to control
// scroll bars.  The inputs are obvious, and there is no return val.
void FE_ShowScrollBars(MWContext* pContext, XP_Bool show)
{
	if(ABSTRACTCX(pContext)->IsWindowContext())	{
		CWinCX *pWinCX = WINCX(pContext);

		pWinCX->ShowScrollBarCX(SB_BOTH, show);
	}
}

#ifdef XP_WIN32
void*
WFE_BeginSetModuleState()
{
	return AfxSetModuleState(AfxGetAppModuleState());	
}

void
WFE_EndSetModuleState(void* pPrevState)
{
	AfxSetModuleState((AFX_MODULE_STATE*)pPrevState);	
}
#endif

#if 0
void FE_FreeEmbedSessionData(MWContext* pContext, NPEmbeddedApp* pEmbeddedApp)
{
    if(NPL_EmbedDelete(pContext, pEmbeddedApp) == TRUE) {
        if(pEmbeddedApp != NULL) {
            NPWindow* pAppWin = pEmbeddedApp->wdata;
            if(pAppWin->window != NULL) {
                // destroy the plugin client area
                DestroyWindow((HWND)(DWORD)pAppWin->window);
                pAppWin->window = NULL;
            }
            XP_FREE(pAppWin);	
            XP_FREE(pEmbeddedApp);	
        }
    }
}
#endif