// MccPluginHostCtrl.cpp : CMccPluginHostCtrl のインプリメンテーション

#include "stdafx.h"
#include "MccPlugin.h"
#include "MccPluginHostCtrl.h"


#include "nsURLDataCallback.h"
#include "npn.h"

#define NS_4XPLUGIN_CALLBACK(_type, _name) _type (__stdcall * _name)

typedef NS_4XPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks);
typedef NS_4XPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks);
typedef NS_4XPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void);

const kArraySizeIncrement = 10;

nsSimpleArray<CMccPluginHostCtrl::LoadedPluginInfo *> CMccPluginHostCtrl::m_LoadedPlugins;

#define PLG_VERSION_MAJOR  0
#define PLG_VERSION_MINOR  11

NPNetscapeFuncs  g_nfuncs;

void*   Rrr_MemAlloc(uint32 size)
{
	void *m;
	m=malloc(size);
	ATLTRACE("Rrr_MemAlloc address:%lx size:%ld\n",m,size);
	return m;
}

void    Rrr_MemFree(void* ptr)
{
	ATLTRACE("Rrr_MemFree address:%ld\n",ptr);
	free(ptr);
}

const char*	 Rrr_UserAgent(NPP instance)
{
	ATLTRACE("Rrr_UserAgent\n");
	return "Microsoft Internet Explorer";
}

NPError Rrr_GetURL(NPP instance, const char* url, const char* target)
{
	ATLTRACE("Rrr_GetURL\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	WCHAR wurl[520], wtgt[110];

	MultiByteToWideChar(CP_ACP,0,url   ,-1,wurl,512);
	MultiByteToWideChar(CP_ACP,0,target,-1,wtgt,100);

	HlinkSimpleNavigateToString( wurl, NULL, wtgt , ctrl->punkMe,
		NULL, NULL, HLNF_INTERNALJUMP, 0);*/
	return NPERR_NO_ERROR;
}

NPError Rrr_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file)
{	
	ATLTRACE("Rrr_PostURL\n");

/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_PostURL not implemented [%s,%s]",url,window);*/
	return NPERR_GENERIC_ERROR;
}

NPError Rrr_RequestRead(NPStream* stream, NPByteRange* rangeList)
{	
	ATLTRACE("Rrr_RequestRead\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(stream->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_RequestRead not implemented");*/
	return NPERR_GENERIC_ERROR;
}

NPError Rrr_NewStream(NPP instance, NPMIMEType type, const char* window, NPStream** stream)
{	
	ATLTRACE("Rrr_NewStream\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_NewStream not implemented");*/
	return NPERR_GENERIC_ERROR;
}

int32 Rrr_Write(NPP instance, NPStream* stream, int32 len, void* buffer)
{	
	ATLTRACE("Rrr_Write\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_Write not implemented");*/
	return (int32)0;
}

NPError Rrr_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
{	
	ATLTRACE("Rrr_DestroyStream\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_DestroyStream not implemented");*/
	return NPERR_GENERIC_ERROR;
}

void    Rrr_Status(NPP instance, const char* message)
{
	ATLTRACE("Rrr_Status\n");
	// fixme - set statusbar to <message>
	return;
}

uint32  Rrr_MemFlush(uint32 size)
{
	ATLTRACE("Rrr_MemFlush\n");
	return (uint32)0;
}

void    Rrr_ReloadPlugins(NPBool reloadPages)
{
	ATLTRACE("Rrr_ReloadPlugins\n");
	return;
}

JRIEnv* Rrr_GetJavaEnv(void)
{
	ATLTRACE("Rrr_GetJavaEnv\n");
	return NULL;
}

jref    Rrr_GetJavaPeer(NPP instance)
{
	ATLTRACE("Rrr_GetJavaPeer\n");
	return NULL;
}

NPError Rrr_GetURLNotify(NPP instance, const char* url, const char* window, void* notifyData)
{	
	ATLTRACE("Rrr_GetURLNotify\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_GetURLNotify not implemented");*/
	return NPERR_GENERIC_ERROR;
}

NPError Rrr_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
{	
	ATLTRACE("Rrr_PostURLNotify\n");
/*	CMccPluginHostCtrl *ctrl;
	ctrl= (CMccPluginHostCtrl*)(instance->ndata);
	ctrl->DisplayMsg(MSG_E,"NPN_PostURLNotify not implemented");*/
	return NPERR_GENERIC_ERROR;
}

NPError Rrr_GetValue(NPP instance, NPNVariable variable, void *ret_value)
{
	ATLTRACE("Rrr_GetValue\n");
/*	int a;
	char **s;

	s=(char**)ret_value;

	a=(int)variable;
	switch(a) {
	case 30000:
		(*s) = replug_versionstring;
		return NPERR_NO_ERROR;
	}
*/
	return NPERR_GENERIC_ERROR;
}

NPError Rrr_SetValue(NPP instance, NPPVariable variable, void *value)
{
	ATLTRACE("Rrr_SetValue\n");
	return NPERR_GENERIC_ERROR;
}

