/******************************************************************************

Copyright (c) 2000 Microsoft Corporation

Module Name:
    HyperLinks.cpp

Abstract:
    This file contains the implementation of the HyperLinks library.

Revision History:
    Davide Massarenti   (Dmassare)  11/28/2000
        created

******************************************************************************/

#include "stdafx.h"

////////////////////////////////////////////////////////////////////////////////

static const DATE l_TIME_hour    = 1.0 / 24.0;
static const DATE l_TIME_minute  = l_TIME_hour / 60.0;
static const DATE l_TIME_second  = l_TIME_hour / 60.0;
static const DATE l_TIME_timeout = l_TIME_minute * 5.0;

static const WCHAR l_szMS_ITS     [] = L"ms-its:";
static const WCHAR l_szMSITSTORE  [] = L"mk:@MSITStore:";
static const WCHAR l_szITS        [] = L"its:";

static const WCHAR l_szHOMEPAGE   [] = L"hcp://services/centers/homepage";
static const WCHAR l_szSUPPORT    [] = L"hcp://services/centers/support";
static const WCHAR l_szOPTIONS    [] = L"hcp://services/centers/options";
static const WCHAR l_szUPDATE     [] = L"hcp://services/centers/update";
static const WCHAR l_szCOMPAT     [] = L"hcp://services/centers/compat";
static const WCHAR l_szTOOLS      [] = L"hcp://services/centers/tools";
static const WCHAR l_szERRMSG     [] = L"hcp://services/centers/errmsg";

static const WCHAR l_szSEARCH     [] = L"hcp://services/search";
static const WCHAR l_szINDEX      [] = L"hcp://services/index";
static const WCHAR l_szSUBSITE    [] = L"hcp://services/subsite";

static const WCHAR l_szFULLWINDOW [] = L"hcp://services/layout/fullwindow";
static const WCHAR l_szCONTENTONLY[] = L"hcp://services/layout/contentonly";
static const WCHAR l_szKIOSK      [] = L"hcp://services/layout/kiosk";
static const WCHAR l_szXML        [] = L"hcp://services/layout/xml";

static const WCHAR l_szREDIRECT   [] = L"hcp://services/redirect";

static const WCHAR l_szHCP        [] = L"hcp://";
static const WCHAR l_szHCP_redir  [] = L"hcp:";

static const WCHAR l_szAPPLICATION[] = L"app:";

static const WCHAR l_szRESOURCE   [] = L"res://";

////////////////////

typedef enum
{
    QFT_TEXT       ,
    QFT_URL        ,
    QFT_TAXONOMY   ,
    QFT_APPLICATION,
} QueryFieldType;

struct QueryField
{
    LPCWSTR        szName;
    QueryFieldType qft;
    bool           fOptional;
};

struct Pattern
{
    LPCWSTR            szTxt;
    size_t             iLen; /* -1 for complete match */
    HyperLinks::Format fmt;

    const QueryField*  rgQueryFields;
    size_t             iQueryFields;
};

////////////////////

static const QueryField l_rgTopic   [] = { { L"topic"      , QFT_URL        , false } };
static const QueryField l_rgTopicOpt[] = { { L"topic"      , QFT_URL        , true  } };

static const QueryField l_rgSEARCH  [] = { { L"query"      , QFT_TEXT       , false } ,
                                           { L"topic"      , QFT_URL        , true  } };

static const QueryField l_rgINDEX   [] = { { L"scope"      , QFT_APPLICATION, true  } ,
                                           { L"select"     , QFT_APPLICATION, true  } ,
                                           { L"topic"      , QFT_URL        , true  } };

static const QueryField l_rgSUBSITE [] = { { L"node"       , QFT_TAXONOMY   , false } ,
                                           { L"select"     , QFT_TAXONOMY   , true  } ,
                                           { L"topic"      , QFT_URL        , true  } };

static const QueryField l_rgXML     [] = { { L"definition" , QFT_URL        , false } ,
                                           { L"topic"      , QFT_URL        , true  } };

static const QueryField l_rgREDIRECT[] = { { L"online"     , QFT_URL        , false } ,
                                           { L"offline"    , QFT_URL        , false } };

static const QueryField l_rgAPP     [] = { { L"topic"      , QFT_URL        , true  } };


