/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: cookies.cpp,v 1.19.20.4 2004/07/09 01:48:15 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. 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
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 *                 Phil Dibowitz
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxcom.h"
#include "hxtypes.h"
#include "hlxclib/string.h"
// #include "hlxclib/stdio.h"
#include "safestring.h"

#ifdef _MACINTOSH
#ifdef _CARBON
#ifdef _MAC_MACHO
#include <sys/stat.h>
#else
#include <stat.h>
#endif
#else /* _CARBON */
#include <stat.mac.h>
#endif /* _CARBON */
#else /* _MACINTOSH */
#include "hlxclib/sys/types.h"
#include "hlxclib/sys/stat.h"
#endif /* _MACINTOSH */

#include "hlxclib/time.h"

#if defined(_AIX)
#include <ctype.h>
#endif

#ifdef _WINDOWS
#include "hlxclib/windows.h"
#include <wininet.h>
#include "hlxclib/io.h"
#endif /* _WINDOWS */

#if defined(_CARBON) || defined(_MAC_MACHO)
#include "hxver.h" // for HXVER_SDK_PRODUCT
#endif

#ifdef _CARBON
#include "cfwrappers.h"
#endif

#include "hxresult.h"
#include "hxslist.h"
#include "chxpckts.h"
#include "hxstrutl.h"
#include "dbcs.h"
#include "dllpath.h"
#include "hxprefs.h"
#include "hxthread.h"
#include "ihxcookies.h"
#include "cookhlpr.h"
#include "ihxcookies2.h"
#include "hxdate.h"
#include "cookies.h"
#include "md5.h"
#include "filespecutils.h"

#ifdef _UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif

#if defined (_UNIX) && !defined(_SUN) && !defined(_HPUX) && !defined(_IRIX) && !defined(_OSF1)
#include <sys/file.h>
#endif /* UNIX */

#define RM_COOKIE_CAPTION	"# Helix Cookie File\n# http://www.netscape.com/newsref/std/cookie_spec.html\n# This is a generated file!  Do not edit.\n\n"

#if defined(_UNIX) && !defined(_MAC_UNIX)
#  define RM_COOKIE_FILE_NAME "Cookies_6_0"
#else
#  define RM_COOKIE_FILE_NAME "cookies.txt"
#endif

/* We should really define it in a common header file */
#if defined (_WINDOWS ) || defined (WIN32) || defined(_SYMBIAN)
#define OS_SEPARATOR_CHAR	'\\'
#define OS_SEPARATOR_STRING	"\\"
#elif defined (_UNIX) || defined (_OPENWAVE)
#define OS_SEPARATOR_CHAR	'/'
#define OS_SEPARATOR_STRING	"/"
#elif defined (_MACINTOSH)
#define OS_SEPARATOR_CHAR	':'
#define OS_SEPARATOR_STRING	":"
#endif /* defined (_WINDOWS ) || defined (WIN32) */

HXCookies::HXCookies(IUnknown* pContext, BOOL bMemoryOnly)
	: m_lRefCount(0)	
	, m_bSaveCookies(FALSE)
	, m_bMemoryOnly(bMemoryOnly)
	, m_pNSCookiesPath(NULL)
	, m_pNSCookies(NULL)
	, m_pRMCookiesPath(NULL)
	, m_pRMCookies(NULL)
	, m_pCookiesHelper(NULL)
	, m_pPreferences(NULL)
	, m_bInitialized(FALSE)
	, m_lastModification(0)
	, m_pContext(NULL)
#ifdef _WINDOWS
	, m_hLib(NULL)
	, m_pLock(NULL)
	, _pInternetSetCookie(NULL)
	, _pInternetGetCookie(NULL)
#elif _UNIX
	, m_fileID(0)
#endif /* _WINDOWS */
{    
    if (pContext)
    {
	m_pContext = pContext;
	m_pContext->AddRef();
    }
}

HXCookies::~HXCookies()
{
    Close();
}

STDMETHODIMP
HXCookies::QueryInterface(REFIID riid, void**ppvObj)
{
    QInterfaceList qiList[] =
    {
	{ GET_IIDHANDLE(IID_IUnknown), this },
	{ GET_IIDHANDLE(IID_IHXCookies), (IHXCookies*) this },
	{ GET_IIDHANDLE(IID_IHXCookies2), (IHXCookies2*) this }
    };	

    if (QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj) == HXR_OK)
    {
        return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXCookiesHelper) && m_pCookiesHelper)
    {
	return m_pCookiesHelper->QueryInterface(riid, ppvObj);
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::AddRef
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) 
HXCookies::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::Release
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) 
HXCookies::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

HX_RESULT    
HXCookies::Initialize(void)
{
    HX_RESULT	hr = HXR_OK;
    
    if (!m_pContext || !IsCookieEnabled())
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    m_pContext->QueryInterface(IID_IHXCookiesHelper, (void**) &m_pCookiesHelper);
    if (!m_pCookiesHelper)
    {
        HXCookiesHelper* pHelper = new HXCookiesHelper();
        if (pHelper)
	{
            pHelper->QueryInterface(IID_IHXCookiesHelper, (void**) &m_pCookiesHelper);
	}
    }

    if (!m_bMemoryOnly)
    {
#ifdef _WINDOWS
        if (m_hLib = LoadLibrary(OS_STRING("wininet.dll")))
	{
       	    _pInternetSetCookie = (INTERNETSETCOOKIE)GetProcAddress(m_hLib, OS_STRING("InternetSetCookieA"));
	    _pInternetGetCookie = (INTERNETGETCOOKIE)GetProcAddress(m_hLib, OS_STRING("InternetGetCookieA"));
	}
#endif /* _WINDOWS */

        PrepareCookiesPath();

        if (m_pNSCookiesPath)
	{
	    OpenCookies(m_pNSCookiesPath, FALSE, m_pNSCookies);
	}

        if (m_pRMCookiesPath)
	{
	    OpenCookies(m_pRMCookiesPath, TRUE, m_pRMCookies);
        }
    }

    m_bInitialized = TRUE;

cleanup:

    return hr;
}

void	
HXCookies::Close(void)
{
    if (m_bSaveCookies && !m_bMemoryOnly)
    {
	SaveCookies();
    }

    ResetCookies(m_pNSCookies);
    ResetCookies(m_pRMCookies);

    HX_DELETE(m_pNSCookies);
    HX_DELETE(m_pRMCookies);

    HX_VECTOR_DELETE(m_pNSCookiesPath);
    HX_VECTOR_DELETE(m_pRMCookiesPath);

    HX_RELEASE(m_pPreferences);
    HX_RELEASE(m_pCookiesHelper);
    HX_RELEASE(m_pContext);

    m_bInitialized = FALSE;

#ifdef _WINDOWS
    if (m_hLib)
    {
	FreeLibrary(m_hLib);
	m_hLib = NULL;
    }

    HX_DELETE(m_pLock);
#endif /* _WINDOWS */
}