void Rrr_InvalidateRect(NPP instance, NPRect *rect)
{
	ATLTRACE("Rrr_InvalidateRect\n");
	return;
}

void Rrr_InvalidateRegion(NPP instance, NPRegion region)
{
	ATLTRACE("Rrr_InvalidateRegion\n");
	return;
}

void Rrr_ForceRedraw(NPP instance)
{
	ATLTRACE("Rrr_ForceRedraw\n");
	return;
}

// called by DllMain when control is loaded
void replug_init(void)
{
	ATLTRACE("replug_init()\n");
	// fill in NPN function pointers
    g_nfuncs.size= sizeof(g_nfuncs);
    g_nfuncs.version= (PLG_VERSION_MAJOR << 8) | PLG_VERSION_MINOR;
    g_nfuncs.geturl=            Rrr_GetURL;
    g_nfuncs.posturl=           Rrr_PostURL;
    g_nfuncs.requestread=       Rrr_RequestRead;
    g_nfuncs.newstream=         Rrr_NewStream;
    g_nfuncs.write=             Rrr_Write;
    g_nfuncs.destroystream=     Rrr_DestroyStream;
    g_nfuncs.status=            Rrr_Status;
    g_nfuncs.uagent=            Rrr_UserAgent;
    g_nfuncs.memalloc=          Rrr_MemAlloc;
    g_nfuncs.memfree=           Rrr_MemFree;
    g_nfuncs.memflush=          Rrr_MemFlush;
    g_nfuncs.reloadplugins=     Rrr_ReloadPlugins;
    g_nfuncs.getJavaEnv=        Rrr_GetJavaEnv;
    g_nfuncs.getJavaPeer=       Rrr_GetJavaPeer;
    g_nfuncs.geturlnotify=      Rrr_GetURLNotify;
    g_nfuncs.posturlnotify=     Rrr_PostURLNotify;
	g_nfuncs.getvalue=          Rrr_GetValue;
    g_nfuncs.setvalue=          Rrr_SetValue;
    g_nfuncs.invalidaterect=    Rrr_InvalidateRect;
    g_nfuncs.invalidateregion=  Rrr_InvalidateRegion;
    g_nfuncs.forceredraw=       Rrr_ForceRedraw;
}

/////////////////////////////////////////////////////////////////////////////
// CMccPluginHostCtrl

CMccPluginHostCtrl::CMccPluginHostCtrl()
{
	ATLTRACE("CMccPluginHostCtrl::CMccPluginHostCtrl()\n");
	m_bWindowOnly = TRUE;

    m_bPluginIsAlive = FALSE;
    m_bCreatePluginFromStreamData = FALSE;
    m_pLoadedPlugin = NULL;

    m_nArgs = 0;
    m_nArgsMax = 0;
    m_pszArgNames = NULL;
    m_pszArgValues = NULL;

	replug_init();
    memset(&m_NPPFuncs, 0, sizeof(m_NPPFuncs));
}

CMccPluginHostCtrl::~CMccPluginHostCtrl()
{
}


LPTSTR URLHostAddressAdd(LPTSTR hostAddress,LPTSTR URL)
{
char hostAddressWK[512];
char URLWK[512];
static char mkBuff[512];
char *p;
LPSTR retPoint;
	retPoint = URL;
	strcpy(hostAddressWK,hostAddress);
	strcpy(URLWK,_strlwr(URL));
	memset(mkBuff,0,sizeof(mkBuff));
	if ((p = strstr(URLWK,"//")) != 0)
	{
		return NULL;
	}
	if ((p = strstr(URLWK,"iviewav")) != 0)
	{
		strcat(mkBuff,hostAddressWK);
		strcat(mkBuff,p-1);
		retPoint = (LPTSTR)mkBuff;	
	}
	else if ((p = strstr(URLWK,"iviewisa")) != 0)
	{
		strcat(mkBuff,hostAddressWK);
		strcat(mkBuff,p-1);
		retPoint = (LPTSTR)mkBuff;	
	}
	else if ((p = strstr(URLWK,"almlist")) != 0)
	{
		strcat(mkBuff,hostAddressWK);
		strcat(mkBuff,p-1);
		retPoint = (LPTSTR)mkBuff;	
	}
	return retPoint;
}