static const Pattern l_rgPattern[] =
{
    { l_szMS_ITS     , MAXSTRLEN( l_szMS_ITS      ), HyperLinks::FMT_MSITS                                                      },
    { l_szMSITSTORE  , MAXSTRLEN( l_szMSITSTORE   ), HyperLinks::FMT_MSITS                                                      },
    { l_szITS        , MAXSTRLEN( l_szITS         ), HyperLinks::FMT_MSITS                                                      },

    ////////////////////

    { l_szHOMEPAGE   , -1                          , HyperLinks::FMT_CENTER_HOMEPAGE                                            },
    { l_szSUPPORT    , -1                          , HyperLinks::FMT_CENTER_SUPPORT    , l_rgTopicOpt, ARRAYSIZE(l_rgTopicOpt ) },
    { l_szOPTIONS    , -1                          , HyperLinks::FMT_CENTER_OPTIONS    , l_rgTopicOpt, ARRAYSIZE(l_rgTopicOpt ) },
    { l_szUPDATE     , -1                          , HyperLinks::FMT_CENTER_UPDATE                                              },
    { l_szCOMPAT     , -1                          , HyperLinks::FMT_CENTER_COMPAT                                              },
    { l_szTOOLS      , -1                          , HyperLinks::FMT_CENTER_TOOLS      , l_rgTopicOpt, ARRAYSIZE(l_rgTopicOpt ) },
    { l_szERRMSG     , -1                          , HyperLinks::FMT_CENTER_ERRMSG                                              },

    { l_szSEARCH     , -1                          , HyperLinks::FMT_SEARCH            , l_rgSEARCH  , ARRAYSIZE(l_rgSEARCH   ) },
    { l_szINDEX      , -1                          , HyperLinks::FMT_INDEX             , l_rgINDEX   , ARRAYSIZE(l_rgINDEX    ) },
    { l_szSUBSITE    , -1                          , HyperLinks::FMT_SUBSITE           , l_rgSUBSITE , ARRAYSIZE(l_rgSUBSITE  ) },

    { l_szFULLWINDOW , -1                          , HyperLinks::FMT_LAYOUT_FULLWINDOW , l_rgTopic   , ARRAYSIZE(l_rgTopic    ) },
    { l_szCONTENTONLY, -1                          , HyperLinks::FMT_LAYOUT_CONTENTONLY, l_rgTopic   , ARRAYSIZE(l_rgTopic    ) },
    { l_szKIOSK      , -1                          , HyperLinks::FMT_LAYOUT_KIOSK      , l_rgTopic   , ARRAYSIZE(l_rgTopic    ) },
    { l_szXML        , -1                          , HyperLinks::FMT_LAYOUT_XML        , l_rgXML     , ARRAYSIZE(l_rgXML      ) },

    { l_szREDIRECT   , -1                          , HyperLinks::FMT_REDIRECT          , l_rgREDIRECT, ARRAYSIZE(l_rgREDIRECT ) },

    { l_szHCP        , MAXSTRLEN( l_szHCP         ), HyperLinks::FMT_HCP                                                        },
    { l_szHCP_redir  , MAXSTRLEN( l_szHCP_redir   ), HyperLinks::FMT_HCP_REDIR                                                  },

    ////////////////////

    { l_szAPPLICATION, MAXSTRLEN( l_szAPPLICATION ), HyperLinks::FMT_APPLICATION       , l_rgAPP     , ARRAYSIZE(l_rgAPP      ) },

    ////////////////////

    { l_szRESOURCE   , MAXSTRLEN( l_szRESOURCE    ), HyperLinks::FMT_RESOURCE                                                   },

    ////////////////////

    { NULL                                                                                                                      }
};

HyperLinks::ParsedUrl::ParsedUrl()
{
                                                // MPC::wstring       m_strURL;
    m_fmt          = HyperLinks::FMT_INVALID;   // Format             m_fmt;
    m_state        = HyperLinks::STATE_INVALID; // State              m_state;
    m_dLastChecked = 0;                         // DATE               m_dLastChecked;
    m_fBackground  = true;                      // bool               m_fBackground;
                                                //
                                                // MPC::wstring       m_strBasePart;
                                                // MPC::WStringLookup m_mapQuery;
}

