/*++

   Copyright    (c)    1995-1996    Microsoft Corporation

   Module  Name :
      Context.cxx

   Abstract:
      The file contains the implementation of the Context object. A context
      job is an object which stored in the logging request queue.

   Author:

       Terence Kwan    ( terryk )    18-Sep-1996

   Project:

       IIS Logging 3.0

--*/

#include "precomp.hxx"
#include "comlog.hxx"
#include "iiscnfg.h"

//
// statics
//

CRITICAL_SECTION    COMLOG_CONTEXT::sm_listLock;
LIST_ENTRY          COMLOG_CONTEXT::sm_ContextListHead;


CHAR    g_pszResFromGetComputerName [MAX_PATH] ="";

VOID
COMLOG_CONTEXT::LoadPluginModules(
    VOID
    )
/*++
Routine Description:

    load all the plugin module from the metabase

Arguments:

    None.

Return Value:

    None

--*/
{

    DWORD   cb;
    MB      mb( (IMDCOM*) m_pvIMDCOM );
    PCHAR   p;
    CLSID   clsid;
    WCHAR   buf[MAX_PATH];
    BUFFER  szLoadOrder(1024);
    PCHAR   pEnd;
    DWORD   dwLogType;
    DWORD   nPlugins = 0;

    PPLUGIN_NODE    pluginNode;

    LPUNKNOWN       punk;
    ILogPluginEx    *pComponent;
    bool            fExtended;
    HRESULT         hr ;

    //
    // get the config information from the metabase
    //

    LockExclusive( );
    
    if ( !mb.Open(m_strMetabasePath.QueryStr()) ) 
    {
        DBGPRINTF((DBG_CONTEXT,"Unable to open MB path %s[err %x]\n",
                    m_strMetabasePath.QueryStr(), GetLastError()));
                    
        goto exit;
    }

    //
    // If logging disabled, bail out
    //

    if ( mb.GetDword( "", MD_LOG_TYPE, IIS_MD_UT_SERVER, &dwLogType)) 
    {

        if ( dwLogType == MD_LOG_TYPE_DISABLED ) 
        {
            DBGPRINTF((DBG_CONTEXT,"Logging disabled\n"));
            goto exit;
        }
    }

    //
    // Read the plugin order list
    //

retry:

    cb = szLoadOrder.QuerySize( );
    
    if ( !mb.GetString( "", MD_LOG_PLUGIN_ORDER, IIS_MD_UT_SERVER, (PCHAR)szLoadOrder.QueryPtr( ), &cb )) 
    {

        DWORD err = GetLastError();

        if ( err == ERROR_INSUFFICIENT_BUFFER ) 
        {

            DBGPRINTF((DBG_CONTEXT,"Buff Too Small[%d] need[%d]\n", szLoadOrder.QuerySize(), cb ));

            if ( cb > szLoadOrder.QuerySize( ) ) 
            {
                szLoadOrder.Resize( cb );
                goto retry;
            }
        }

        DBGPRINTF((DBG_CONTEXT,"Error getting pluginOrder[err %x]\n", err));

        mb.Close();
        goto exit;
    }

    mb.Close();

    //
    // Parse it
    //

    pEnd = (PCHAR)szLoadOrder.QueryPtr( );

    for ( p = pEnd;  pEnd != NULL;  p = pEnd + 1 ) 
    {
        if ( *p == '\0' ) 
        {
            break;
        }

        //
        // pEnd will point to the next entry
        //

        pEnd = strchr(p, ',');
        
        if ( pEnd != NULL ) 
        {
            *pEnd = '\0';
        }

        //
        // p points to the CLSID
        //

        DBGPRINTF((DBG_CONTEXT,"Got Logging clsid %s\n",p));
        
        if ( !TsIsNtServer() ) 
        {

            //
            // odbc not allowed
            //

            if ( _stricmp(p,ODBCLOG_CLSID) == 0 ) 
            {
                DBGPRINTF((DBG_CONTEXT,"ODBC logging not allowed for NTW\n"));
                continue;
            }
        }

        //
        // convert string to CLSID
        //

        mbstowcs( (WCHAR *)buf, p, MAX_PATH);

        hr = CLSIDFromString( buf, &clsid );
        
        if (FAILED(hr)) 
        {
            //
            // cannot convert string
            //
            
            DBGPRINTF((DBG_CONTEXT,"Cannot convert string to CLSID: %s\n",p));
            continue;
        }

        hr = CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punk );

        if (FAILED(hr)) 
        {
            //
            // cannot convert string
            //
            
            DBGPRINTF((DBG_CONTEXT,"Cannot create instance: %s\n",p));
            continue;
        }

        hr = punk->QueryInterface(IID_ILogPluginEx, (void **)&pComponent);

        if (SUCCEEDED(hr)) 
        {
            fExtended = true;
        }
        else
        {
            fExtended = false;

            //
            // Try getting the older interface
            //
            
            hr = punk->QueryInterface(IID_ILogPlugin, (void **)&pComponent);
        }
        
        punk->Release();

        if (FAILED(hr))
        {
            DBGPRINTF((DBG_CONTEXT,"Unable to get the Extended or the Standard Plugin Interface.\n"));
            continue;
        }

        //
        // Add the component
        //

        pluginNode = (PPLUGIN_NODE)LocalAlloc( 0, sizeof(PLUGIN_NODE) );
        
        if ( pluginNode == NULL ) 
        {
            pComponent->Release( );
            continue;
        }

        pluginNode->pComponent = pComponent;
        pluginNode->fSupportsExtendedInterface = fExtended;
        
        nPlugins++;
        InsertTailList(&m_pluginList, &pluginNode->ListEntry);

        //
        // Is this the default?
        //

        if ( _stricmp(p,EXTLOG_CLSID) == 0 ) 
        {
            m_fDefault = TRUE;
            DBGPRINTF((DBG_CONTEXT,"Logging Extended[%d]\n", m_fDefault));
        }
    }

    if ( nPlugins > 1 ) 
    {
        m_fDefault = FALSE;
    }