LRESULT CMccPluginHostCtrl::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	ATLTRACE("CMccPluginHostCtrl::OnCreate()\n");
	LPTSTR URLTEMP;
    LPTSTR FULLURL;
    SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) | WS_CLIPCHILDREN);

    // Load a list of plugins
    CreatePluginList(
        PLUGINS_FROM_IE | PLUGINS_FROM_NS4X |
        PLUGINS_FROM_NS6X | PLUGINS_FROM_MOZILLA);

    HRESULT hr = E_FAIL;
    if (m_bstrContentType.Length() == 0 &&
        m_bstrSource.Length() != 0)
    {
        USES_CONVERSION;
        // Do a late instantiation of the plugin based on the content type of
        // the stream data.
        m_bCreatePluginFromStreamData = TRUE;

		GetBaseURL(&URLTEMP);
		FULLURL = URLHostAddressAdd(URLTEMP,OLE2T(m_bstrSource));
		if (FULLURL)
		{
			m_bstrSource = FULLURL;
        }
        hr = OpenURLStream(OLE2T(m_bstrSource), NULL, NULL, 0);
    }
    else
    {
        // Create a plugin based upon the specified content type property
        USES_CONVERSION;
        hr = LoadPluginByContentType(OLE2T(m_bstrContentType));
        if (SUCCEEDED(hr))
        {

			GetBaseURL(&URLTEMP);
			FULLURL = URLHostAddressAdd(URLTEMP,OLE2T(m_bstrSource));
			if (FULLURL)
			{
				m_bstrSource = FULLURL;
            }
			hr = CreatePluginInstance();
            if (m_bstrSource.Length())
            {
               OpenURLStream(OLE2T(m_bstrSource), NULL, NULL, 0);
            }
        }
    }

	return SUCCEEDED(hr) ? 0 : -1;
}

LRESULT CMccPluginHostCtrl::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	ATLTRACE(_T("-- CMccPluginHostCtrl::OnDestroy() start------------------------\n"));

    DestroyPluginInstance();
    UnloadPlugin();
    CleanupPluginList();

	ATLTRACE(_T("-- CMccPluginHostCtrl::OnDestroy() end  ------------------------\n"));
    return 0;
}

LRESULT CMccPluginHostCtrl::OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
    SizeToFitPluginInstance();
    return 0;
}

LRESULT CMccPluginHostCtrl::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc;

    hdc = BeginPaint(&ps);
    GetClientRect(&rc);
    FillRect(hdc, &rc, (HBRUSH) GetStockObject(GRAY_BRUSH));
    EndPaint(&ps);

    return 0;
}


HRESULT CMccPluginHostCtrl::GetWebBrowserApp(IWebBrowserApp **pBrowser)
{
	ATLTRACE("CMccPluginHostCtrl::GetWebBrowserApp()\n");
    ATLASSERT(pBrowser);
    if (!pBrowser)
    {
        return E_INVALIDARG;
    }

    // Get the web browser through the site the control is attached to.
    // Note: The control could be running in some other container than IE
    //       so code shouldn't expect this function to work all the time.

    CComPtr<IWebBrowserApp> cpWebBrowser;
    CComQIPtr<IServiceProvider, &IID_IServiceProvider> cpServiceProvider = m_spClientSite;

    HRESULT hr;
    if (cpServiceProvider)
    {
        hr = cpServiceProvider->QueryService(IID_IWebBrowserApp, &cpWebBrowser);
    }
    if (!cpWebBrowser)
    {
        return E_FAIL;
    }

    *pBrowser = cpWebBrowser;
    (*pBrowser)->AddRef();

    return S_OK;
}

HRESULT CMccPluginHostCtrl::GetBaseURL(TCHAR **ppszBaseURL)
{
	ATLTRACE("CMccPluginHostCtrl::GetBaseURL()\n");
    ATLASSERT(ppszBaseURL);
    *ppszBaseURL = NULL;

    CComPtr<IWebBrowserApp> cpWebBrowser;
    GetWebBrowserApp(&cpWebBrowser);
    if (!cpWebBrowser)
    {
        return E_FAIL;
    }

    USES_CONVERSION;
    CComBSTR bstrURL;
    cpWebBrowser->get_LocationURL(&bstrURL);
    
    DWORD cbBaseURL = (bstrURL.Length() + 1) * sizeof(WCHAR);
    DWORD cbBaseURLUsed = 0;
    WCHAR *pszBaseURL = (WCHAR *) malloc(cbBaseURL);
    ATLASSERT(pszBaseURL);

    CoInternetParseUrl(
        bstrURL.m_str,
        PARSE_ROOTDOCUMENT,
        0,
        pszBaseURL,
        cbBaseURL,
        &cbBaseURLUsed,
        0);

    *ppszBaseURL = _tcsdup(W2T(pszBaseURL));
    free(pszBaseURL);

    return S_OK;
}

HRESULT CMccPluginHostCtrl::LoadPluginByContentType(const TCHAR *pszContentType)
{
	ATLTRACE("CMccPluginHostCtrl::LoadPluginByContentType()\n");
    TCHAR * pszPluginPath = NULL;

    // Set the content type
    USES_CONVERSION;
    put_PluginContentType(T2OLE(pszContentType));

    // Search for a plugin that can handle this content
    HRESULT hr = FindPluginPathByContentType(pszContentType, &pszPluginPath);
    if (FAILED(hr))
    {
        // Try the default 'catch-all' plugin
        hr = FindPluginPathByContentType(_T("*"), &pszPluginPath);
        if (FAILED(hr))
        {
            return hr;
        }
    }

    hr = LoadPlugin(pszPluginPath);
    free(pszPluginPath);

    return hr;
}