HRESULT HyperLinks::ParsedUrl::Initialize( /*[in]*/ LPCWSTR szURL )
{
    __HCP_FUNC_ENTRY( "HyperLinks::ParsedUrl::Initialize" );

    HRESULT hr;

    SANITIZEWSTR(szURL);


    m_strURL  = szURL;
    m_fmt     = HyperLinks::FMT_INVALID;
    m_state   = HyperLinks::STATE_NOTPROCESSED;


    MPC::HTML::ParseHREF( m_strURL.c_str(), m_strBasePart, m_mapQuery );


    if(m_strBasePart.size() == 0)
    {
        m_state = HyperLinks::STATE_MALFORMED;
    }
    else if(MPC::MSITS::IsCHM( m_strBasePart.c_str() ))
    {
        m_fmt = HyperLinks::FMT_MSITS;
    }
    else
    {
        CComBSTR bstrURL( m_strBasePart.c_str() ); if(bstrURL) ::CharLowerW( bstrURL );

        szURL = bstrURL;
        for(const Pattern* ptr=l_rgPattern; ptr->szTxt; ptr++)
        {
            int iCmp;

            if(ptr->iLen) iCmp = wcsncmp( szURL, ptr->szTxt, ptr->iLen );
            else          iCmp = wcscmp ( szURL, ptr->szTxt            );

            if(iCmp == 0)
            {
                const QueryField* field = ptr->rgQueryFields;

                for(size_t i=0; i<ptr->iQueryFields; i++, field++)
                {
                    CComBSTR bstrValue;

                    if(GetQueryField( field->szName, bstrValue ) == false)
                    {
                        if(field->fOptional == false)
                        {
                            m_state = HyperLinks::STATE_MALFORMED;
                            break;
                        }
                    }
                    else
                    {
                        if(field->qft == QFT_TEXT)
                        {
                            ;
                        }
                        else if(field->qft == QFT_URL)
                        {
                            if(FAILED(IsValid( bstrValue )))
                            {
                                m_state = HyperLinks::STATE_MALFORMED;
                                break;
                            }
                        }
                        else if(field->qft == QFT_TAXONOMY)
                        {
                            /* TO DO */
                        }
                        else if(field->qft == QFT_APPLICATION)
                        {
                            /* TO DO */
                        }
                    }
                }

                m_fmt = ptr->fmt;
                break;
            }
        }

        //
        // If the URL begins with HCP: but not HCP://, it's a protocol redirection, so recurse.
        //
        if(m_fmt == HyperLinks::FMT_HCP_REDIR)
        {
            __MPC_SET_ERROR_AND_EXIT(hr, Initialize( szURL + MAXSTRLEN( l_szHCP_redir ) ));
        }

        if(m_fmt == HyperLinks::FMT_INVALID) // Still not resolved...
        {
            MPC::URL        url;
            INTERNET_SCHEME scheme;

            if(SUCCEEDED(url.put_URL   ( szURL  )) &&
               SUCCEEDED(url.get_Scheme( scheme ))  )
            {
                switch(scheme)
                {
                case INTERNET_SCHEME_UNKNOWN   : m_fmt   = HyperLinks::FMT_INTERNET_UNKNOWN   ; break;
                case INTERNET_SCHEME_FTP       : m_fmt   = HyperLinks::FMT_INTERNET_FTP       ; break;
                case INTERNET_SCHEME_GOPHER    : m_fmt   = HyperLinks::FMT_INTERNET_GOPHER    ; break;
                case INTERNET_SCHEME_HTTP      : m_fmt   = HyperLinks::FMT_INTERNET_HTTP      ; break;
                case INTERNET_SCHEME_HTTPS     : m_fmt   = HyperLinks::FMT_INTERNET_HTTPS     ; break;
                case INTERNET_SCHEME_FILE      : m_fmt   = HyperLinks::FMT_INTERNET_FILE      ; break;
                case INTERNET_SCHEME_NEWS      : m_fmt   = HyperLinks::FMT_INTERNET_NEWS      ; break;
                case INTERNET_SCHEME_MAILTO    : m_fmt   = HyperLinks::FMT_INTERNET_MAILTO    ; break;
                case INTERNET_SCHEME_SOCKS     : m_fmt   = HyperLinks::FMT_INTERNET_SOCKS     ; break;
                case INTERNET_SCHEME_JAVASCRIPT: m_fmt   = HyperLinks::FMT_INTERNET_JAVASCRIPT; break;
                case INTERNET_SCHEME_VBSCRIPT  : m_fmt   = HyperLinks::FMT_INTERNET_VBSCRIPT  ; break;
                default                        : m_state = HyperLinks::STATE_MALFORMED        ; break;
                }
            }
        }
    }

    hr = S_OK;



    __HCP_FUNC_CLEANUP;

    __HCP_FUNC_EXIT(hr);
}