exit:
    Unlock( );
    return;

} // COMLOG_CONTEXT::LoadPlugInModules


VOID
COMLOG_CONTEXT::ReleasePluginModules(
    VOID
    )
{
    PLIST_ENTRY     listEntry;
    PPLUGIN_NODE  pluginModule;

    LockExclusive( );

    m_fDefault = FALSE;

    while ( !IsListEmpty(&m_pluginList) ) {

        listEntry = RemoveHeadList( &m_pluginList );
        pluginModule = (PPLUGIN_NODE)CONTAINING_RECORD(
                                                listEntry,
                                                PLUGIN_NODE,
                                                ListEntry
                                                );

        pluginModule->pComponent->Release( );
        LocalFree( pluginModule );
    }

    Unlock( );
    return;
} // COMLOG_CONTEXT::ReleasePlugInModules


COMLOG_CONTEXT::COMLOG_CONTEXT(
            LPCSTR pszInstanceName,
            LPCSTR pszMetabasePath,
            LPVOID pvIMDCOM
            )
:     m_fDefault            (FALSE),
      m_pvIMDCOM            (pvIMDCOM)

/*++

Routine Description:
    Constructor for clapi context object

Arguments:
    pszInstanceName - name of the instance

Return Value:

--*/
{
    DWORD cbComputerNameSize = sizeof(g_pszResFromGetComputerName);
    MB      mb( (IMDCOM*) m_pvIMDCOM );

    InitializeListHead( &m_pluginList );
    InitializeListHead( &m_PublicListEntry);
    
    m_strInstanceName.Copy(pszInstanceName);
    m_strMetabasePath.Copy(pszMetabasePath);


    if (g_pszResFromGetComputerName[0]==0)
    {
        if ( !GetComputerName( g_pszResFromGetComputerName, &cbComputerNameSize) ) 
        {
            strcpy(g_pszResFromGetComputerName,"<Server>");
        }
    }

    m_strComputerName.Copy(g_pszResFromGetComputerName);

    //
    // Add into the global list
    //

    EnterCriticalSection( &COMLOG_CONTEXT::sm_listLock );
    InsertTailList(
            &COMLOG_CONTEXT::sm_ContextListHead,
            &m_ContextListEntry
            );

    LeaveCriticalSection( &COMLOG_CONTEXT::sm_listLock );

    //
    // Load all the plugin modules
    //

    LoadPluginModules( );

    return;

} // COMLOG_CONTEXT::COMLOG