HRESULT CMccPluginHostCtrl::CreatePluginList(unsigned long ulFlags)
{
	ATLTRACE("CMccPluginHostCtrl::CreatePluginList()\n");
    // This function trawls through the plugin directory and builds a list
    // of plugins and what MIME types each plugin handles.

    CleanupPluginList();

    // Try and obtain a path to the plugins in Netscape 6.x or Mozilla
    if (ulFlags & PLUGINS_FROM_NS6X ||
        ulFlags & PLUGINS_FROM_MOZILLA)
    {
        // TODO search for Mozilla/NS 6.x plugins dir
    }

    // Try and obtain a path to the plugins in Netscape 4.x
    if (ulFlags & PLUGINS_FROM_NS4X)
    {
        TCHAR szPluginsDir[_MAX_PATH];
        memset(szPluginsDir, 0, sizeof(szPluginsDir));
        
        CRegKey keyNS;
        const TCHAR *kNav4xKey = _T("Software\\Netscape\\Netscape Navigator");
        if (keyNS.Open(HKEY_LOCAL_MACHINE, kNav4xKey, KEY_READ) == ERROR_SUCCESS)
        {
            TCHAR szVersion[10];
            DWORD nVersion = sizeof(szVersion) / sizeof(szVersion[0]);
            keyNS.QueryValue(szVersion, _T("CurrentVersion"), &nVersion);
        
            CRegKey keyVersion;
            if (keyVersion.Open(keyNS, szVersion, KEY_READ) == ERROR_SUCCESS)
            {
                CRegKey keyMain;
                if (keyMain.Open(keyVersion, _T("Main"), KEY_READ) == ERROR_SUCCESS)
                {
                    DWORD nPluginsDir = sizeof(szPluginsDir) / sizeof(szPluginsDir[0]);
                    keyMain.QueryValue(szPluginsDir, _T("Plugins Directory"), &nPluginsDir);
                    keyMain.Close();
                }
                keyVersion.Close();
            }
            keyNS.Close();
        }
        if (szPluginsDir[0])
        {
            CreatePluginListFrom(szPluginsDir);
        }
    }

    // Try and obtain a path to the plugins in Internet Explorer
    if (ulFlags & PLUGINS_FROM_IE)
    {
        TCHAR szPluginsDir[_MAX_PATH];
        memset(szPluginsDir, 0, sizeof(szPluginsDir));

        CRegKey keyIE;
        const TCHAR *kIEKey = _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\IEXPLORE.EXE");
        if (keyIE.Open(HKEY_LOCAL_MACHINE, kIEKey, KEY_READ) == ERROR_SUCCESS)
        {
            DWORD nPluginsDir = sizeof(szPluginsDir) / sizeof(szPluginsDir[0]);
            keyIE.QueryValue(szPluginsDir, _T("Path"), &nPluginsDir);

            TCHAR *szSemiColon = _tcschr(szPluginsDir, _TCHAR(';'));
            if (szSemiColon)
            {
                *szSemiColon = _TCHAR('\0');
            }

            ULONG nLen = _tcslen(szPluginsDir);
            if (nLen > 0 && szPluginsDir[nLen - 1] == _TCHAR('\\'))
            {
                szPluginsDir[nLen - 1] = _TCHAR('\0');
            }
            _tcscat(szPluginsDir, _T("\\Plugins"));

            keyIE.Close();
        }
        if (szPluginsDir[0])
        {
            CreatePluginListFrom(szPluginsDir);
        }
    }

    return S_OK;
}

HRESULT CMccPluginHostCtrl::CreatePluginListFrom(const TCHAR *szPluginsDir)
{
	ATLTRACE("CMccPluginHostCtrl::CreatePluginListFrom()\n");
    HANDLE hFind;
    WIN32_FIND_DATA finddata;

    // Change to the plugin directory
    TCHAR szCurrentDir[MAX_PATH + 1];
    GetCurrentDirectory(sizeof(szCurrentDir) / sizeof(szCurrentDir[0]), szCurrentDir);
    SetCurrentDirectory(szPluginsDir);

    // Search for files matching the "np*dll" pattern
    hFind = FindFirstFile(_T("np*dll"), &finddata);
    if (hFind != INVALID_HANDLE_VALUE)
    {
        do {
            PluginInfo *pInfo = new PluginInfo;
            if (!pInfo)
            {
                CleanupPluginList();
                SetCurrentDirectory(szCurrentDir);
                return E_OUTOFMEMORY;
            }
            if (SUCCEEDED(GetPluginInfo(finddata.cFileName, pInfo)))
            {
                pInfo->szPluginName = _tcsdup(finddata.cFileName);
                pInfo->szPluginPath = _tcsdup(szPluginsDir);
                m_Plugins.AppendElement(pInfo);
            }
            else
            {
                ATLTRACE(_T("Error: Cannot plugin info for \"%s\".\n"), finddata.cFileName);
                delete pInfo;
            }
        } while (FindNextFile(hFind, &finddata));
        FindClose(hFind);
    }

    SetCurrentDirectory(szCurrentDir);

    return S_OK;
}