STDMETHODIMP 
HXCookies::SetCookies(const char* pHost, const char* pPath, IHXBuffer* pCookies)
{
    HX_RESULT	    hr = HXR_OK;
    int		    host_length = 0;
    int		    path_length = 0;
    int		    domain_length = 0;
    char*	    pURL = NULL;
    char*	    path_from_header = NULL;
    char*	    domain_from_header = NULL;
    char*	    name_from_header = NULL;
    char*	    cookie_from_header = NULL;
    char*	    dot = NULL;
    char*	    slash = NULL;
    BOOL	    bIsDomain = FALSE;
    time_t	    expires=0;
    IHXBuffer*	    pBuffer = NULL;
    IHXValues*	    pValues = NULL;    
    CookieStruct*   pNewCookie = NULL;
    
    if (!IsCookieEnabled())
    {
	goto cleanup;
    }

    if (!m_bInitialized)
    {
	hr = Initialize();

	if (HXR_OK != hr)
	{
	    goto cleanup;
	}
    }

    if (!m_pCookiesHelper || !pCookies || !pHost)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != m_pCookiesHelper->Pack(pCookies, pValues))
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK == pValues->GetPropertyBuffer("path", pBuffer) && pBuffer)
    {
	::StrAllocCopy(path_from_header, (char*)pBuffer->GetBuffer());
    }
    HX_RELEASE(pBuffer);

    if (HXR_OK == pValues->GetPropertyBuffer("domain", pBuffer) && pBuffer)
    {
        // verify that this host has the authority to set for
        // this domain.   We do this by making sure that the
        // host is in the domain
        // We also require that a domain have at least two
        // periods to prevent domains of the form ".com"
        // and ".edu"
	//
	// also make sure that there is more stuff after
	// the second dot to prevent ".com."
	::StrAllocCopy(domain_from_header, (char*)pBuffer->GetBuffer());

        dot = strchr(domain_from_header, '.');
    
	if(dot)
	{
            dot = strchr(dot+1, '.');
	}

	if(!dot || *(dot+1) == '\0') 
	{
	    // did not pass two dot test. FAIL
	    hr = HXR_FAILED;
	    goto cleanup;
	}

	domain_length = strlen(domain_from_header);
	host_length = strlen(pHost);

	// check to see if the host is in the domain
	if(domain_length > host_length || 
	   strcasecmp(domain_from_header, &pHost[host_length-domain_length]))
	{
	    hr = HXR_FAILED;		
	    goto cleanup;
	}

	bIsDomain = TRUE;	
    }
    HX_RELEASE(pBuffer);

    if (HXR_OK == pValues->GetPropertyBuffer("name", pBuffer) && pBuffer)
    {
	::StrAllocCopy(name_from_header, (char*)pBuffer->GetBuffer());
    }
    HX_RELEASE(pBuffer);

    if (HXR_OK == pValues->GetPropertyBuffer("value", pBuffer) && pBuffer)
    {
	::StrAllocCopy(cookie_from_header, (char*)pBuffer->GetBuffer());
    }
    HX_RELEASE(pBuffer);

    if (HXR_OK == pValues->GetPropertyBuffer("expires", pBuffer) && pBuffer)
    {
	expires = ::ParseDate((char*)pBuffer->GetBuffer());
    }
    HX_RELEASE(pBuffer);

    if(!path_from_header && pPath)
    {
        // strip down everything after the last slash
        // to get the path.
	CHXString strPath = pPath;
	INT32 nIndex = strPath.ReverseFind('/');

	if (nIndex != -1)
	{
	    strPath = strPath.Left(nIndex + 1);
	}

	::StrAllocCopy(path_from_header, (char*) (const char*) strPath);
    }

    if(!domain_from_header && pHost)
    {
	::StrAllocCopy(domain_from_header, (char*)pHost); 
    }
    
    m_bSaveCookies = TRUE;

    // We have decided we are going to insert a cookie into the list
    // Get the cookie lock so that we can munge on the list

    pNewCookie = CheckForPrevCookie(path_from_header, 
				    domain_from_header, 
				    name_from_header);

    if(pNewCookie) 
    {
	HX_DELETE(pNewCookie->pCookieValue);
	HX_DELETE(pNewCookie->pCookieName);
	HX_DELETE(pNewCookie->pPath);
        HX_DELETE(pNewCookie->pHost);
        
	pNewCookie->pCookieValue = new CHXString(cookie_from_header);
	pNewCookie->pCookieName = new CHXString(name_from_header);
        pNewCookie->pPath = new CHXString(path_from_header);
        pNewCookie->pHost = new CHXString(domain_from_header);
        pNewCookie->expires = expires;
        pNewCookie->bIsDomain = bIsDomain;
	pNewCookie->bMemoryOnly = TRUE;
    }	
    else 
    {
	pNewCookie = new CookieStruct;

        // copy
	pNewCookie->pCookieValue = new CHXString(cookie_from_header);
	pNewCookie->pCookieName = new CHXString(name_from_header);
        pNewCookie->pPath = new CHXString(path_from_header);
        pNewCookie->pHost = new CHXString(domain_from_header);
        pNewCookie->expires = expires;
        pNewCookie->bIsDomain = bIsDomain;
	pNewCookie->bMemoryOnly = TRUE;

	if (!m_pRMCookies)
	{
	    m_pRMCookies = new CHXSimpleList();
	}

	hr = AddCookie(pNewCookie, m_pRMCookies);
    }

#ifdef _WINDOWS
    if (!m_bMemoryOnly)
    {
        if (!_pInternetSetCookie)
	{
	    goto cleanup;
	}

	host_length = strlen(pHost);

	if (pPath)
	{
	    path_length = strlen(pPath);
	}

	pURL = new char[host_length + path_length + 8];
	sprintf(pURL, "http://%s%s", pHost, pPath); /* Flawfinder: ignore */

	_pInternetSetCookie(pURL, NULL, (const char*)pCookies->GetBuffer());
    }
#endif /* _WINDOWS */

cleanup:

    HX_RELEASE(pBuffer);
    HX_RELEASE(pValues);

    HX_VECTOR_DELETE(pURL);
    HX_VECTOR_DELETE(path_from_header);
    HX_VECTOR_DELETE(domain_from_header);
    HX_VECTOR_DELETE(name_from_header);
    HX_VECTOR_DELETE(cookie_from_header);
    
    return hr;
}

STDMETHODIMP 
HXCookies::GetCookies(const char* pHost, const char* pPath, REF(IHXBuffer*) pCookies)
{
    IHXBuffer* pPlayerCookies = NULL;
    HX_RESULT res = GetCookiesInternal(pHost, pPath, pCookies, pPlayerCookies);
    HX_RELEASE(pPlayerCookies);
    return res;
}

STDMETHODIMP HXCookies::GetCookies(const char*	    pHost,
		      const char*	    pPath,
		      REF(IHXBuffer*)   pCookies,
		      REF(IHXBuffer*)   pPlayerCookies)
{
    return GetCookiesInternal(pHost, pPath, pCookies, pPlayerCookies);
}


#if defined(_CARBON) || defined(_MAC_MACHO)
#define REPORT_COOKIES 1
#endif