bool HyperLinks::ParsedUrl::IsLocal()
{
    switch(m_fmt)
    {
    case HyperLinks::FMT_INTERNET_UNKNOWN   :
    case HyperLinks::FMT_INTERNET_FTP       :
    case HyperLinks::FMT_INTERNET_GOPHER    :
    case HyperLinks::FMT_INTERNET_HTTP      :
    case HyperLinks::FMT_INTERNET_HTTPS     :
    case HyperLinks::FMT_INTERNET_FILE      :
    case HyperLinks::FMT_INTERNET_NEWS      :
    case HyperLinks::FMT_INTERNET_MAILTO    :
    case HyperLinks::FMT_INTERNET_SOCKS     :
    case HyperLinks::FMT_INTERNET_JAVASCRIPT:
    case HyperLinks::FMT_INTERNET_VBSCRIPT  :
        return false;

    case HyperLinks::FMT_MSITS:
        //
        // Make sure it's not on a network share!
        //
        {
            CComBSTR bstrStorageName;
            CComBSTR bstrFilePath;

            if(MPC::MSITS::IsCHM( m_strURL.c_str(), &bstrStorageName, &bstrFilePath ))
            {
                if(wcsncmp( L"\\\\", SAFEBSTR(bstrStorageName), 2 ) == 0)
                {
                    return false;
                }
            }
        }
        break;

    }

    return true;
}