HRESULT CMccPluginHostCtrl::CleanupPluginList()
{
	ATLTRACE("CMccPluginHostCtrl::CleanupPluginList()\n");
    // Free the memory used by the plugin info list
    for (unsigned long i = 0; i < m_Plugins.Count(); i++)
    {
        PluginInfo *pI = m_Plugins[i];
        if (pI->szMIMEType)
            free(pI->szMIMEType);
        if (pI->szPluginName)
            free(pI->szPluginName);
        if (pI->szPluginPath)
            free(pI->szPluginPath);
        free(pI);
    }
    m_Plugins.Empty();
    return S_OK;
}


HRESULT CMccPluginHostCtrl::GetPluginInfo(const TCHAR *pszPluginPath, PluginInfo *pInfo)
{
	ATLTRACE("CMccPluginHostCtrl::GetPluginInfo()\n");
    // Get the version info from the plugin
    USES_CONVERSION;
    DWORD nVersionInfoSize;
    DWORD nZero = 0;
    void *pVersionInfo = NULL;
    nVersionInfoSize = GetFileVersionInfoSize((TCHAR *)pszPluginPath, &nZero);
    if (nVersionInfoSize)
    {
        pVersionInfo = malloc(nVersionInfoSize);
    }
    if (!pVersionInfo)
    {
        return E_OUTOFMEMORY;
    }

    GetFileVersionInfo((TCHAR *)pszPluginPath, NULL, nVersionInfoSize, pVersionInfo);

    // Extract the MIMEType info
    TCHAR *szValue = NULL;
    UINT nValueLength = 0;
    if (!VerQueryValue(pVersionInfo,
        _T("\\StringFileInfo\\040904E4\\MIMEType"),
        (void **) &szValue, &nValueLength))
    {
        return E_FAIL;
    }

	// m_bstrContentType is "text/html" when RVC data is protected.
	// so Nprvc32.dll need to support this type also.
	if (!_tcscmp(szValue,_T("video/x-rvc")))
	{
		_tcsncat(szValue, _T("|text/html"), _tcslen(_T("|text/html")));
	}

    if (pInfo)
    {
        pInfo->szMIMEType = _tcsdup(szValue);
    }

    free(pVersionInfo);

    return S_OK;
}

HRESULT CMccPluginHostCtrl::FindPluginPathByContentType(const TCHAR *pszContentType, TCHAR **ppszPluginPath)
{
	ATLTRACE("CMccPluginHostCtrl::FindPluginPathByContentType()\n");
    *ppszPluginPath = NULL;

    if (pszContentType == NULL)
    {
        return E_FAIL;
    }

    // Search the list of plugins for one that will handle the content type
    TCHAR szPluginPath[_MAX_PATH + 1];
    unsigned long nContentType = _tcslen(pszContentType);
    for (unsigned long i = 0; i < m_Plugins.Count(); i++)
    {
        PluginInfo *pI = m_Plugins[i];
        if (pI->szMIMEType)
        {
            TCHAR *pszMIMEType = pI->szMIMEType;
            do {
                if (_tcsncmp(pszContentType, pszMIMEType, nContentType) == 0)
                {
                    // Found a match
                    _tmakepath(szPluginPath, NULL,
                        pI->szPluginPath, pI->szPluginName, NULL);
                    *ppszPluginPath = _tcsdup(szPluginPath);
                    return S_OK;
                }
                // Check the other types the plugin handles
                pszMIMEType = _tcschr(pszMIMEType, TCHAR('|'));
                if (pszMIMEType)
                {
                    pszMIMEType++;
                }
            } while (pszMIMEType && *pszMIMEType);
        }
    }

    return E_FAIL;
}


// Given a filename with a full path, CD to its directory.
void ChangeToDirectoryOf(const TCHAR *fn)
{
	int p,i;
	char d[MAX_PATH];

	strcpy(d,fn);
	p=0;
	for(i=0;d[i];i++) {   // find last '\'
		if(d[i]=='\\') p=i;
	}
	if(p<1) return;

	d[p]='\0';
	SetCurrentDirectory(d);
}