HX_RESULT HXCookies::GetCookiesInternal(const char* pHost,
				 const char*	    pPath,
				 REF(IHXBuffer*)   pCookies,
				 REF(IHXBuffer*)   pPlayerCookies)
{
    HX_RESULT	    hr = HXR_OK;
    char*	    cp = NULL;
    char*	    pComma = NULL;
    char*	    pEqualSign = NULL;
    char*	    pData = NULL;
    char*	    pURL = NULL;
    int		    l = 0;
    int		    host_length = 0;
    int		    path_length = 0;
    BOOL	    bAdded = FALSE;
UINT32	    dwSize = 0;    
    time_t	    cur_time = time(NULL);
    CookieStruct*   pTempCookie = NULL;
    CookieStruct*   pNewCookie = NULL;
    CHXSimpleList*  pCookiesFound1 = NULL;
    CHXSimpleList*  pCookiesFound2 = NULL;
    CHXSimpleList*  pCookiesList = NULL;
    IHXValues*	    pValues = NULL;
    CHXSimpleList::Iterator  i;
    CHXString       cHostCopy;
    INT32           lColon;
    BOOL	    bIsPlayerCookieList = FALSE;
    CHXString	    strPlayerCookies;
#ifdef REPORT_COOKIES
    CHXString		strCookieReport;
#endif
    
    pCookies = NULL;
    pPlayerCookies = NULL;

    if (!IsCookieEnabled())
    {
	goto cleanup;
    }

    if (!m_bInitialized)
    {
	hr = Initialize();

	if (HXR_OK != hr)
	{
	    goto cleanup;
	}
    }
    else
    {
	SyncRMCookies(FALSE);
    }

    if (!pHost || !m_pCookiesHelper)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    // return string to build    
    if (!(pValues = new CHXHeader()))
    {
	hr = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    pValues->AddRef();

    cHostCopy = pHost;
    lColon    = cHostCopy.Find(':');
    if (lColon >= 0)
    {
	cHostCopy = cHostCopy.Left(lColon);
    }
    cHostCopy.MakeLower();

    // search for all cookies(Netscape only for now)
    for (l = 0; l < 2; l++)
    {
	switch (l)
	{
	case 0:
	    pCookiesList = m_pRMCookies;
	    bIsPlayerCookieList = TRUE;
	    break;
	case 1:
	    pCookiesList = m_pNSCookies;
	    bIsPlayerCookieList = FALSE;
	    break;
	default:
	    break;
	}

	if (!pCookiesList)
	{
	    continue;
	}

	for (i = pCookiesList->Begin(); i != pCookiesList->End(); ++i)
	{
	    pTempCookie = (CookieStruct*) (*i);
    
	    if (!pTempCookie->pHost)
	    {
		continue;
	    }
	    
	    // check the host or domain first
	    if(pTempCookie->bIsDomain)
	    {
		if (!DoesDomainMatch(*pTempCookie->pHost, cHostCopy))
		{
		    continue;
		}
	    }
	    else if(strcasecmp((const char*)*(pTempCookie->pHost), pHost))
	    {
		// hostname matchup failed.
		continue;
	    }

	    // shorter strings always come last so there can be no
	    // ambiquity						     	      
	    if(pTempCookie->pPath && 
	       !strncmp(pPath, (const char*)*(pTempCookie->pPath), pTempCookie->pPath->GetLength()))
	    {
		// check for expired cookies
		if(pTempCookie->expires && (pTempCookie->expires < cur_time))
		{
		    m_bSaveCookies = TRUE;
		    continue;
		}

		if (!pCookiesFound1)
		{
		    pCookiesFound1 = new CHXSimpleList();
		    pCookiesFound1->AddTail(pTempCookie);
		}
		else if (!WasCookieAdded(pCookiesFound1, pTempCookie))
		{
		    pCookiesFound1->AddTail(pTempCookie);
		}
		if(bIsPlayerCookieList)
		{
		    if(!strPlayerCookies.IsEmpty())
		    {
		        strPlayerCookies += "|";
		    }
		    strPlayerCookies += *pTempCookie->pCookieName;
#ifdef REPORT_COOKIES
		    strCookieReport += (strCookieReport.IsEmpty() ? "" : ", ");
		    strCookieReport += *pTempCookie->pCookieName;
		    strCookieReport += "=";
		    strCookieReport += *pTempCookie->pCookieValue;
#endif
		}
	    }
	}
    }

#ifdef _WINDOWS
    if (!_pInternetGetCookie || m_bMemoryOnly)
    {
	goto cleanup;
    }

    host_length = strlen(pHost);

    if (pPath)
    {
	path_length = strlen(pPath);
    }

    pURL = new char[host_length + path_length + 8];
    sprintf(pURL, "http://%s%s", pHost, pPath); /* Flawfinder: ignore */

    if (_pInternetGetCookie(pURL, NULL, pData, &dwSize) && !pData && dwSize)
    {
	pData = new char[dwSize+1];

	if (!_pInternetGetCookie(pURL, NULL, pData, &dwSize))
	{
	    goto cleanup;
	}

	cp = pData;

	while (pComma = (char*) ::HXFindChar(cp, ';'))
	{
	    *pComma = '\0';
	    pComma++;

	    if (pEqualSign = (char*) ::HXFindChar(cp, '='))
	    {
		*pEqualSign = '\0';
		pEqualSign++;

        	pNewCookie = new CookieStruct;
		bAdded = FALSE;

		// copy
		pNewCookie->pCookieValue = new CHXString(pEqualSign);
		pNewCookie->pCookieName = new CHXString(cp);
		pNewCookie->pPath = new CHXString(pPath);
		pNewCookie->pHost = new CHXString(pHost);
		pNewCookie->expires = 0;
		pNewCookie->bIsDomain = FALSE;
		pNewCookie->bMemoryOnly = FALSE;

		if(pNewCookie->pCookieName)
		{
		    pNewCookie->pCookieName->TrimLeft();
		    pNewCookie->pCookieName->TrimRight();
		}

		if (!WasCookieAdded(pCookiesFound1, pNewCookie))
		{
		    if (!pCookiesFound2)
		    {
			pCookiesFound2 = new CHXSimpleList();
			pCookiesFound2->AddTail(pNewCookie);
			bAdded = TRUE;
		    }
		    else if (!WasCookieAdded(pCookiesFound2, pNewCookie))
		    {
			pCookiesFound2->AddTail(pNewCookie);
			bAdded = TRUE;
		    }
		}
		
		if (!bAdded)
		{
		    HX_DELETE(pNewCookie);
		}
	    }

	    cp = pComma;
	}

	if (pEqualSign = (char*) ::HXFindChar(cp, '='))
	{
	    *pEqualSign = '\0';
	    pEqualSign++;

	    pNewCookie = new CookieStruct;
	    bAdded = FALSE;

	    // copy
	    pNewCookie->pCookieValue = new CHXString(pEqualSign);
	    pNewCookie->pCookieName = new CHXString(cp);
	    pNewCookie->pPath = NULL;
	    pNewCookie->pHost = NULL;
	    pNewCookie->expires = 0;
	    pNewCookie->bIsDomain = FALSE;
	    pNewCookie->bMemoryOnly = FALSE;

	    if(pNewCookie->pCookieName)
	    {
	        pNewCookie->pCookieName->TrimLeft();
	        pNewCookie->pCookieName->TrimRight();
	    }

	    if (!WasCookieAdded(pCookiesFound1, pNewCookie))
	    {
		if (!pCookiesFound2)
		{
		    pCookiesFound2 = new CHXSimpleList();
		    pCookiesFound2->AddTail(pNewCookie);
		    bAdded = TRUE;
		}
		else if (!WasCookieAdded(pCookiesFound2, pNewCookie))
		{
		    pCookiesFound2->AddTail(pNewCookie);
		    bAdded = TRUE;
		}
	    }

	    if (!bAdded)
	    {
		HX_DELETE(pNewCookie);
	    }
	}
    }
#endif /* _WINDOWS */

cleanup:

    if (pCookiesFound1)
    {
	for (i = pCookiesFound1->Begin(); i != pCookiesFound1->End(); ++i)
	{
	    pTempCookie = (CookieStruct*) (*i);

	    if(pTempCookie->pCookieName && pTempCookie->pCookieValue)
	    {
		::SaveStringToHeader(pValues,
				     (const char*)*(pTempCookie->pCookieName),
				     (char*)(const char*)*(pTempCookie->pCookieValue));
	    }
	}
    }

    if (pCookiesFound2)
    {
	for (i = pCookiesFound2->Begin(); i != pCookiesFound2->End(); ++i)
	{
	    pTempCookie = (CookieStruct*) (*i);

	    if(pTempCookie->pCookieName && pTempCookie->pCookieValue)
	    {
		::SaveStringToHeader(pValues,
				     (const char*)*(pTempCookie->pCookieName),
				     (char*)(const char*)*(pTempCookie->pCookieValue));
	    }

	    HX_DELETE(pTempCookie);
	}
    }

    if (m_pCookiesHelper)
    {
	hr = m_pCookiesHelper->UnPack(pValues, pCookies);
    }

#ifdef REPORT_COOKIES
	// if the preference "CookieData" is set to "console" then write the cookie requests to the console
    IHXBuffer* pBuff = NULL;
    if (m_pPreferences
		&& m_pPreferences->ReadPref("CookieData", pBuff) == HXR_OK
		&& 0 == stricmp((const char*)pBuff->GetBuffer(), "console"))
    {
	CHXString str;
	str.Format("Cookies requested, getting host: %s  path: %s  cookies: %s",
			pHost, pPath, 
			strCookieReport.IsEmpty() ? "<no cookies>" : (const char *) strCookieReport);
#if defined(_CARBON) || defined(_MAC_MACHO)
	CFStringRef stringRef = ::CFStringCreateWithCString( kCFAllocatorDefault, (const char*) str, kCFStringEncodingUTF8 );
	::CFShow(stringRef);
	::CFRelease(stringRef);
#endif
    }
    HX_RELEASE(pBuff);
#endif

    if(!strPlayerCookies.IsEmpty())
    {
	pPlayerCookies = new CHXBuffer;
	pPlayerCookies->AddRef();
	pPlayerCookies->Set((const UCHAR*)(const char*)strPlayerCookies, strPlayerCookies.GetLength() + 1);
    }

    HX_DELETE(pCookiesFound1);
    HX_DELETE(pCookiesFound2);
    HX_VECTOR_DELETE(pData);
    HX_VECTOR_DELETE(pURL);

    HX_RELEASE(pValues);
   
    return hr;
}

HX_RESULT	    
HXCookies::PrepareCookiesPath(void)
{
    HX_RESULT		hr = HXR_OK;
#ifndef _VXWORKS
    const char*		pRMCookiesPath = NULL;
    IHXBuffer*		pBuffer = NULL;

#if !defined(_CARBON) && !defined(_MAC_MACHO) /* figured out every time at runtime on the Mac since paths are not stable between runs */
    if (m_pPreferences && (m_pPreferences->ReadPref("CookiesPath", pBuffer) == HXR_OK))
    {
	m_pRMCookiesPath = new char[pBuffer->GetSize() + 1];
	strcpy(m_pRMCookiesPath, (const char*)pBuffer->GetBuffer()); /* Flawfinder: ignore */
    }
    HX_RELEASE(pBuffer);
#endif

    if( !m_pRMCookiesPath )
    {
	if (m_pPreferences && (m_pPreferences->ReadPref("UserSDKDataPath", pBuffer) == HXR_OK))
	{
	    pRMCookiesPath = (char*) pBuffer->GetBuffer();
	}
	else
	{
#if defined(_CARBON) || defined(_MAC_MACHO)
		CHXString strSDKPath = CHXFileSpecUtils::GetAppDataDir(HXVER_SDK_PRODUCT).GetPathName();
#elif defined(_UNIX)
	    pRMCookiesPath = getenv("HOME");
	    HX_ASSERT( pRMCookiesPath );	    
#else
	    pRMCookiesPath = GetDLLAccessPath()->GetPath(DLLTYPE_COMMON);
#endif        
	}
	
        if( pRMCookiesPath )
        {
        
            m_pRMCookiesPath = new char[strlen(pRMCookiesPath) + strlen(RM_COOKIE_FILE_NAME)+2];
	    ::strcpy(m_pRMCookiesPath, pRMCookiesPath); /* Flawfinder: ignore */
	    if (m_pRMCookiesPath[::strlen(m_pRMCookiesPath)-1] != OS_SEPARATOR_CHAR)
	    {
		strcat(m_pRMCookiesPath, OS_SEPARATOR_STRING); /* Flawfinder: ignore */
	    }

	    strcat(m_pRMCookiesPath, RM_COOKIE_FILE_NAME); /* Flawfinder: ignore */
            
	    HX_RELEASE(pBuffer);
            pBuffer = new CHXBuffer();
            pBuffer->AddRef();
            
            pBuffer->Set((const unsigned char*)m_pRMCookiesPath, strlen(m_pRMCookiesPath) + 1);    	
            
            if (m_pPreferences)
            {
                m_pPreferences->WritePref("CookiesPath", pBuffer);
            }
	
            HX_RELEASE(pBuffer);
        }
   }
#endif

#ifdef _WINDOWS
    HKEY    hMainAppKey = NULL;
    HKEY    hBiffKey = NULL;
    HKEY    hUsersKey = NULL;
    HKEY    hServersKey = NULL;
    HKEY    hCookiesKey = NULL;
    INT32   lSize = _MAX_PATH - 1;
    UINT32  ulSize = _MAX_PATH - 1;
    UINT32  ulType = 0;
    char    regKey[_MAX_PATH] = {0}; /* Flawfinder: ignore */
    char*   pPath = NULL;
    char*   pUser = NULL;
    char*   pValue = NULL;
    char*   pCursor = NULL;

    // Netscape Browser 4.0+
    if (RegOpenKeyEx(HKEY_CURRENT_USER, 
		     OS_STRING("Software\\Netscape\\Netscape Navigator\\biff"), 
		     0,
		     KEY_READ, &hBiffKey) == ERROR_SUCCESS) 
    {
	pUser = new char[_MAX_PATH];
	memset(pUser, 0, _MAX_PATH);

	if (RegQueryValueEx(hBiffKey, OS_STRING("CurrentUser"), 0, &ulType, (LPBYTE)pUser, &ulSize) == ERROR_SUCCESS) 
	{
	    SafeSprintf(regKey, _MAX_PATH, "Software\\Netscape\\Netscape Navigator\\biff\\users\\%s", pUser);
	    
	    if (RegOpenKeyEx(HKEY_CURRENT_USER, OS_STRING(regKey), 0, KEY_READ, &hUsersKey) == ERROR_SUCCESS)
	    {
		pValue = new char[_MAX_PATH];
		memset(pValue, 0, _MAX_PATH);

		ulSize = _MAX_PATH - 1;
		if (RegQueryValueEx(hUsersKey, OS_STRING("defaultServer"), 0, &ulType, (LPBYTE)pValue, &ulSize) == ERROR_SUCCESS)
		{
		    SafeSprintf(regKey, _MAX_PATH, "Software\\Netscape\\Netscape Navigator\\biff\\users\\%s\\servers\\%s",
			    pUser, pValue);

		    if (RegOpenKeyEx(HKEY_CURRENT_USER, OS_STRING(regKey), 0, KEY_READ, &hServersKey) == ERROR_SUCCESS)
		    {
			pPath = new char[_MAX_PATH];
			memset(pPath, 0, _MAX_PATH);

			ulSize = _MAX_PATH - 1;
			if (RegQueryValueEx(hServersKey, OS_STRING("popstatePath"), 0, &ulType, (LPBYTE)pPath, &ulSize) == ERROR_SUCCESS)
			{
			    // pPath ="..\mail\popstate.dat"
			    // the actual location of cookies is 1 level up
			    if (pCursor = ::HXReverseFindChar(pPath, '\\'))
			    {
				*pCursor = '\0';
			    
				if (pCursor = ::HXReverseFindChar(pPath, '\\'))
				{
				    *pCursor = '\0';

				    m_pNSCookiesPath = new char[strlen(pPath) + 13];
				    sprintf(m_pNSCookiesPath, "%s\\%s", pPath, RM_COOKIE_FILE_NAME); /* Flawfinder: ignore */
				    goto cleanup;
				}
			    }
			}
		    }
		}
	    }
	}
    }

    // Netscape Browser 3.0+
    if (RegOpenKeyEx(HKEY_CURRENT_USER, OS_STRING("Software\\Netscape\\Netscape Navigator\\Cookies"), 0,
	KEY_READ, &hCookiesKey) == ERROR_SUCCESS) 
    {
	HX_VECTOR_DELETE(pPath);
	pPath = new char[_MAX_PATH];
	memset(pPath, 0, _MAX_PATH);

	ulSize = _MAX_PATH - 1;
	if (RegQueryValueEx(hCookiesKey, OS_STRING("Cookie File"), 0, &ulType, (LPBYTE)pPath, &ulSize) == ERROR_SUCCESS)
	{
	    m_pNSCookiesPath = new char[strlen(pPath) + 1];
	    strcpy(m_pNSCookiesPath, pPath); /* Flawfinder: ignore */
	}
    }

cleanup:

    HX_RELEASE(pBuffer);

    if (hMainAppKey)
    {
	RegCloseKey(hMainAppKey);
    }

    if (hBiffKey)
    {
	RegCloseKey(hBiffKey);
    }

    if (hUsersKey)
    {
	RegCloseKey(hUsersKey);
    }

    if (hServersKey)
    {
	RegCloseKey(hServersKey);
    }

    if (hCookiesKey)
    {
	RegCloseKey(hCookiesKey);
    }

    HX_VECTOR_DELETE(pUser);
    HX_VECTOR_DELETE(pPath);
    HX_VECTOR_DELETE(pValue);    
#endif /* _WINDOWS */

    return hr;
}

void
HXCookies::ResetCookies(CHXSimpleList* pCookieList)
{
    CookieStruct*   pCookie = NULL;

    while (pCookieList && pCookieList->GetCount() > 0)
    {
	pCookie = (CookieStruct*) pCookieList->RemoveHead();
	HX_DELETE(pCookie);
    }
}

HX_RESULT	
HXCookies::FileReadLine(FILE* fp, char* pLine, UINT32 ulLineBuf, UINT32* pBytesRead)
{
#ifdef _OPENWAVE
    return HXR_NOTIMPL;
#else
    HX_RESULT	hr = HXR_OK;
    UINT32	i = 0;
    UINT32	ulBytesRead = 0;
    char*	pTmpBuf = NULL;

    if (!fp)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    if (ulLineBuf < 1) 
    {
        *pBytesRead = 0;
        goto cleanup;
    }

    ulBytesRead = fread(pLine, sizeof(char), ulLineBuf, fp);
    pTmpBuf = pLine;

    if (ulBytesRead)
    {
	while (i < ulBytesRead) 
	{
#ifdef _MACINTOSH
	    if (pTmpBuf[i] == 10 || pTmpBuf[i] == 13)
#else
	    if (pTmpBuf[i] == 10)
#endif
	    {   // LF
		if (pTmpBuf[i+1])
		{
		    pTmpBuf[i+1] = '\0';
		}

		// Back the file pointer up.
		fseek(fp, (long)((i + 1) - ulBytesRead), SEEK_CUR);
		*pBytesRead = i + 1;
		break;
	    }
	    i++;
	}
    }
    else
    {
	hr = HXR_FAILED;
    }
    
cleanup:

    return hr;
#endif /* _OPENWAVE */
}

CookieStruct*
HXCookies::CheckForPrevCookie(char * path,
			       char * hostname,
			       char * name)
{
    int		    nResult = 0;
    const char*	    pValue = NULL;
    BOOL	    bFound = FALSE;
    CookieStruct*   pCookie = NULL;
    CHXSimpleList::Iterator  i;

    if (!m_pRMCookies)
    {	
	goto cleanup;
    }

    for (i = m_pRMCookies->Begin(); i != m_pRMCookies->End(); ++i)
    {
	pCookie = (CookieStruct*) (*i);
	
        if(path && hostname			&& 
	   pCookie->pPath			&& 
	   pCookie->pHost			&& 
	   pCookie->pCookieName			&&
	   !pCookie->pCookieName->Compare(name)	&&
	   !pCookie->pPath->Compare(path)	&&	    
	   !pCookie->pHost->CompareNoCase(hostname))
	{
            bFound = TRUE;
	    break;
	}
    }

cleanup:    
    return bFound?pCookie:NULL;
}

HX_RESULT
HXCookies::OpenCookies(char* pCookieFile, BOOL bRMCookies, CHXSimpleList*& pCookiesList)
{
#ifdef _OPENWAVE
    return HXR_NOTIMPL;
#else
    HX_RESULT	    hr = HXR_OK;
    char*	    pHost = NULL;
    char*	    pPath = NULL;
    char*	    pCookieName = NULL; 
    char*	    pCookieValue = NULL;
    char*	    pIsDomain = NULL;
    char*	    pXXX = NULL;
    char*	    pExpires = NULL;
    char*	    pBuffer = new char[LINE_BUFFER_SIZE]; /* Flawfinder: ignore */
    UINT32	    ulBytesRead = 0;
    FILE*	    fp = NULL;
    CookieStruct*   pNewCookie = NULL;
 
    pCookiesList = NULL;

    if (!pBuffer)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    pBuffer[0] = '\0';

    if (!pCookieFile)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

#ifdef _WINDOWS
    if (bRMCookies)
    {
	if (!m_pLock)
	{
#ifdef THREADS_SUPPORTED            
	    HXEvent::MakeEvent(m_pLock, "CookieFileLock", FALSE);
#else            
	    HXEvent::MakeStubEvent(m_pLock, "CookieFileLock", FALSE);
#endif            
	}
	else
	{
	    m_pLock->Wait();
	}
    }
#endif /* _WINDOWS */

    if (bRMCookies)
    {
	if(CheckCookies() == HXR_FAIL)
	{
     	    hr = HXR_FAILED;
	    goto cleanup;
	}
    }

    if (!(fp = fopen(pCookieFile, "r+b")))
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

#if defined (_UNIX) && !defined(_SUN) && !defined(_HPUX) && !defined(_IRIX) && !defined(_AIX) && !defined(_OSF1)
    if (bRMCookies)
    {
	m_fileID = fileno(fp);    
	flock(m_fileID, LOCK_EX);
    }
#endif /* _UNIX */

    /* format is:
     *
     * host \t is_domain \t path \t xxx \t expires \t name \t cookie
     *
     * if this format isn't respected we move onto the next line in the file.
     *
     * is_domain is TRUE or FALSE   -- defaulting to FALSE
     * xxx is TRUE or FALSE	    -- should default to TRUE
     * expires is a time_t integer
     * cookie can have tabs
     */
    while(HXR_OK == FileReadLine(fp, &pBuffer[0], LINE_BUFFER_SIZE, &ulBytesRead))
    {
	if (*pBuffer == '#' || *pBuffer == CR || *pBuffer == LF || *pBuffer == 0)
	{
	    continue;
	}

	pHost = pBuffer;
	    
	if(!(pIsDomain = strchr(pHost, '\t')))
	{
	    continue;
	}
	*pIsDomain++ = '\0';
	if(*pIsDomain == CR || *pIsDomain == LF || *pIsDomain == 0)
	{
	    continue;
	}
	
	if(!(pPath = strchr(pIsDomain, '\t')))
	{
	    continue;
	}
	*pPath++ = '\0';
	if(*pPath == CR || *pPath == LF || *pPath == 0)
	{
	    continue;
	}

	if(!(pXXX = strchr(pPath, '\t')))
	{
	    continue;
	}
	*pXXX++ = '\0';
	if(*pXXX == CR || *pXXX == LF || *pXXX == 0)
	{
	    continue;
	}

	if(!(pExpires = strchr(pXXX, '\t')))
	{
	    continue;
	}
	*pExpires++ = '\0';
	if(*pExpires == CR || *pExpires == LF || *pExpires == 0)
	{
	    continue;
	}

	if(!(pCookieName = strchr(pExpires, '\t')))
	{
	    continue;
	}
	*pCookieName++ = '\0';
	if(*pCookieName == CR || *pCookieName == LF || *pCookieName == 0)
	{
	    continue;
	}

	if(!(pCookieValue = strchr(pCookieName, '\t')))
	{
	    continue;
	}
	*pCookieValue++ = '\0';
	if(*pCookieValue == CR || *pCookieValue == LF || *pCookieValue == 0)
	{
	    continue;
	}

	// remove the '\n' from the end of the cookie
	pCookieValue = ::StripLine(pCookieValue);

	// construct a new cookie_struct
	pNewCookie = new CookieStruct;
	if(!pNewCookie)
	{
	    hr = HXR_OUTOFMEMORY;
	    goto cleanup;
	}
	
	memset(pNewCookie, 0, sizeof(CookieStruct));

	/* copy
	 */
	pNewCookie->pCookieValue = new CHXString(pCookieValue);
	pNewCookie->pCookieName = new CHXString(pCookieName);
	pNewCookie->pPath = new CHXString(pPath);
	pNewCookie->pHost = new CHXString(pHost);

#ifdef _MACINTOSH
	pNewCookie->expires = (time_t)atoi64(pExpires);
#else
	pNewCookie->expires = atol(pExpires);
#endif

	if(!strcasecmp(pIsDomain, "TRUE"))
	{
	    pNewCookie->bIsDomain = TRUE;
	}
	else
	{
	    pNewCookie->bIsDomain = FALSE;
	}
	
	pNewCookie->bMemoryOnly = FALSE;

	if (!pCookiesList)
	{
	    pCookiesList = new CHXSimpleList();
	}

	hr = AddCookie(pNewCookie, pCookiesList);
    }

    UpdateModificationTime();

cleanup:

#if defined (_UNIX) && !defined(_SUN) && !defined(_HPUX) && !defined(_IRIX) && !defined(_AIX) && !defined(_OSF1)
    if (bRMCookies)
    {
	flock(m_fileID, LOCK_UN);
    }
#endif /* _UNIX */

    if (fp)
    {
	fclose(fp);
    }

#ifdef _WINDOWS
    if (bRMCookies && m_pLock)
    {
	m_pLock->SignalEvent();
    }
#endif /* _WINDOWS */

    delete [] pBuffer;
    
    return hr;
#endif /* _OPENWAVE */
}

HX_RESULT	
HXCookies::SaveCookies(void)
{
#ifdef _OPENWAVE
    return HXR_NOTIMPL;
#else
    HX_RESULT	    hr = HXR_OK;
    FILE*	    fp = NULL;
    INT32	    len = 0;
    char	    date_string[36] = {0}; /* Flawfinder: ignore */
    time_t	    cur_date = time(NULL);
    CookieStruct*   pTempCookie = NULL;
    CHXSimpleList::Iterator  i;

    if (!m_pRMCookies || !m_pRMCookiesPath)
    {
	goto cleanup;
    }

#ifdef _WINDOWS
    if (!m_pLock)
    {
#ifdef THREADS_SUPPORTED        
	HXEvent::MakeEvent(m_pLock, "CookieFileLock", FALSE);
#else        
	HXEvent::MakeStubEvent(m_pLock, "CookieFileLock", FALSE);
#endif
    }
    else
    {
	m_pLock->Wait();
    }
#endif /* _WINDOWS */

    if (!(fp = fopen(m_pRMCookiesPath, "w")))
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

#ifdef _UNIX
    //Make the permisions on the cookies file User read/write only.
    if( chmod( m_pRMCookiesPath, S_IRUSR | S_IWUSR ) != 0 )
    {
        HX_ASSERT( "Can't change permision on cookies file." == NULL );
    }
    
#endif    

#if defined (_UNIX) && !defined(_SUN) && !defined(_HPUX) && !defined(_IRIX) && !defined(_AIX) && !defined(_OSF1)
    m_fileID = fileno(fp);    
    flock(m_fileID, LOCK_EX);
#endif /* _UNIX */

    fwrite(RM_COOKIE_CAPTION, sizeof(char), strlen(RM_COOKIE_CAPTION), fp);

    for (i = m_pRMCookies->Begin(); i != m_pRMCookies->End(); ++i)
    {
	pTempCookie = (CookieStruct*) (*i);

	/* format shall be:
 	 *
	 * host \t is_domain \t path \t secure \t expires \t name \t cookie
	 *
	 * is_domain is TRUE or FALSE
	 * secure is TRUE or FALSE  
	 * expires is a time_t integer
	 * cookie can have tabs
	 */
	if(pTempCookie->expires < cur_date)
	{
	    continue;  /* don't write entry if cookie has expired 
			* or has no expiration date
			*/
	}
		
	len = fwrite((const char*)*(pTempCookie->pHost), sizeof(char), pTempCookie->pHost->GetLength(), fp);
	if (len < 0)
	{
	    hr = HXR_FAILED;
	    goto cleanup;
	}
	fwrite("\t", sizeof(char), 1, fp);

	if(pTempCookie->bIsDomain)
	{
	    fwrite("TRUE", sizeof(char), 4, fp);
	}
	else
	{
	    fwrite("FALSE", sizeof(char), 5, fp);
	}	    
	fwrite("\t", sizeof(char), 1, fp);

	fwrite((const char*)*(pTempCookie->pPath), sizeof(char), pTempCookie->pPath->GetLength(), fp);
	fwrite("\t", sizeof(char), 1, fp);

	fwrite("FALSE", sizeof(char), 5, fp);
	fwrite("\t", sizeof(char), 1, fp);

	sprintf(date_string, "%lu", pTempCookie->expires); /* Flawfinder: ignore */
	fwrite(date_string, sizeof(char), strlen(date_string), fp);
	fwrite("\t", sizeof(char), 1, fp);

	fwrite((const char*)*(pTempCookie->pCookieName), sizeof(char), 
		pTempCookie->pCookieName->GetLength(), fp);
	fwrite("\t", sizeof(char), 1, fp);

	fwrite((const char*)*(pTempCookie->pCookieValue), sizeof(char), 
		pTempCookie->pCookieValue->GetLength(), fp);
	fwrite(LINEBREAK, sizeof(char), LINEBREAK_LEN, fp);

	//once saved to disk, make sure the memory only flag is false
	pTempCookie->bMemoryOnly = FALSE;
    }

    UpdateModificationTime();    
    m_bSaveCookies = FALSE;

cleanup:

#if defined (_UNIX) && !defined(_SUN) && !defined(_HPUX) && !defined(_IRIX) && !defined(_AIX) && !defined(_OSF1)
    flock(m_fileID, LOCK_UN);
#endif /* _UNIX */

    if (fp)
    {
	fclose(fp);
    }

#ifdef _WINDOWS
    if (m_pLock)
    {
	m_pLock->SignalEvent();
    }
#endif /* _WINDOWS */

    SecureCookies();

    return(hr);
#endif /* _OPENWAVE */
}

HX_RESULT
HXCookies::AddCookie(CookieStruct* pCookie, CHXSimpleList*& pCookiesList)
{
    HX_RESULT	    hr = HXR_OK;
    BOOL	    bShorterPathFound = FALSE;
    CookieStruct*   pTempCookie = NULL;
    LISTPOSITION    position;

    if (!pCookie || !pCookiesList)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    /* add it to the list so that it is before any strings of
     * smaller length
     */
    position = pCookiesList->GetHeadPosition();
    while (position != NULL)
    {
	pTempCookie = (CookieStruct*) pCookiesList->GetNext(position);

	if (strlen(*pCookie->pPath) > strlen(*pTempCookie->pPath))
	{
	    // Remember that we found a shorter path
	    bShorterPathFound = TRUE;

	    // If the position is null, then event was the first
	    // item in the list, and we need to do some fancy footwork...
	    if (!position)
	    {
		POSITION theTail = pCookiesList->GetTailPosition();
		pCookiesList->InsertBefore(theTail,(void*) pCookie);
	    }
	    // otherwise, roll after one...
	    else
	    {
		pCookiesList->GetPrev(position);

		if (!position)
		{
		    pCookiesList->AddHead((void*) pCookie);
		}
		else
		{
		    pCookiesList->InsertBefore(position,(void*) pCookie);
		}
	    }

	    break;
	}
    }

    // If we didn't find an earlier packet, then we should insert at
    // the head of the list...
    if (!bShorterPathFound)
    {
	pCookiesList->AddTail((void*) pCookie);
    }

cleanup:

    return hr;
}

BOOL
HXCookies::WasCookieAdded(CHXSimpleList* pCookiesList, CookieStruct* pCookie)
{
    BOOL bResult = FALSE;
    CookieStruct*  pTempCookie = NULL;
    CHXSimpleList::Iterator  i;

    if (!pCookiesList || !pCookie)
    {
	goto cleanup;
    }

    for (i = pCookiesList->Begin(); i != pCookiesList->End(); ++i)
    {
	pTempCookie = (CookieStruct*) (*i);
	
	if (pTempCookie->pCookieName && pCookie->pCookieName 
		&& *(pTempCookie->pCookieName) == *(pCookie->pCookieName) 
		&& pTempCookie->pHost && pCookie->pHost)
	{
	    if(DoesDomainMatch(*pTempCookie->pHost, *pCookie->pHost))
	    {
		bResult = TRUE;
		break;
	    }
	}
    }

cleanup:

    return bResult;
}


BOOL HXCookies::DoesDomainMatch(const char* szDomain, const char* szDomainToParse)
{
    BOOL bMatches = FALSE;
    CHXString cHostCopy;
    CHXString cDomainCopy;
    CHXString cHostRight;

    if(!szDomain || !szDomainToParse || !strlen(szDomain) || !strlen(szDomainToParse))
    {
	goto cleanup;
    }

    cHostCopy = szDomainToParse;
    cDomainCopy = szDomain;
    cDomainCopy.MakeLower();

    // Now we compare the domain (from the cookie itself) with
    // the rightmost characters of the host. For instance,
    // a domain of ".bar.com" would match with a host (passed in)
    // of "foo.bar.com", "www.bar.com", etc. but would NOT match
    // a host of "bar.com".
    cHostRight = cHostCopy.Right(cDomainCopy.GetLength());
    if (cHostRight != cDomainCopy)
    {
        // no match
        goto cleanup;
    }
    else if (cDomainCopy.GetAt(0) != '.' && cHostCopy.GetLength() > cDomainCopy.GetLength() &&
                 cHostCopy.GetAt(cHostCopy.GetLength() - cDomainCopy.GetLength() - 1) != '.')
    {
       // no match
        goto cleanup;
    }
    bMatches = TRUE;

cleanup:
    return bMatches;
}


BOOL
HXCookies::IsCookieEnabled()
{
    BOOL	bResult = TRUE;
    IHXBuffer*	pBuffer = NULL;

    if (!m_pPreferences)
    {
	if (HXR_OK != m_pContext->QueryInterface(IID_IHXPreferences, (void**)&m_pPreferences))
	{
	    m_pPreferences = NULL;
	}
    }

    if (m_pPreferences &&
	m_pPreferences->ReadPref("CookiesEnabled", pBuffer) == HXR_OK)
    {
	if (strcmp((const char*)pBuffer->GetBuffer(), "0") == 0)
	{
	    bResult = FALSE;
	}

	HX_RELEASE(pBuffer);
    } 
    return bResult;
}

void	    
HXCookies::UpdateModificationTime(void)
{
#ifdef _OPENWAVE
    // XXXSAB implement this!!!
#else
    struct stat status;

    if (!m_pRMCookiesPath)
    {
	goto cleanup;
    }

#ifndef _VXWORKS
    if (0 == stat(m_pRMCookiesPath, &status))
    {
	m_lastModification = status.st_mtime;
    }
#endif /* _VXWORKS */

cleanup:

    return;
#endif
}

BOOL	    
HXCookies::IsCookieFileModified(void)
{
#ifdef _OPENWAVE
    // XXXSAB implement this!!!
    return FALSE;
#else
    BOOL	bResult = FALSE;
    struct stat	status;

    if (!m_pRMCookiesPath)
    {
	goto cleanup;
    }

#ifndef _VXWORKS
    if (0 == stat(m_pRMCookiesPath, &status) &&
	status.st_mtime != m_lastModification)
    {
	bResult = TRUE;
    }
#endif /* _VXWORKS */

cleanup:

    return bResult;
#endif
}

HX_RESULT
HXCookies::MergeCookieList(CHXSimpleList* pFromList, CHXSimpleList* pToList)
{
    HX_RESULT	    hr = HXR_OK;
    CookieStruct*   pCookie = NULL;
    CHXSimpleList::Iterator  i;

    if (!pFromList)
    {
	goto cleanup;
    }

    for (i = pFromList->Begin(); HXR_OK == hr && i != pFromList->End(); ++i)
    {
	pCookie = (CookieStruct*) (*i);
	
	if (pCookie->bMemoryOnly && !WasCookieAdded(pToList, pCookie))
	{
	    CookieStruct* pNewCookie = new CookieStruct;
	    pNewCookie->pPath = new CHXString(*pCookie->pPath);
	    pNewCookie->pHost = new CHXString(*pCookie->pHost);
	    pNewCookie->pCookieName = new CHXString(*pCookie->pCookieName);
	    pNewCookie->pCookieValue = new CHXString(*pCookie->pCookieValue);
	    pNewCookie->expires = pCookie->expires;
	    pNewCookie->bIsDomain = pCookie->bIsDomain;
	    pNewCookie->bMemoryOnly = pCookie->bMemoryOnly;
	    hr = AddCookie(pNewCookie, pToList);
	}
    }

cleanup:

    return hr;
}

HX_RESULT	
HXCookies::SyncRMCookies(BOOL bSave)
{
    HX_RESULT	    hr = HXR_OK;
    CHXSimpleList*  pNewRMCookies = NULL;
    CHXSimpleList::Iterator i;
    CookieStruct* pCookie = NULL;
    BOOL bCookieFileModified = FALSE;

    if (!m_bInitialized)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    bCookieFileModified = IsCookieFileModified();
    if (bCookieFileModified)
    {
	if (HXR_OK == OpenCookies(m_pRMCookiesPath, TRUE, pNewRMCookies))
	{
	    if (m_bSaveCookies)
	    {
		MergeCookieList(m_pRMCookies, pNewRMCookies);
	    }		
	     
	    ResetCookies(m_pRMCookies);
	    HX_DELETE(m_pRMCookies);

	    m_pRMCookies = pNewRMCookies;
	}	    	    
    }

    if (bSave && m_bSaveCookies)
    {
	if(m_pRMCookies && bCookieFileModified && pNewRMCookies == NULL)
	{
	    //this means there are no more cookies on disk, and we need to look at what's in memory, and keep
	    //only cookies that have bMemoryOnly set
	    pNewRMCookies = new CHXSimpleList;

	    while (m_pRMCookies && m_pRMCookies->GetCount() > 0 && pNewRMCookies)
	    {
	    	pCookie = (CookieStruct*) m_pRMCookies->RemoveHead();
		if(pCookie->bMemoryOnly)
		{
		    CookieStruct* pNewCookie = new CookieStruct;
		    pNewCookie->pPath = new CHXString(*pCookie->pPath);
		    pNewCookie->pHost = new CHXString(*pCookie->pHost);
		    pNewCookie->pCookieName = new CHXString(*pCookie->pCookieName);
		    pNewCookie->pCookieValue = new CHXString(*pCookie->pCookieValue);
		    pNewCookie->expires = pCookie->expires;
		    pNewCookie->bIsDomain = pCookie->bIsDomain;
		    pNewCookie->bMemoryOnly = pCookie->bMemoryOnly;
		    AddCookie(pNewCookie, pNewRMCookies);
		}
	    	HX_DELETE(pCookie);
	    }
	    HX_DELETE(m_pRMCookies);
	    m_pRMCookies = pNewRMCookies;
	}
	hr = SaveCookies();
    }

cleanup:

    return hr;
}

#ifdef _TEST
void
HXCookies::DumpCookies(void)
{
    CHXSimpleList* pCookies = NULL;
    CookieStruct*  pCookie = NULL;
    CHXSimpleList::Iterator  i;

    for (int l = 0; l < 2; l++)
    {
	switch (l)
	{
	case 0:
	    pCookies = m_pNSCookies;
	    break;
	case 1:
	    pCookies = m_pRMCookies;
	    break;
	default:
	    break;
	}

	if (pCookies)
	{
	    printf("Total cookies: %lu\n", pCookies->GetCount());
    
	    for (i = pCookies->Begin(); i != pCookies->End(); ++i)
	    {
		pCookie = (CookieStruct*) (*i);
		
		printf("%s\t%s\t%s\n",
		       (const char*)*(pCookie->pHost),
		       (const char*)*(pCookie->pCookieName),
		       (const char*)*(pCookie->pCookieValue));
	    }
	}
    }

    return;
}
#endif // _TEST




//IRPCookies
STDMETHODIMP HXCookies::GetExpiredCookies(const char* pHost,
    				    const char*	       pPath,
    				    REF(IHXBuffer*)   pCookies)
{
    HX_RESULT	    hr = HXR_OK;
    char*	    cp = NULL;
    char*	    pComma = NULL;
    char*	    pEqualSign = NULL;
    char*	    pData = NULL;
    char*	    pURL = NULL;
    int		    l = 0;
    int		    host_length = 0;
    int		    path_length = 0;
    int		    domain_length = 0;
    BOOL	    bAdded = FALSE;
    UINT32	    dwSize = 0;    
    time_t	    cur_time = time(NULL);
    CookieStruct*   pTempCookie = NULL;
    CookieStruct*   pNewCookie = NULL;
    CHXSimpleList*  pCookiesFound1 = NULL;
    //CHXSimpleList*  pCookiesFound2 = NULL;
    CHXSimpleList*  pCookiesList = NULL;
    IHXValues*	    pValues = NULL;
    CHXSimpleList::Iterator  i;
    CHXString       cHostCopy;
    INT32           lColon;
    
    pCookies = NULL;

    if (!IsCookieEnabled())
    {
	goto cleanup;
    }

    if (!m_bInitialized)
    {
	hr = Initialize();

	if (HXR_OK != hr)
	{
	    goto cleanup;
	}
    }
    else
    {
	SyncRMCookies(FALSE);
    }

    if (!pHost || !m_pCookiesHelper)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    // return string to build    
    if (!(pValues = new CHXHeader()))
    {
	hr = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    pValues->AddRef();

    cHostCopy = pHost;
    lColon    = cHostCopy.Find(':');
    if (lColon >= 0)
    {
	cHostCopy = cHostCopy.Left(lColon);
    }
    cHostCopy.MakeLower();

    // search for all cookies(Netscape only for now)
    for (l = 0; l < 2; l++)
    {
	switch (l)
	{
	case 0:
	    pCookiesList = m_pRMCookies;
	    break;
	case 1:
	    pCookiesList = m_pNSCookies;
	    break;
	default:
	    break;
	}

	if (!pCookiesList)
	{
	    continue;
	}

	for (i = pCookiesList->Begin(); i != pCookiesList->End(); ++i)
	{
	    pTempCookie = (CookieStruct*) (*i);
    
	    if (!pTempCookie->pHost)
	    {
		continue;
	    }

	    // check the host or domain first
	    if(pTempCookie->bIsDomain)
	    {
		domain_length = pTempCookie->pHost->GetLength();

		CHXString cDomainCopy(*(pTempCookie->pHost));
		cDomainCopy.MakeLower();

		// Now we compare the domain (from the cookie itself) with
		// the rightmost characters of the host. For instance,
		// a domain of ".bar.com" would match with a host (passed in)
		// of "foo.bar.com", "www.bar.com", etc. but would NOT match
		// a host of "bar.com".
		CHXString cHostRight = cHostCopy.Right(cDomainCopy.GetLength());
		if (cHostRight != cDomainCopy)
		{
		    // no match
		    continue;
		}
	    }
	    else if(strcasecmp((const char*)*(pTempCookie->pHost), pHost))
	    {
		// hostname matchup failed.
		continue;
	    }

	    // shorter strings always come last so there can be no
	    // ambiquity						     	      
	    if(pTempCookie->pPath && 
	       !strncmp(pPath, (const char*)*(pTempCookie->pPath), pTempCookie->pPath->GetLength()))
	    {
		// check for expired cookies
		if(pTempCookie->expires && (pTempCookie->expires < cur_time))
		{
		    if (!pCookiesFound1)
		    {
		        pCookiesFound1 = new CHXSimpleList();
		    }
		    pCookiesFound1->AddTail(pTempCookie);
		}
	    }
	}
    }

#ifdef _WINDOWS
    if (!_pInternetGetCookie || m_bMemoryOnly)
    {
	goto cleanup;
    }

    host_length = strlen(pHost);

    if (pPath)
    {
	path_length = strlen(pPath);
    }

    pURL = new char[host_length + path_length + 8];
    sprintf(pURL, "http://%s%s", pHost, pPath); /* Flawfinder: ignore */

    if (_pInternetGetCookie(pURL, NULL, pData, &dwSize) && !pData && dwSize)
    {
	pData = new char[dwSize+1];

	if (!_pInternetGetCookie(pURL, NULL, pData, &dwSize))
	{
	    goto cleanup;
	}

	cp = pData;

	while (pComma = (char*) ::HXFindChar(cp, ';'))
	{
	    *pComma = '\0';
	    pComma++;

	    if (pEqualSign = (char*) ::HXFindChar(cp, '='))
	    {
		*pEqualSign = '\0';
		pEqualSign++;

		pNewCookie = new CookieStruct;
		bAdded = FALSE;

		// copy
		pNewCookie->pCookieValue = new CHXString(pEqualSign);
		pNewCookie->pCookieName = new CHXString(cp);
		pNewCookie->pPath = NULL;
		pNewCookie->pHost = NULL;
		pNewCookie->expires = 0;
		pNewCookie->bIsDomain = FALSE;
		pNewCookie->bMemoryOnly = FALSE;

		if (!WasCookieAdded(pCookiesFound1, pNewCookie))
		{
		    //if (!pCookiesFound2)
		    //{
		    //	pCookiesFound2 = new CHXSimpleList();
		    //	pCookiesFound2->AddTail(pNewCookie);
		    //	bAdded = TRUE;
		    //}
		    //else if (!WasCookieAdded(pCookiesFound2, pNewCookie))
		    //{
		    //	pCookiesFound2->AddTail(pNewCookie);
		    //	bAdded = TRUE;
		    //}
		}
		
		if (!bAdded)
		{
		    HX_DELETE(pNewCookie);
		}
	    }

	    cp = pComma;
	}

	if (pEqualSign = (char*) ::HXFindChar(cp, '='))
	{
	    *pEqualSign = '\0';
	    pEqualSign++;

	    pNewCookie = new CookieStruct;
	    bAdded = FALSE;

	    // copy
	    pNewCookie->pCookieValue = new CHXString(pEqualSign);
	    pNewCookie->pCookieName = new CHXString(cp);
	    pNewCookie->pPath = NULL;
	    pNewCookie->pHost = NULL;
	    pNewCookie->expires = 0;
	    pNewCookie->bIsDomain = FALSE;
	    pNewCookie->bMemoryOnly = FALSE;

	    if (!WasCookieAdded(pCookiesFound1, pNewCookie))
	    {
		//if (!pCookiesFound2)
		//{
		//    pCookiesFound2 = new CHXSimpleList();
		//    pCookiesFound2->AddTail(pNewCookie);
		//    bAdded = TRUE;
		//}
		//else if (!WasCookieAdded(pCookiesFound2, pNewCookie))
		//{
		//    pCookiesFound2->AddTail(pNewCookie);
		//    bAdded = TRUE;
		//}
	    }

	    if (!bAdded)
	    {
		HX_DELETE(pNewCookie);
	    }
	}
    }
#endif /* _WINDOWS */

cleanup:

    if (pCookiesFound1)
    {
	for (i = pCookiesFound1->Begin(); i != pCookiesFound1->End(); ++i)
	{
	    pTempCookie = (CookieStruct*) (*i);

	    if(pTempCookie->pCookieName && pTempCookie->pCookieValue)
	    {
		::SaveStringToHeader(pValues,
				     (const char*)*(pTempCookie->pCookieName),
				     (char*)(const char*)*(pTempCookie->pCookieValue));
	    }
	}
    }

    //if (pCookiesFound2)
    //{
    //	for (i = pCookiesFound2->Begin(); i != pCookiesFound2->End(); ++i)
    //	{
    //	    pTempCookie = (CookieStruct*) (*i);
    //
    //	    if(pTempCookie->pCookieName && pTempCookie->pCookieValue)
    //	    {
    //		::SaveStringToHeader(pValues,
    //				     (const char*)*(pTempCookie->pCookieName),
    //				     (char*)(const char*)*(pTempCookie->pCookieValue));
    //	    }
    //
    //	    HX_DELETE(pTempCookie);
    //	}
    //}

    if (m_pCookiesHelper)
    {
	hr = m_pCookiesHelper->UnPack(pValues, pCookies);
    }

    HX_DELETE(pCookiesFound1);
    //HX_DELETE(pCookiesFound2);
    HX_VECTOR_DELETE(pData);
    HX_VECTOR_DELETE(pURL);

    HX_RELEASE(pValues);
   
    return hr;
}

HX_RESULT HXCookies::SecureCookies()
{
    return HXR_OK;
}

HX_RESULT HXCookies::CheckCookies()
{
    return HXR_OK;
}