HyperLinks::State HyperLinks::ParsedUrl::CheckState( /*[in/out]*/ bool& fFirstWinInetUse )
{
    HRESULT  hr;
    State    state = HyperLinks::STATE_NOTFOUND;
    LPCWSTR  szURL = m_strURL.c_str();
    LPCWSTR  szEnd;
    CComBSTR bstrURL;

    //
    // Skip the bookmark sign.
    //
    if((szEnd = wcschr( szURL, '#' )))
    {
        bstrURL.Attach( ::SysAllocStringLen( szURL, (szEnd - szURL) ) );
        szURL = SAFEBSTR( bstrURL );
    }

    switch(m_fmt)
    {
    case HyperLinks::FMT_INVALID            :
        state = HyperLinks::STATE_MALFORMED;
        break;

    case HyperLinks::FMT_INTERNET_FTP       :
    case HyperLinks::FMT_INTERNET_GOPHER    :
    case HyperLinks::FMT_INTERNET_HTTP      :
    case HyperLinks::FMT_INTERNET_HTTPS     :
    case HyperLinks::FMT_INTERNET_FILE      :
    case HyperLinks::FMT_INTERNET_NEWS      :
		while(1)
        {
            DWORD dwTimeout = m_fBackground ? HC_TIMEOUT_LINKCHECKER_BACKGROUND : HC_TIMEOUT_LINKCHECKER_FOREGROUND;

            if(SUCCEEDED(hr = MPC::Connectivity::DestinationReachable( szURL, dwTimeout )))
            {
                state = HyperLinks::STATE_ALIVE;
            }
            else if(hr == E_INVALIDARG) // Unsupported protocol, assume the link is OK.
            {
                state = HyperLinks::STATE_ALIVE;
            }
            else if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
            {
                state = HyperLinks::STATE_NOTFOUND;
            }
            else if(hr == HRESULT_FROM_WIN32(ERROR_INTERNET_DISCONNECTED))
            {
                state = HyperLinks::STATE_OFFLINE;
            }
            else
            {
                //
                // If it's the first time through, it could be that WinINET couldn't activate the proxy code in time...
                //
                if(fFirstWinInetUse == true)
                {
					fFirstWinInetUse = false; continue;
                }

				if(SUCCEEDED(MPC::Connectivity::NetworkAlive( dwTimeout )))
				{
					state = HyperLinks::STATE_UNREACHABLE;
				}
				else
				{
					state = HyperLinks::STATE_OFFLINE;
				}
            }

            fFirstWinInetUse = false; break;
        }
        break;

    case HyperLinks::FMT_INTERNET_UNKNOWN   :
    case HyperLinks::FMT_INTERNET_MAILTO    :
    case HyperLinks::FMT_INTERNET_SOCKS     :
    case HyperLinks::FMT_INTERNET_JAVASCRIPT:
    case HyperLinks::FMT_INTERNET_VBSCRIPT  :
        state = HyperLinks::STATE_ALIVE;
        break;

    case HyperLinks::FMT_HCP                :
        {
            CComPtr<IInternetProtocolRoot> obj;

            state = HyperLinks::STATE_NOTFOUND;

            if(SUCCEEDED(CHCPProtocol::CreateInstance( &obj )))
            {
                if(SUCCEEDED(obj->Start( szURL, NULL, NULL, 0, NULL )))
                {
                    state = HyperLinks::STATE_ALIVE;
                }
            }
        }
        break;

    case HyperLinks::FMT_MSITS              :
        {
            MPC::wstring     strUrlModified;
            MPC::wstring     strUrlModified2;
            CComBSTR         bstrStorageName;
            CComBSTR         bstrFilePath;
            CComPtr<IStream> stream;

            state = HyperLinks::STATE_NOTFOUND;

            CPCHWrapProtocolInfo::NormalizeUrl( szURL                 , strUrlModified , /*fReverse*/true  );
            CPCHWrapProtocolInfo::NormalizeUrl( strUrlModified.c_str(), strUrlModified2, /*fReverse*/false );
            if(MPC::MSITS::IsCHM( strUrlModified2.c_str(), &bstrStorageName, &bstrFilePath ))
            {
                if(SUCCEEDED(MPC::MSITS::OpenAsStream( bstrStorageName, bstrFilePath, &stream )))
                {
                    state = HyperLinks::STATE_ALIVE;
                }
            }
        }
        break;

    case HyperLinks::FMT_CENTER_HOMEPAGE    :
    case HyperLinks::FMT_CENTER_SUPPORT     :
    case HyperLinks::FMT_CENTER_OPTIONS     :
    case HyperLinks::FMT_CENTER_UPDATE      :
    case HyperLinks::FMT_CENTER_COMPAT      :
    case HyperLinks::FMT_CENTER_TOOLS       :
    case HyperLinks::FMT_CENTER_ERRMSG      :
        state = HyperLinks::STATE_ALIVE;
        break;

    case HyperLinks::FMT_SEARCH             :
    case HyperLinks::FMT_INDEX              :
    case HyperLinks::FMT_SUBSITE            :
        state = HyperLinks::STATE_ALIVE;
        break;

    case HyperLinks::FMT_LAYOUT_FULLWINDOW  :
    case HyperLinks::FMT_LAYOUT_CONTENTONLY :
    case HyperLinks::FMT_LAYOUT_KIOSK       :
    case HyperLinks::FMT_LAYOUT_XML         :
        state = HyperLinks::STATE_ALIVE;
        break;

    case HyperLinks::FMT_REDIRECT           :
        state = HyperLinks::STATE_ALIVE;
        break;

    case HyperLinks::FMT_APPLICATION        :
        state = HyperLinks::STATE_ALIVE;
        break;

    case HyperLinks::FMT_RESOURCE           :
        state = HyperLinks::STATE_UNREACHABLE;
        break;
    }

    m_dLastChecked = MPC::GetLocalTime();

    return state;
}

bool HyperLinks::ParsedUrl::IsOkToProceed()
{
    switch(m_state)
    {
    case HyperLinks::STATE_NOTPROCESSED:
    case HyperLinks::STATE_CHECKING    :
    case HyperLinks::STATE_ALIVE       :
        return true;
    }

    return false;
}


bool HyperLinks::ParsedUrl::HasQueryField( /*[in]*/ LPCWSTR szField )
{
    MPC::WStringLookupIter it = m_mapQuery.find( szField );

    return (it != m_mapQuery.end());
}

bool HyperLinks::ParsedUrl::GetQueryField( /*[in]*/ LPCWSTR szField, /*[in]*/ CComBSTR& bstrValue )
{
    MPC::WStringLookupIter it = m_mapQuery.find( szField );

    if(it != m_mapQuery.end())
    {
        bstrValue = it->second.c_str();
        return true;
    }

    bstrValue.Empty();
    return false;
}