HRESULT CMccPluginHostCtrl::LoadPlugin(const TCHAR *szPluginPath)
{
	ATLTRACE("CMccPluginHostCtrl::LoadPlugin()\n");
	NPError err;

    ATLASSERT(m_pLoadedPlugin == NULL);
    if (m_pLoadedPlugin)
    {
        return E_UNEXPECTED;
    }

    // TODO critical section

    // Test if the plugin has already been loaded
    for (unsigned long i = 0; i < m_LoadedPlugins.Count(); i++)
    {
        if (_tcscmp(m_LoadedPlugins[i]->szFullPath, szPluginPath) == 0)
        {
            m_pLoadedPlugin = m_LoadedPlugins[i];
            memcpy(&m_NPPFuncs, &m_pLoadedPlugin->NPPFuncs, sizeof(m_NPPFuncs));
            m_pLoadedPlugin->nRefCount++;
            return S_OK;
        }
    }

    // Plugin library is being loaded for the first time so initialise it
    // and store an entry in the loaded plugins array.


	::ChangeToDirectoryOf(szPluginPath);

	// Guard from freeze
	Sleep(100);

	HINSTANCE hInstance=LoadLibrary(szPluginPath);
	if(!hInstance)
	{
	   return E_FAIL;
	}
	m_pLoadedPlugin = new LoadedPluginInfo;
	if (!m_pLoadedPlugin)
	{
		ATLASSERT(m_pLoadedPlugin);
		return E_OUTOFMEMORY;
	}
		

	// Try to retrieve the function pointers by name.

	NP_GETENTRYPOINTS	pfnGetEntryPoints;
	NP_PLUGININIT		pfnPluginInit;
	NP_PLUGINSHUTDOWN	pfnPluginShutdown;

	pfnGetEntryPoints	= (NP_GETENTRYPOINTS)GetProcAddress(hInstance,"NP_GetEntryPoints");
	pfnPluginInit		= (NP_PLUGININIT	)GetProcAddress(hInstance,"NP_Initialize");
	pfnPluginShutdown	= (NP_PLUGINSHUTDOWN)GetProcAddress(hInstance,"NP_Shutdown");

	// If that doesn't work for some reason, try by ordinal.
	if(!pfnGetEntryPoints)
		pfnGetEntryPoints	= (NP_GETENTRYPOINTS)GetProcAddress(hInstance,(LPCSTR)1);
	if(!pfnPluginInit)
		pfnPluginInit		= (NP_PLUGININIT	)GetProcAddress(hInstance,(LPCSTR)2);
	if(!pfnPluginShutdown)
		pfnPluginShutdown	= (NP_PLUGINSHUTDOWN)GetProcAddress(hInstance,(LPCSTR)3);

	if(!pfnGetEntryPoints || !pfnPluginInit	|| !pfnPluginShutdown)
	{
		FreeLibrary(hInstance);
		return E_FAIL;
	}

	ZeroMemory(&m_NPPFuncs,sizeof(m_NPPFuncs));
	m_NPPFuncs.size=sizeof(m_NPPFuncs);

	// Get the plugin function entry points
	err = pfnGetEntryPoints(&m_NPPFuncs);
	if(err!=NPERR_NO_ERROR) 
	{
		FreeLibrary(hInstance);
		return E_FAIL;
	}

	// Tell the plugin to initialize itself
	err = pfnPluginInit(&g_nfuncs);
	if(err != NPERR_NO_ERROR) 
	{
		FreeLibrary(hInstance);
		return E_FAIL;
	}


    // Create a new entry for the plugin
    m_pLoadedPlugin->szFullPath = _tcsdup(szPluginPath);
    m_pLoadedPlugin->nRefCount = 1;
    m_pLoadedPlugin->hInstance = hInstance;
    memcpy(&m_pLoadedPlugin->NPPFuncs, &m_NPPFuncs, sizeof(m_NPPFuncs));

    // Add it to the array
    m_LoadedPlugins.AppendElement(m_pLoadedPlugin);

    return S_OK;
}

HRESULT CMccPluginHostCtrl::UnloadPlugin()
{
	ATLTRACE("CMccPluginHostCtrl::UnloadPlugin()\n");
    if (!m_pLoadedPlugin)
    {
        return E_FAIL;
    }

    // TODO critical section

    ATLASSERT(m_pLoadedPlugin->nRefCount > 0);
    if (m_pLoadedPlugin->nRefCount == 1)
    {
        NP_PLUGINSHUTDOWN pfnShutdown = (NP_PLUGINSHUTDOWN)
            GetProcAddress(
                m_pLoadedPlugin->hInstance,
                "NP_Shutdown");
        if (pfnShutdown)
        {
            pfnShutdown();
        }
        FreeLibrary(m_pLoadedPlugin->hInstance);

        // Delete the entry from the array
        m_LoadedPlugins.RemoveElement(m_pLoadedPlugin);
        free(m_pLoadedPlugin->szFullPath);
        delete m_pLoadedPlugin;
    }
    else
    {
        m_pLoadedPlugin->nRefCount--;
    }

    m_pLoadedPlugin = NULL;

    return S_OK;
}


HRESULT CMccPluginHostCtrl::AddPluginParam(const char *szName, const char *szValue)
{
	ATLTRACE("CMccPluginHostCtrl::AddPluginParam()\n");
    ATLASSERT(szName);
    ATLASSERT(szValue);
    if (!szName || !szValue)
    {
        return E_INVALIDARG;
    }

    // Skip params that already there
    for (unsigned long i = 0; i < m_nArgs; i++)
    {
        if (stricmp(szName, m_pszArgNames[i]) == 0)
        {
            return S_OK;
        }
    }

    // Add the value
    if (!m_pszArgNames)
    {
        ATLASSERT(!m_pszArgValues);
        m_nArgsMax = kArraySizeIncrement;
        m_pszArgNames = (char **) malloc(sizeof(char *) * m_nArgsMax);
        m_pszArgValues = (char **) malloc(sizeof(char *) * m_nArgsMax);
    }
    else if (m_nArgs == m_nArgsMax)
    {
        m_nArgsMax += kArraySizeIncrement;
        m_pszArgNames = (char **) realloc(m_pszArgNames, sizeof(char *) * m_nArgsMax);
        m_pszArgValues = (char **) realloc(m_pszArgValues, sizeof(char *) * m_nArgsMax);
    }
    if (!m_pszArgNames || !m_pszArgValues)
    {
        return E_OUTOFMEMORY;
    }

    m_pszArgNames[m_nArgs] = strdup(szName);
    m_pszArgValues[m_nArgs] = strdup(szValue);

    m_nArgs++;
    
    return S_OK;
}