COMLOG_CONTEXT::~COMLOG_CONTEXT()
/*++

Routine Description:
    destructor

Arguments:

Return Value:

--*/
{
    PLIST_ENTRY     listEntry;
    PInetLogPublic  pPublic;

    EnterCriticalSection( &COMLOG_CONTEXT::sm_listLock );
    LockExclusive();
    
    RemoveEntryList(&m_ContextListEntry);
    
    ReleasePluginModules();
    
    for ( listEntry = m_PublicListEntry.Flink;
          listEntry != &m_PublicListEntry;
          listEntry = listEntry->Flink    ) 
    {

        pPublic = (PInetLogPublic)CONTAINING_RECORD(
                                        listEntry,
                                        CInetLogPublic,
                                        m_ListEntry
                                        );

       pPublic->m_pContext = NULL;
    }

    Unlock();
    LeaveCriticalSection( &COMLOG_CONTEXT::sm_listLock );

} // COMLOG_CONTEXT::~COMLOG()


VOID
COMLOG_CONTEXT::LogInformation(
            PINETLOG_INFORMATION pLogInfo
            )
{

    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;
    CInetLogInformation inetLog;

    LockShared();

    if ( m_pluginList.Flink != &m_pluginList )
    {
        // logging is enabled
        
        inetLog.CanonicalizeLogRecord(
                                pLogInfo,
                                m_strInstanceName.QueryStr(),
                                m_strComputerName.QueryStr(),
                                m_fDefault
                                );

        for ( listEntry = m_pluginList.Flink;
              listEntry != &m_pluginList;
              listEntry = listEntry->Flink    ) 
        {

            plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

            plugin->pComponent->LogInformation( &inetLog );
        }
    }

    Unlock();

} // COMLOG_CONTEXT::LogInformation



VOID
COMLOG_CONTEXT::LogInformation(
            IInetLogInformation *pLogObj 
            )
{

    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;

    LockShared();

    if ( m_pluginList.Flink != &m_pluginList )
    {
        // logging is enabled
        
        for ( listEntry = m_pluginList.Flink;
              listEntry != &m_pluginList;
              listEntry = listEntry->Flink    ) 
        {

            plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

            plugin->pComponent->LogInformation( pLogObj );
        }
    }

    Unlock();

} // COMLOG_CONTEXT::LogInformation


VOID
COMLOG_CONTEXT::LogCustomInformation(
            IN  DWORD               cCount, 
            IN  PCUSTOM_LOG_DATA    pCustomLogData,
            IN  LPSTR               szHeaderSuffix
            )
{

    //
    // This function is supported only if the extended interface was found on the plugin
    //
    
    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;

    LockShared();
    
    for ( listEntry = m_pluginList.Flink;
          listEntry != &m_pluginList;
          listEntry = listEntry->Flink    ) 
    {

        plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

        if (plugin->fSupportsExtendedInterface)
        {
            plugin->pComponent->LogCustomInformation( cCount, pCustomLogData, szHeaderSuffix);
        }
    }

    Unlock();

} // COMLOG_CONTEXT::LogCustomInformation