////////////////////////////////////////////////////////////////////////////////

HyperLinks::UrlHandle::UrlHandle()
{
    m_main = NULL; // Lookup*    m_main;
    m_pu   = NULL; // ParsedUrl* m_pu;
}

HyperLinks::UrlHandle::~UrlHandle()
{
    Release();
}

void HyperLinks::UrlHandle::Attach( /*[in]*/ Lookup*    main ,
                                    /*[in]*/ ParsedUrl* pu   )
{
    Release();

    m_main = main; if(main) main->Lock();
    m_pu   = pu;
}

void HyperLinks::UrlHandle::Release()
{
    if(m_main) m_main->Unlock();

    m_main = NULL;
    m_pu   = NULL;
}

////////////////////////////////////////////////////////////////////////////////

HyperLinks::Lookup::Lookup()
{
    // PendingUrlList m_lst;
    // UrlMap         m_map;
}

HyperLinks::Lookup::~Lookup()
{
    Thread_Wait();
}

////////////////////

HyperLinks::Lookup* HyperLinks::Lookup::s_GLOBAL( NULL );

HRESULT HyperLinks::Lookup::InitializeSystem()
{
    if(s_GLOBAL == NULL)
    {
        s_GLOBAL = new HyperLinks::Lookup;
    }

    return s_GLOBAL ? S_OK : E_OUTOFMEMORY;
}

void HyperLinks::Lookup::FinalizeSystem()
{
    if(s_GLOBAL)
    {
        delete s_GLOBAL; s_GLOBAL = NULL;
    }
}

////////////////////

HRESULT HyperLinks::Lookup::RunChecker()
{
    __HCP_FUNC_ENTRY( "HyperLinks::Lookup::RunChecker" );

    HRESULT                      hr;
    MPC::SmartLock<_ThreadModel> lock( this );
    bool                         fFirstWinInetUse = true;


    Thread_SignalMain();

    while(Thread_IsAborted() == false)
    {
        bool           fSleep  = true;
        ParsedUrl*     urlBest = NULL;
        PendingUrlIter it;

        //
        // Look for the first query store not ready and execute it.
        //
        for(it = m_lst.begin(); it != m_lst.end();)
        {
            ParsedUrl* url = *it;

            if(url->m_state != HyperLinks::STATE_NOTPROCESSED)
            {
                m_lst.erase( it );

                it      = m_lst.begin();
                urlBest = NULL;
            }
            else
            {
                if(url->m_fBackground == false)
                {
                    urlBest = url;
                    break;
                }

                urlBest = url;
                it++;
            }
        }

        if(urlBest)
        {
            State state = HyperLinks::STATE_NOTFOUND;

            //
            // Remove this query from the pending list.
            //
            for(it = m_lst.begin(); it != m_lst.end(); )
            {
                if(*it == urlBest)
                {
                    m_lst.erase( it );

                    it = m_lst.begin();
                }
                else
                {
                    it++;
                }
            }

            DebugLog( L"%%%%%%%%%%%%%%%%%%%% CHECKING %s\n", urlBest->m_strURL.c_str() );
            urlBest->m_state = HyperLinks::STATE_CHECKING;

            lock = NULL;
            __MPC_PROTECT( state = urlBest->CheckState( fFirstWinInetUse ) );
            lock = this;

            urlBest->m_state = state;

            Thread_SignalMain();

            fSleep = false;
        }

        if(fSleep)
        {
            lock = NULL;
            Thread_WaitForEvents( NULL, INFINITE );
            lock = this;
        }
    }

    hr = S_OK;


    Thread_Abort();

    __HCP_FUNC_EXIT(hr);
}

HRESULT HyperLinks::Lookup::CreateItem( /*[in ]*/ LPCWSTR     szURL ,
                                        /*[out]*/ ParsedUrl*& pu    )
{
    __HCP_FUNC_ENTRY( "HyperLinks::Lookup::CreateItem" );

    HRESULT        hr;
    UrlIter        it;
    MPC::wstringUC strURL( SAFEWSTR( szURL ) );

    it = m_map.find( strURL );
    if(it == m_map.end())
    {
        pu = &(m_map[ strURL ]);

        __MPC_EXIT_IF_METHOD_FAILS(hr, pu->Initialize( szURL ));
    }
    else
    {
        pu = &it->second;
    }

    hr = S_OK;


    __HCP_FUNC_CLEANUP;

    __HCP_FUNC_EXIT(hr);
}