HRESULT CMccPluginHostCtrl::CreatePluginInstance()
{
	ATLTRACE(" !!!! CMccPluginHostCtrl::CreatePluginInstance()\n");
    m_NPP.pdata = NULL;
    m_NPP.ndata = this;

    USES_CONVERSION;
    char *szContentType = strdup(OLE2A(m_bstrContentType.m_str));

    // Create a child window to house the plugin
    RECT rc;
    GetClientRect(&rc);
    m_wndPlugin.Create(m_hWnd, rc, NULL, WS_CHILD | WS_VISIBLE);

    m_NPWindow.window = (void *) m_wndPlugin.m_hWnd;
    m_NPWindow.type = NPWindowTypeWindow;

    if (m_NPPFuncs.newp)
    {
        // Create the arguments to be fed into the plugin
        if (m_bstrSource.m_str)
        {
            AddPluginParam("SRC", OLE2A(m_bstrSource.m_str));
        }
        if (m_bstrContentType.m_str)
        {
            AddPluginParam("TYPE", OLE2A(m_bstrContentType.m_str));
        }
        if (m_bstrPluginsPage.m_str)
        {
            AddPluginParam("PLUGINSPAGE", OLE2A(m_bstrPluginsPage.m_str));
        }
        char szTmp[50];
        sprintf(szTmp, "%d", (int) (rc.right - rc.left));
        AddPluginParam("WIDTH", szTmp);
        sprintf(szTmp, "%d", (int) (rc.bottom - rc.top));
        AddPluginParam("HEIGHT", szTmp);

        NPSavedData *pSaved = NULL;

        // Create the plugin instance
        NPError npres = m_NPPFuncs.newp(szContentType, &m_NPP, NP_EMBED,
            (short) m_nArgs, m_pszArgNames, m_pszArgValues, pSaved);

        if (npres != NPERR_NO_ERROR)
        {
            return E_FAIL;
        }
    }

    m_bPluginIsAlive = TRUE;

    SizeToFitPluginInstance();

    return S_OK;
}

HRESULT CMccPluginHostCtrl::DestroyPluginInstance()
{
	ATLTRACE(" !!!! CMccPluginHostCtrl::DestroyPluginInstance()\n");
    if (!m_bPluginIsAlive)
    {
        return S_OK;
    }

    // Destroy the plugin
    if (m_NPPFuncs.destroy)
    {
        NPSavedData *pSavedData = NULL;
ATLTRACE("	Pre NPP_Destroy() call\n");
        NPError npres = m_NPPFuncs.destroy(&m_NPP, &pSavedData);
ATLTRACE("	After NPP_Destroy() call\n");

        // TODO could store saved data instead of just deleting it.
        if (pSavedData && pSavedData->buf)
        {
			Rrr_MemFree(pSavedData->buf);
        }
    }

    // Destroy the arguments
    if (m_pszArgNames)
    {
        for (unsigned long i = 0; i < m_nArgs; i++)
        {
            free(m_pszArgNames[i]);
        }
        free(m_pszArgNames);
        m_pszArgNames = NULL;
    }
    if (m_pszArgValues)
    {
        for (unsigned long i = 0; i < m_nArgs; i++)
        {
            free(m_pszArgValues[i]);
        }
        free(m_pszArgValues);
        m_pszArgValues = NULL;
    }

    m_wndPlugin.DestroyWindow();

    m_bPluginIsAlive = FALSE;

    return S_OK;
}

HRESULT CMccPluginHostCtrl::SizeToFitPluginInstance()
{
	ATLTRACE("CMccPluginHostCtrl::SizeToFitPluginInstance()\n");
    if (!m_bPluginIsAlive)
    {
        return S_OK;
    }

    // Resize the plugin to fit the window

    RECT rc;
    GetClientRect(&rc);

    m_wndPlugin.SetWindowPos(HWND_TOP,
        rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
        SWP_NOZORDER);
    
    m_NPWindow.x = 0;
    m_NPWindow.y = 0;
    m_NPWindow.width = rc.right - rc.left;
    m_NPWindow.height = rc.bottom - rc.top;
    m_NPWindow.clipRect.left = 0;
    m_NPWindow.clipRect.top = 0;
    m_NPWindow.clipRect.right = (unsigned short)m_NPWindow.width;
    m_NPWindow.clipRect.bottom = (unsigned short)m_NPWindow.height;

    if (m_NPPFuncs.setwindow)
    {
       NPError npres = m_NPPFuncs.setwindow(&m_NPP, &m_NPWindow);
    }

    return S_OK;
}