VOID
COMLOG_CONTEXT::NotifyChange(
            VOID
            )
{
    TerminateLog();
    ReleasePluginModules( );

    LoadPluginModules( );
    InitializeLog(
        m_strInstanceName.QueryStr(),
        m_strMetabasePath.QueryStr(),
        (CHAR*)m_pvIMDCOM
        );

} // COMLOG_CONTEXT::NotifyChange



VOID
COMLOG_CONTEXT::GetConfig(
    IN INETLOG_CONFIGURATIONA *pConfigInfo
    )
{
    //
    // just return the first configuration information
    //

    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;

    LockShared( );
    listEntry = m_pluginList.Flink;

    if (listEntry != &m_pluginList){

        plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

        plugin->pComponent->GetConfig(
                            sizeof(INETLOG_CONFIGURATIONA),
                            (BYTE *)pConfigInfo);

        Unlock( );
        return;
    }

    //
    // No Log
    //

    Unlock( );
    pConfigInfo->inetLogType = INET_LOG_DISABLED;
    return;
} // GetConfig


VOID
COMLOG_CONTEXT::SetConfig(
    IN INETLOG_CONFIGURATIONA *pConfigInfo
    )
{
    //
    // check the log type and call the proper setconfig function
    //

    MB      mb( (IMDCOM*) m_pvIMDCOM );

    //
    // NTW restrictions
    //

    if ( (pConfigInfo->inetLogType == INET_LOG_TO_SQL) &&
         !TsIsNtServer() ) {
        return;
    }

    if ( !mb.Open( m_strMetabasePath.QueryStr(),
                   METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
    {
        return;
    }

    //
    // Release all
    //

    ReleasePluginModules( );

    switch (pConfigInfo->inetLogType) {

        case INET_LOG_TO_FILE:
            switch (pConfigInfo->u.logFile.ilFormat) {

            case INET_LOG_FORMAT_INTERNET_STD:

                mb.SetString("",
                             MD_LOG_PLUGIN_ORDER,
                             IIS_MD_UT_SERVER,
                             ASCLOG_CLSID );
                break;

            case INET_LOG_FORMAT_NCSA:
                mb.SetString("",
                             MD_LOG_PLUGIN_ORDER,
                             IIS_MD_UT_SERVER,
                             NCSALOG_CLSID );
                break;

            case INET_LOG_FORMAT_EXTENDED:
                mb.SetString("",
                             MD_LOG_PLUGIN_ORDER,
                             IIS_MD_UT_SERVER,
                             EXTLOG_CLSID );
                mb.SetDword( "",
                        MD_LOGEXT_FIELD_MASK,
                        IIS_MD_UT_SERVER,
                        pConfigInfo->u.logFile.dwFieldMask );
                break;

            default:
                DBGPRINTF((DBG_CONTEXT,"SetConfig: Invalid Format type %d\n",
                    pConfigInfo->inetLogType));
                goto no_log;
            }

            mb.SetDword( "",
                    MD_LOGFILE_PERIOD,
                    IIS_MD_UT_SERVER,
                    pConfigInfo->u.logFile.ilPeriod );

            if (pConfigInfo->u.logFile.ilPeriod == INET_LOG_PERIOD_NONE ) {
                mb.SetDword( "",
                    MD_LOGFILE_TRUNCATE_SIZE,
                    IIS_MD_UT_SERVER, pConfigInfo->
                    u.logFile.cbSizeForTruncation );
            }

            mb.SetString( "",
                MD_LOGFILE_DIRECTORY,
                IIS_MD_UT_SERVER,
                pConfigInfo->u.logFile.rgchLogFileDirectory );

            mb.SetDword( "",
                    MD_LOG_TYPE,
                    IIS_MD_UT_SERVER,
                    MD_LOG_TYPE_ENABLED );

            break;

        case INET_LOG_TO_SQL:

            mb.SetString("",
                         MD_LOG_PLUGIN_ORDER,
                         IIS_MD_UT_SERVER,
                         ODBCLOG_CLSID );

            mb.SetString( "",
                          MD_LOGSQL_DATA_SOURCES,
                          IIS_MD_UT_SERVER,
                          pConfigInfo->u.logSql.rgchDataSource );

            mb.SetString( "",
                          MD_LOGSQL_TABLE_NAME,
                          IIS_MD_UT_SERVER,
                          pConfigInfo->u.logSql.rgchTableName );

            mb.SetString( "",
                          MD_LOGSQL_USER_NAME,
                          IIS_MD_UT_SERVER,
                          pConfigInfo->u.logSql.rgchUserName );

            mb.SetString( "",
                          MD_LOGSQL_PASSWORD,
                          IIS_MD_UT_SERVER,
                          pConfigInfo->u.logSql.rgchPassword,
                          METADATA_INHERIT|METADATA_SECURE );

            mb.SetDword( "",
                    MD_LOG_TYPE,
                    IIS_MD_UT_SERVER,
                    MD_LOG_TYPE_ENABLED );

            break;

        case INET_LOG_DISABLED:
        default:
            goto no_log;
    }

exit:
    mb.Save();
    mb.Close();
    return;

no_log:

    mb.SetString( "",
            MD_LOG_PLUGIN_ORDER,
            IIS_MD_UT_SERVER,
            ""
            );

    mb.SetDword( "",
            MD_LOG_TYPE,
            IIS_MD_UT_SERVER,
            MD_LOG_TYPE_DISABLED );

    goto exit;

} // COMLOG_CONTEXT::SetConfig


VOID
COMLOG_CONTEXT::QueryExtraLogFields(
                PDWORD pcbSize,
                PCHAR  pszLogFields
                )
{
    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;

    LockShared( );
    listEntry = m_pluginList.Flink;
    if (listEntry != &m_pluginList){

        plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

        plugin->pComponent->QueryExtraLoggingFields(
                                        pcbSize,
                                        pszLogFields
                                        );

        //
        // handle just the 1st component
        //

        Unlock( );
        return;
    }

    Unlock( );

    *pcbSize = 0;
    *pszLogFields = '\0';
    return;

} // COMLOG_CONTEXT::QueryExtraLogFields




VOID
COMLOG_CONTEXT::InitializeLog(
        LPCSTR pszInstanceName,
        LPCSTR pszMetabasePath,
        LPVOID
        )
{
    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;

    LockExclusive();

    for ( listEntry = m_pluginList.Flink;
          listEntry != &m_pluginList;
          listEntry = listEntry->Flink    ) {

        plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

        plugin->pComponent->InitializeLog(
                                pszInstanceName,
                                pszMetabasePath,
                                (PCHAR)m_pvIMDCOM
                                );

        if ( m_fDefault ) {

            INETLOG_CONFIGURATIONA  config;
            m_fDefault = FALSE;
            if ( SUCCEEDED( plugin->pComponent->GetConfig(
                                    sizeof(config),
                                    (PBYTE)&config ) ) ) {

                if ( (config.u.logFile.ilFormat ==
                                INET_LOG_FORMAT_EXTENDED)
                                &&
                     (config.u.logFile.dwFieldMask ==
                                DEFAULT_EXTLOG_FIELDS) ) {

                    m_fDefault = TRUE;
                }
            }
        }

    }

    Unlock();

} // COMLOG_CONTEXT::InitializeLog


VOID
COMLOG_CONTEXT::TerminateLog(
    VOID
    )
{
    PLIST_ENTRY listEntry;
    PPLUGIN_NODE plugin;

    LockExclusive( );
    for ( listEntry = m_pluginList.Flink;
          listEntry != &m_pluginList;
          listEntry = listEntry->Flink    ) {

        plugin = (PPLUGIN_NODE)CONTAINING_RECORD(
                                        listEntry,
                                        PLUGIN_NODE,
                                        ListEntry
                                        );

        plugin->pComponent->TerminateLog( );
    }

    Unlock( );

} // COMLOG_CONTEXT::TerminateLog