////////////////////

HRESULT HyperLinks::Lookup::Queue( /*[in]*/ LPCWSTR szURL )
{
    UrlHandle uh;

    return Get( szURL, uh );
}

HRESULT HyperLinks::Lookup::Get( /*[in]*/ LPCWSTR    szURL          ,
                                 /*[in]*/ UrlHandle& uh             ,
                                 /*[in]*/ DWORD      dwWaitForCheck ,
                                 /*[in]*/ bool       fForce         )
{
    __HCP_FUNC_ENTRY( "HyperLinks::Lookup::Get" );

    HRESULT                      hr;
    MPC::SmartLock<_ThreadModel> lock( this );
    ParsedUrl*                   pu;


    uh.Release();

    ////////////////////////////////////////////////////////////////////////////////

    if(Thread_IsRunning() == false)
    {
        lock = NULL;
        __MPC_EXIT_IF_METHOD_FAILS(hr, Thread_Start( this, RunChecker, NULL ));

        Thread_WaitNotificationFromWorker( INFINITE, /*fNoMessagePump*/true );
        lock = this;
    }

    ////////////////////////////////////////////////////////////////////////////////

    __MPC_EXIT_IF_METHOD_FAILS(hr, CreateItem( szURL, pu ));

    if(fForce)
    {
        pu->m_state = HyperLinks::STATE_NOTPROCESSED;
    }

    switch(pu->m_state)
    {
    case HyperLinks::STATE_ALIVE      :
    case HyperLinks::STATE_NOTFOUND   :
    case HyperLinks::STATE_UNREACHABLE:
    case HyperLinks::STATE_OFFLINE    :
        if(dwWaitForCheck)
        {
            //
            // Make sure the state is not stale.
            //
            DATE dNow = MPC::GetLocalTime();

            if((dNow - pu->m_dLastChecked) >= l_TIME_timeout)
            {
                pu->m_state = HyperLinks::STATE_NOTPROCESSED;
            }
        }
        break;
    }

    if(pu->m_state == HyperLinks::STATE_NOTPROCESSED)
    {
        bool fQueue = false;
        bool fWait  = false;

        if(dwWaitForCheck)
        {
            //
            // Elevate the URL to "important".
            //
            pu->m_fBackground = false;

            fWait = true;

            if(pu->IsLocal() == false)
            {
                fQueue = true;
            }
        }
        else
        {
            fQueue = true;
        }

        if(fQueue)
        {
            m_lst.push_back( pu );
            Thread_Signal();
        }

        if(fWait)
        {
            if(pu->IsLocal())
            {
                bool fFirstWinInetUse = false;

                pu->m_state = pu->CheckState( fFirstWinInetUse );
            }
            else
            {
                int iRetry = 5;

                dwWaitForCheck /= iRetry;

                while(pu->m_state == HyperLinks::STATE_NOTPROCESSED ||
                      pu->m_state == HyperLinks::STATE_CHECKING      )
                {
                    DWORD dwRes;

                    lock  = NULL;
                    dwRes = Thread_WaitNotificationFromWorker( dwWaitForCheck, /*fNoMessagePump*/true );
                    lock  = this;

                    if(iRetry-- == 0)
                    {
                        __MPC_SET_ERROR_AND_EXIT(hr, S_FALSE);
                    }
                }
            }
        }
    }

    hr = S_OK;


    __HCP_FUNC_CLEANUP;

    if(pu) uh.Attach( this, pu );

    __HCP_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

HRESULT HyperLinks::IsValid( /*[in]*/ LPCWSTR szURL )
{
    __HCP_FUNC_ENTRY( "HyperLinks::IsValid" );

    HRESULT   hr;
    ParsedUrl pu;

    __MPC_EXIT_IF_METHOD_FAILS(hr, pu.Initialize( szURL ));

    switch(pu.m_state)
    {
    case HyperLinks::STATE_INVALID  :
    case HyperLinks::STATE_MALFORMED:
        __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
    }

    hr = S_OK;


    __HCP_FUNC_CLEANUP;

    __HCP_FUNC_EXIT(hr);
}