HRESULT CMccPluginHostCtrl::OpenURLStream(const TCHAR *szURL, void *pNotifyData, const void *pPostData, unsigned long nPostDataLength)
{
	ATLTRACE("CMccPluginHostCtrl::OpenURLStream()\n");
    nsURLDataCallback::OpenURL(this, szURL, pNotifyData, pPostData, nPostDataLength);
    return S_OK;
}

///////////////////////////////////////////////////////////////////////////////
// IMccPluginHostCtrl

STDMETHODIMP CMccPluginHostCtrl::SetInterfaceSafetyOptions(REFIID riid, DWORD dwSupportedOptions, DWORD dwEnabledOptions)
{
	ATLTRACE("CMccPluginHostCtrl::SetInterfaceSafetyOptions()\n");
	if (riid == IID_IPersistPropertyBag)
	{
		if ((dwEnabledOptions == INTERFACESAFE_FOR_UNTRUSTED_DATA	)||
			(dwEnabledOptions == INTERFACESAFE_FOR_UNTRUSTED_CALLER	)	)
		{
			return S_OK;
		}
		return E_FAIL;
	}
	return IObjectSafetyImpl<CMccPluginHostCtrl, INTERFACESAFE_FOR_UNTRUSTED_DATA>::SetInterfaceSafetyOptions(riid, dwSupportedOptions, dwEnabledOptions);
}

STDMETHODIMP CMccPluginHostCtrl::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
	ATLTRACE("CMccPluginHostCtrl::Load()\n");
    CComQIPtr<IPropertyBag2> cpPropBag2 = pPropBag;
    if (cpPropBag2)
    {
        // Read *all* the properties via IPropertyBag2 and store them somewhere
        // so they can be fed into the plugin instance at creation..
        ULONG nProperties;
        cpPropBag2->CountProperties(&nProperties);
        if (nProperties > 0)
        {
            PROPBAG2 *pProperties = (PROPBAG2 *) malloc(sizeof(PROPBAG2) * nProperties);
            ULONG nPropertiesGotten = 0;
            cpPropBag2->GetPropertyInfo(0, nProperties, pProperties, &nPropertiesGotten);
            for (ULONG i = 0; i < nPropertiesGotten; i++)
            {
                if (pProperties[i].vt == VT_BSTR)
                {
                    USES_CONVERSION;
                    CComVariant v;
                    HRESULT hrRead;
                    cpPropBag2->Read(1, &pProperties[i], NULL, &v, &hrRead);
                    AddPluginParam(OLE2A(pProperties[i].pstrName), OLE2A(v.bstrVal));
                }
                if (pProperties[i].pstrName)
                {
                    CoTaskMemFree(pProperties[i].pstrName);
                }
            }
            free(pProperties);
        }
    }
    return IPersistPropertyBagImpl<CMccPluginHostCtrl>::Load(pPropBag, pErrorLog);
}

///////////////////////////////////////////////////////////////////////////////
// IMccPluginHostCtrl

STDMETHODIMP CMccPluginHostCtrl::get_PluginContentType(BSTR *pVal)
{
    if (!pVal)
    {
        return E_INVALIDARG;
    }
    *pVal = m_bstrContentType.Copy();
	return S_OK;
}

STDMETHODIMP CMccPluginHostCtrl::put_PluginContentType(BSTR newVal)
{
    // Security. Copying the source BSTR this way ensures that embedded NULL
    // characters do not end up in the destination BSTR. SysAllocString will
    // create a copy truncated at the first NULL char.
    m_bstrContentType.Empty();
    m_bstrContentType.Attach(SysAllocString(newVal));
	return S_OK;
}

STDMETHODIMP CMccPluginHostCtrl::get_PluginSource(BSTR *pVal)
{
    if (!pVal)
    {
        return E_INVALIDARG;
    }
    *pVal = m_bstrSource.Copy();
	return S_OK;
}

STDMETHODIMP CMccPluginHostCtrl::put_PluginSource(BSTR newVal)
{
    // Security. Copying the source BSTR this way ensures that embedded NULL
    // characters do not end up in the destination BSTR. SysAllocString will
    // create a copy truncated at the first NULL char.
    m_bstrSource.Empty();
    m_bstrSource.Attach(SysAllocString(newVal));
	return S_OK;
}

STDMETHODIMP CMccPluginHostCtrl::get_PluginsPage(BSTR *pVal)
{
    if (!pVal)
    {
        return E_INVALIDARG;
    }
    *pVal = m_bstrPluginsPage.Copy();
	return S_OK;
}

STDMETHODIMP CMccPluginHostCtrl::put_PluginsPage(BSTR newVal)
{
    // Security. Copying the source BSTR this way ensures that embedded NULL
    // characters do not end up in the destination BSTR. SysAllocString will
    // create a copy truncated at the first NULL char.
    m_bstrPluginsPage.Empty();
    m_bstrPluginsPage.Attach(SysAllocString(newVal));
	return S_OK;
}