Example #1
0
//
// Load authorized serial list for the supplied account
//
void CAccountManager::LoadAccountSerialUsage(CAccount* pAccount)
{
    auto& outSerialUsageList = pAccount->GetSerialUsageList();
    outSerialUsageList.clear();

    CRegistryResult result;
    m_pDatabaseManager->QueryWithResultf(m_hDbConnection, &result,
                                         "SELECT "
                                         " serial"
                                         " ,added_ip"
                                         " ,added_date"
                                         " ,auth_who"
                                         " ,auth_date"
                                         " ,last_login_ip"
                                         " ,last_login_date"
                                         " ,last_login_http_date"
                                         " FROM serialusage"
                                         " WHERE userid=?",
                                         SQLITE_INTEGER, pAccount->GetID());

    for (CRegistryResultIterator iter = result->begin(); iter != result->end(); ++iter)
    {
        const CRegistryResultRow& row = *iter;
        outSerialUsageList.push_back(CAccount::SSerialUsage());
        CAccount::SSerialUsage& info = outSerialUsageList.back();
        info.strSerial = (const char*)row[0].pVal;
        info.strAddedIp = (const char*)row[1].pVal;
        info.tAddedDate = row[2].GetNumber<time_t>();
        info.strAuthWho = (const char*)row[3].pVal;
        info.tAuthDate = row[4].GetNumber<time_t>();
        info.strLastLoginIp = (const char*)row[5].pVal;
        info.tLastLoginDate = row[6].GetNumber<time_t>();
        info.tLastLoginHttpDate = row[7].GetNumber<time_t>();
    }
}
bool CAccountManager::GetAllAccountData( CAccount* pAccount, lua_State* pLua )
{
    //Get the user ID
    int iUserID = pAccount->GetID();
    //create a new registry result for the query return
    CRegistryResult result;
    SString strKey;

    //Select the value and type from the database where the user is our user and the key is the required key
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT key,value,type from userdata where userid=?", SQLITE_INTEGER, iUserID );

    //Do we have any results?
    if ( result->nRows > 0 )
    {
        //Loop through until i is the same as the number of rows
        for ( CRegistryResultIterator iter = result->begin() ; iter != result->end() ; ++iter )
        {
            const CRegistryResultRow& row = *iter;
            //Get our key
            strKey = (const char *)row[0].pVal;
            //Get our type
            int iType = static_cast < int > ( row[2].nVal );
            //Account data is stored as text so we don't need to check what type it is just return it
            if ( iType == LUA_TNIL )
            {
                lua_pushstring ( pLua, strKey );
                lua_pushnil ( pLua );
                lua_settable ( pLua, -3 );
            }
            if ( iType == LUA_TBOOLEAN )
            {
                SString strResult = (const char *)row[1].pVal;
                lua_pushstring ( pLua, strKey );
                lua_pushboolean ( pLua, strResult == "true" ? true : false );
                lua_settable ( pLua, -3 );
            }
            if ( iType == LUA_TNUMBER )
            {
                lua_pushstring ( pLua, strKey );
                lua_pushnumber ( pLua, strtod ( (const char*)row[1].pVal, NULL ) );
                lua_settable ( pLua, -3 );
            }
            else
            {
                lua_pushstring ( pLua, strKey );
                lua_pushstring ( pLua, ( (const char*)row[1].pVal ) );
                lua_settable ( pLua, -3 );
            }
        }
        return true;
    }
    return false;
}
void CAccountManager::GetAccountsBySerial ( const SString& strSerial, std::vector<CAccount*>& outAccounts )
{
    CRegistryResult result;
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT name FROM accounts WHERE serial = ?", SQLITE_TEXT, strSerial.c_str () );

    for ( CRegistryResultIterator iter = result->begin (); iter != result->end (); ++iter )
    {
        const CRegistryResultRow& row = *iter;

        CAccount* pAccount = Get ( (const char*)row[0].pVal );
        outAccounts.push_back ( pAccount );
    }
}
Example #4
0
CAccount* CAccountManager::GetAccountByID(int ID)
{
    CRegistryResult result;
    m_pDatabaseManager->QueryWithResultf(m_hDbConnection, &result, "SELECT name FROM accounts WHERE id = ?", SQLITE_INTEGER, ID);

    for (CRegistryResultIterator iter = result->begin(); iter != result->end(); ++iter)
    {
        const auto& row = *iter;

        return Get(reinterpret_cast<const char*>(row[0].pVal));
    }

    return nullptr;
}
Example #5
0
void CAccountManager::GetAccountsByData ( const SString& dataName, const SString& value, std::vector<CAccount*>& outAccounts ) 
{
    Save();
    CRegistryResult result;
    m_pDatabaseManager->QueryWithResultf( m_hDbConnection, &result, "SELECT acc.name FROM accounts acc, userdata dat WHERE dat.key = ? AND dat.value = ? AND dat.userid = acc.id", SQLITE_TEXT, dataName.c_str(), SQLITE_TEXT, value.c_str() );

    for ( CRegistryResultIterator iter = result->begin(); iter != result->end(); ++iter ) 
    {
        const CRegistryResultRow& row = *iter;

        CAccount* pAccount = Get( (const char*) row[0].pVal );
        if (pAccount)
            outAccounts.push_back( pAccount );
    }
}
bool CAccountManager::CopyAccountData( CAccount* pFromAccount, CAccount* pToAccount )
{
    //Get the user ID of the from account
    int iUserID = pFromAccount->GetID();
    //create a new registry result for the from account query return value
    CRegistryResult result;
    //create a new registry result for the to account query return value
    //initialize key and value strings
    SString strKey;
    SString strValue;

    //Select the key and value from the database where the user is our from account
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT key,value,type from userdata where userid=? LIMIT 1", SQLITE_INTEGER, iUserID );

    //Do we have any results?
    if ( result->nRows > 0 ) {
        //Loop through until i is the same as the number of rows
        for ( CRegistryResultIterator iter = result->begin() ; iter != result->end() ; ++iter )
        {
            const CRegistryResultRow& row = *iter;
            //Get our key
            strKey = (const char *)row[0].pVal;
            //Get our value
            strValue = (const char *)row[1].pVal;
            int iType = static_cast < int > ( row[2].nVal );
            //Select the id and userid where the user is the to account and the key is strKey
            CRegistryResult subResult;
            m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &subResult, "SELECT id,userid from userdata where userid=? and key=? LIMIT 1", SQLITE_INTEGER, iUserID, SQLITE_TEXT, strKey.c_str () );
            //If there is a key with this value update it otherwise insert it and store the return value in bRetVal
            if ( subResult->nRows > 0 )
                m_pDatabaseManager->Execf ( m_hDbConnection, "UPDATE userdata SET value=?, type=? WHERE userid=? AND key=?", SQLITE_TEXT, strValue.c_str (), SQLITE_INTEGER, iType, SQLITE_INTEGER, pToAccount->GetID (), SQLITE_TEXT, strKey.c_str () );
            else
                m_pDatabaseManager->Execf ( m_hDbConnection, "INSERT INTO userdata (userid, key, value, type) VALUES(?,?,?,?)", SQLITE_INTEGER, pToAccount->GetID (), SQLITE_TEXT, strKey.c_str (), SQLITE_TEXT, strValue.c_str (), SQLITE_INTEGER, iType );

        }
    }
    else
        //We had no results so return false (Nothing has changed)
        return false;

    return true;
}
Example #7
0
bool CAccountManager::CopyAccountData( CAccount* pFromAccount, CAccount* pToAccount )
{
    // list to store pFromAccount data to
    std::map < SString, CAccountData > copiedData;

    if ( !pFromAccount->IsRegistered () )   // is not registered account, retrieve data from memory
    {
        std::map < SString, CAccountData > ::iterator iter = pFromAccount->DataBegin ();
        for ( ; iter != pFromAccount->DataEnd (); iter++ )
        {
            MapSet( copiedData, iter->second.GetKey(), CAccountData( iter->second.GetKey(), iter->second.GetStrValue(), iter->second.GetType() ) );
        }
    }
    else    // is registered account, retrieve from database
    {
        SString strKey;
        SString strValue;

        //Get the user ID of the from account
        int iUserID = pFromAccount->GetID ();
        //create a new registry result for the from account query return value
        CRegistryResult result;

        m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT key,value,type from userdata where userid=?", SQLITE_INTEGER, iUserID );

        //Do we have any results?
        if ( result->nRows > 0 )
        {
            for ( CRegistryResultIterator iter = result->begin() ; iter != result->end() ; ++iter )
            {
                const CRegistryResultRow& row = *iter;
                //Get our key
                strKey = (const char *)row[0].pVal;
                //Get our value
                strValue = (const char *)row[1].pVal;
                int iType = static_cast < int > ( row[2].nVal );

                MapSet( copiedData, strKey, CAccountData ( strKey, strValue, iType ) );
           }
        }
    }

    if (copiedData.size () > 0) // got anything to copy?
    {
        std::map < SString, CAccountData > ::iterator iter = copiedData.begin ();

        for (; iter != copiedData.end(); iter++)
        {
            if ( !pToAccount->IsRegistered () ) // store to memory
            {
                pToAccount->SetData ( iter->second.GetKey (), iter->second.GetStrValue (), iter->second.GetType () );
            }
            else // store to database
            {
                CRegistryResult subResult;

                m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &subResult, "SELECT id,userid from userdata where userid=? and key=? LIMIT 1", SQLITE_INTEGER, pToAccount->GetID (), SQLITE_TEXT, iter->second.GetKey ().c_str() );
                //If there is a key with this value update it otherwise insert it and store the return value in bRetVal
                if ( subResult->nRows > 0 )
                    m_pDatabaseManager->Execf ( m_hDbConnection, "UPDATE userdata SET value=?, type=? WHERE userid=? AND key=?", SQLITE_TEXT, iter->second.GetStrValue ().c_str(), SQLITE_INTEGER, iter->second.GetType (), SQLITE_INTEGER, pToAccount->GetID (), SQLITE_TEXT, iter->second.GetKey ().c_str() );
                else
                    m_pDatabaseManager->Execf ( m_hDbConnection, "INSERT INTO userdata (userid, key, value, type) VALUES(?,?,?,?)", SQLITE_INTEGER, pToAccount->GetID (), SQLITE_TEXT, iter->second.GetKey ().c_str(), SQLITE_TEXT, iter->second.GetStrValue ().c_str(), SQLITE_INTEGER, iter->second.GetType () );
            }
        }
        return true;
    }
    else
        return false;
}
Example #8
0
bool CAccountManager::Load( void )
{
    //Create a registry result
    CRegistryResult result;
    //Select all our required information from the accounts database
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT id,name,password,ip,serial,httppass from accounts" );

    //Initialize all our variables
    m_iAccounts = 0;
    bool bNeedsVacuum = false;
    CElapsedTime activityTimer;
    bool bOutputFeedback = false;
    for ( CRegistryResultIterator iter = result->begin() ; iter != result->end() ; ++iter )
    {
        const CRegistryResultRow& row = *iter;
        //Fill User ID, Name & Password (Required data)
        int iUserID = static_cast < int > ( row[0].nVal );
        SString strName = (const char *)row[1].pVal;
        SString strPassword = (const char *)row[2].pVal;
        SString strIP = (const char *)row[3].pVal;
        SString strSerial = (const char *)row[4].pVal;
        SString strHttpPassAppend = (const char *)row[5].pVal;

        // Check for overlong names and incorrect escapement
        bool bRemoveAccount = false;
        bool bChanged = false;
        if ( strName.length () > 64 )
        {
            // Try to repair name
            if ( strName.length () <= 256 )
            {
                strName = strName.Replace ( "\"\"", "\"", true ).substr ( 0, 64 );
                bChanged = true;
            }

            // If name gone doolally or account with this name already exists, remove account
            if ( strName.length () > 256 || Get ( strName ) )
            {
                bNeedsVacuum = true;
                bRemoveAccount = true;
                CLogger::LogPrintf ( "Removed duplicate or damaged account for %s\n", strName.substr ( 0, 64 ).c_str() );
            }
        }

        // Check for disallowed account names
        if ( strName == "*****" || strName == CONSOLE_ACCOUNT_NAME )
            bRemoveAccount = true;

        // Do account remove if required
        if ( bRemoveAccount )
        {
            m_pDatabaseManager->Execf ( m_hDbConnection, "DELETE FROM accounts WHERE id=?", SQLITE_INTEGER, iUserID );
            m_pDatabaseManager->Execf ( m_hDbConnection, "DELETE FROM userdata WHERE userid=?", SQLITE_INTEGER, iUserID );
            m_pDatabaseManager->Execf ( m_hDbConnection, "DELETE FROM serialusage WHERE userid=?", SQLITE_INTEGER, iUserID );
            continue;
        }

        //Create a new account with the specified information
        CAccount* pAccount = g_pGame->GetAccountManager ()->AddPlayerAccount ( strName, strPassword, iUserID, strIP, strSerial, strHttpPassAppend );

        if ( bChanged )
            pAccount->SetChanged ( bChanged );
        m_iAccounts = std::max ( m_iAccounts, iUserID );

        // Feedback for the user
        if ( activityTimer.Get() > 5000 )
        {
            activityTimer.Reset();
            bOutputFeedback = true;
            CLogger::LogPrintf ( "Reading accounts %d/%d\n", m_List.size(), result->nRows );
        }
    }
    if ( bOutputFeedback )
        CLogger::LogPrintf ( "Reading accounts done.\n");
    if ( bNeedsVacuum )
        m_pDatabaseManager->Execf ( m_hDbConnection, "VACUUM" );

    // Save any upgraded accounts
    {
        CElapsedTime activityTimer;
        bool bOutputFeedback = false;
        uint uiSaveCount = 0;
        for ( CMappedAccountList::const_iterator iter = m_List.begin () ; iter != m_List.end () ; iter++ )
        {
            CAccount* pAccount = *iter;
            if ( pAccount->IsRegistered () && pAccount->HasChanged () && !pAccount->IsConsoleAccount () )
            {
                uiSaveCount++;
                Save ( pAccount, false );
                // Feedback for the user
                if ( activityTimer.Get() > 5000 )
                {
                    activityTimer.Reset();
                    bOutputFeedback = true;
                    CLogger::LogPrintf ( "Saving upgraded accounts %d\n", uiSaveCount );
                }
            }
        }

        if ( uiSaveCount > 100 )
        {
            bOutputFeedback = true;
            CLogger::LogPrintf ( "Finishing accounts upgrade...\n" );
            for ( uint i = 0 ; i < 10 ; i++ )
            {
                Sleep( 10 );
                m_pDatabaseManager->DoPulse ();
            }
        }

        if ( bOutputFeedback )
            CLogger::LogPrintf ( "Completed accounts upgrade.\n");
    }

    return true;
}
///////////////////////////////////////////////////////////////
//
// CPerfStatBandwidthUsageImpl::LoadStats
//
// Load from permanent storage 
//
///////////////////////////////////////////////////////////////
void CPerfStatBandwidthUsageImpl::LoadStats ( void )
{
    SFixedArray < uint, BWSTAT_INDEX_COUNT > uiSizeList;
    uiSizeList [ BWSTAT_INDEX_SPECIAL ] = 1;
    uiSizeList [ BWSTAT_INDEX_HOURS ] = NUM_HOUR_STATS;
    uiSizeList [ BWSTAT_INDEX_DAYS ] = NUM_DAY_STATS;
    uiSizeList [ BWSTAT_INDEX_MONTHS ] = NUM_MONTH_STATS;

    //
    // Clear current
    //
    m_History.clear ();
    m_History.resize ( BWSTAT_INDEX_COUNT );
    for ( uint t = 0 ; t < m_History.size () ; t++ )
    {
        SNetStatHistoryType& type = m_History [ t ];
        type.itemList.resize ( uiSizeList [ t ] );
        type.nowIndex = -1;

        for ( uint r = 0 ; r < type.itemList.size () ; r++ )
        {
            assert ( type.itemList [ r ].bDirty == false );
            assert ( type.itemList [ r ].llGameRecv == 0 );
            assert ( type.itemList [ r ].llGameRecvBlocked == 0 );
            assert ( type.itemList [ r ].llGameSent == 0 );
            assert ( type.itemList [ r ].llHttpSent == 0 );
        }
    }

    //
    // Fetch table from database
    //
    CDatabaseManager* pDatabaseManager = g_pGame->GetDatabaseManager ();

    CDbJobData* pJobData = pDatabaseManager->QueryStartf ( m_DatabaseConnection, "SELECT `type`,`idx`,`GameRecv`,`GameSent`,`HttpSent`,`GameRecvBlocked`,`GameResent` from " BW_STATS_TABLE_NAME );
    pDatabaseManager->QueryPoll ( pJobData, -1 );
    CRegistryResult result = pJobData->result.registryResult;

    // If data set is empty, try loading old data
    if ( result->nRows == 0 )
    {
        CDbJobData* pJobData = pDatabaseManager->QueryStartf ( m_DatabaseConnection, "SELECT `type`,`idx`,`GameRecv`,`GameSent`,`HttpSent`,`GameRecvBlocked` from " BW_STATS_TABLE_NAME );
        pDatabaseManager->QueryPoll ( pJobData, -1 );
        result = pJobData->result.registryResult;
    }

    // If data set is empty, try loading old data
    if ( result->nRows == 0 )
    {
        pJobData = pDatabaseManager->QueryStartf ( m_DatabaseConnection, "SELECT `type`,`idx`,`GameRecv`,`GameSent`,`HttpSent` from " BW_STATS_TABLE_NAME );
        pDatabaseManager->QueryPoll ( pJobData, -1 );
        result = pJobData->result.registryResult;
    }

    // If data set is empty, try loading old data
    if ( result->nRows == 0 )
        g_pGame->GetRegistry ()->Query ( &result, "SELECT `type`,`idx`,`GameRecv`,`GameSent`,`HttpSent` from `_perfstats_bandwidth_usage`" );

    if ( result->nRows > 0 && result->nColumns >= 5 )
    {
        for ( CRegistryResultIterator iter = result->begin() ; iter != result->end() ; ++iter )
        {
            const CRegistryResultRow& row = *iter;
            SString strType = (const char*)row[0].pVal;
            uint uiIndex = static_cast < uint > ( row[1].nVal );
            float GameRecv = Max ( 0.f, row[2].fVal );
            float GameSent = Max ( 0.f, row[3].fVal );
            float HttpSent = Max ( 0.f, row[4].fVal );
            float GameRecvBlocked = 0;
            if ( result->nColumns >= 6 )
                GameRecvBlocked = Max ( 0.f, row[5].fVal );
            float GameResent = 0;
            if ( result->nColumns >= 7 )
                GameResent = Max ( 0.f, row[6].fVal );

            uint uiType = BWStatNameToIndex ( strType );

            if ( uiType < m_History.size () )
            {
                SNetStatHistoryType& type = m_History [ uiType ];

                if ( uiIndex < type.itemList.size () )
                {
                    type.itemList [ uiIndex ].llGameRecv = (long long)GameRecv;
                    type.itemList [ uiIndex ].llGameRecvBlocked = (long long)GameRecvBlocked;
                    type.itemList [ uiIndex ].llGameSent = (long long)GameSent;
                    type.itemList [ uiIndex ].llGameResent = (long long)GameResent;
                    type.itemList [ uiIndex ].llHttpSent = (long long)HttpSent;
                }
            }
        }
    }

    //
    // Adjust for time difference between last save and now
    //
    {
        time_t tTime = UnixTimeNow ();

        // Special item
        const SNetStatHistoryType& type = m_History[ BWSTAT_INDEX_SPECIAL ];
        uint uiStatsHoursThen = (uint)type.itemList[0].llGameSent;          // Hours since 1/1/2011
        uint uiStatsHoursNow = UnixTimeToStatsHours ( tTime );          // Hours since 1/1/2011
        int iHoursElpased = uiStatsHoursNow - uiStatsHoursThen;

        // Max elapsed time of 13 months
        iHoursElpased = Min ( iHoursElpased, 730 * 13 );

        // Skip forward in time to clear out past data
        for ( int i = iHoursElpased - 1 ; i >= 0; i-- )
        {
            time_t tTime = StatsHoursToUnixTime ( uiStatsHoursNow - i );
            AddSampleAtTime ( tTime, 0, 0, 0, 0, 0 );
        }
    }

    //
    // (Re)create table to ensure it's in sync with what we have
    //
    pDatabaseManager->Execf ( m_DatabaseConnection, "DROP TABLE " BW_STATS_TABLE_NAME );
    pDatabaseManager->Execf ( m_DatabaseConnection, "CREATE TABLE IF NOT EXISTS " BW_STATS_TABLE_NAME " (`type` TEXT,`idx` INT, `GameRecv` REAL, `GameRecvBlocked` REAL, `GameSent` REAL, `GameResent` REAL, `HttpSent` REAL)" );
    pDatabaseManager->Execf ( m_DatabaseConnection, "CREATE INDEX IF NOT EXISTS IDX_BW_STATS_TYPE_IDX on " BW_STATS_TABLE_NAME "(`type`,`idx`)" );

    for ( uint t = 0 ; t < m_History.size () ; t++ )
    {
        const SNetStatHistoryType& type = m_History [ t ];

        for ( uint r = 0 ; r < type.itemList.size () ; r++ )
        {
            pDatabaseManager->Execf ( m_DatabaseConnection, 
                                                "INSERT INTO " BW_STATS_TABLE_NAME " (`type`,`idx`,`GameRecv`,`GameRecvBlocked`,`GameSent`,`GameResent`,`HttpSent`) VALUES (?,?,?,?,?,?,?)"
                                                , SQLITE_TEXT, *BWStatIndexToName ( t )
                                                , SQLITE_INTEGER, r
                                                , SQLITE_FLOAT, (float)type.itemList [ r ].llGameRecv
                                                , SQLITE_FLOAT, (float)type.itemList [ r ].llGameRecvBlocked
                                                , SQLITE_FLOAT, (float)type.itemList [ r ].llGameSent
                                                , SQLITE_FLOAT, (float)type.itemList [ r ].llGameResent
                                                , SQLITE_FLOAT, (float)type.itemList [ r ].llHttpSent
                                                );
        }
    }
}
Example #10
0
int CLuaDatabaseDefs::ExecuteSQLSelect ( lua_State* luaVM )
{
    SString strTable;
    SString strColumns;
    SString strWhere;
    SString strError;
    unsigned int uiLimit;

    CScriptArgReader argStream ( luaVM );
    argStream.ReadString ( strTable );
    argStream.ReadString ( strColumns );
    argStream.ReadString ( strWhere, "" );
    argStream.ReadNumber ( uiLimit, 0 );

    if ( !argStream.HasErrors () )
    {
        CRegistryResult Result;
        CPerfStatSqliteTiming::GetSingleton ()->SetCurrentResource ( luaVM );
        if ( CStaticFunctionDefinitions::ExecuteSQLSelect ( strTable, strColumns, strWhere, uiLimit, &Result ) )
        {
            lua_newtable ( luaVM );
            int i = 0;
            for ( CRegistryResultIterator iter = Result->begin (); iter != Result->end (); ++iter, ++i )
            {
                const CRegistryResultRow& row = *iter;
                //            for ( int i = 0; i < Result.nRows; i++ ) {
                lua_newtable ( luaVM );                             // new table
                lua_pushnumber ( luaVM, i + 1 );                      // row index number (starting at 1, not 0)
                lua_pushvalue ( luaVM, -2 );                        // value
                lua_settable ( luaVM, -4 );                         // refer to the top level table
                for ( int j = 0; j < Result->nColumns; j++ )
                {
                    const CRegistryResultCell& cell = row[j];
                    if ( cell.nType == SQLITE_NULL )
                        continue;

                    // Push the column name
                    lua_pushlstring ( luaVM, Result->ColNames[j].c_str (), Result->ColNames[j].size () );
                    switch ( cell.nType )                           // push the value with the right type
                    {
                    case SQLITE_INTEGER:
                        lua_pushnumber ( luaVM, static_cast <double> ( cell.nVal ) );
                        break;
                    case SQLITE_FLOAT:
                        lua_pushnumber ( luaVM, cell.fVal );
                        break;
                    case SQLITE_BLOB:
                        lua_pushlstring ( luaVM, (const char *) cell.pVal, cell.nLength );
                        break;
                    case SQLITE_TEXT:
                        lua_pushlstring ( luaVM, (const char *) cell.pVal, cell.nLength - 1 );
                        break;
                    default:
                        lua_pushnil ( luaVM );
                    }
                    lua_settable ( luaVM, -3 );
                }
                lua_pop ( luaVM, 1 );                               // pop the inner table
            }
            return 1;
        }
        else
        {
            strError = "Database query failed: " + CStaticFunctionDefinitions::SQLGetLastError ();
            m_pScriptDebugging->LogError ( luaVM, "%s", strError.c_str () );

            lua_pushstring ( luaVM, strError );
            lua_pushboolean ( luaVM, false );
            return 2;
        }
    }
    else
        m_pScriptDebugging->LogCustom ( luaVM, argStream.GetFullErrorMessage () );

    lua_pushboolean ( luaVM, false );
    return 1;
}
CAccountManager::CAccountManager ( const char* szFileName, SString strBuffer ): CXMLConfig ( szFileName )
    , m_AccountProtect( 6, 30000, 60000 * 1 )     // Max of 6 attempts per 30 seconds, then 1 minute ignore
{
    m_bAutoLogin = false;
    m_llLastTimeSaved = GetTickCount64_ ();
    m_bChangedSinceSaved = false;
    //set loadXML to false
    m_bLoadXML = false;
    m_iAccounts = 1;

    //Load internal.db
    m_pDatabaseManager = g_pGame->GetDatabaseManager ();
    m_hDbConnection = m_pDatabaseManager->Connect ( "sqlite", PathConform ( strBuffer ) );

    // Check if new installation
    CRegistryResult result;
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'" );
    bool bNewInstallation = ( result->nRows == 0 );

    //Create all our tables (Don't echo the results)
    m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE TABLE IF NOT EXISTS accounts (id INTEGER PRIMARY KEY, name TEXT, password TEXT, ip TEXT, serial TEXT)" );
    m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE TABLE IF NOT EXISTS userdata (id INTEGER PRIMARY KEY, userid INTEGER, key TEXT, value TEXT, type INTEGER)" );
    m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE TABLE IF NOT EXISTS settings (id INTEGER PRIMARY KEY, key TEXT, value INTEGER)" );

    // Check if unique index on accounts exists
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT name FROM sqlite_master WHERE type='index' AND name='IDX_ACCOUNTS_NAME_U'" );
    if ( result->nRows == 0 )
    {
        // Need to add unique index on accounts
        if ( !bNewInstallation )
            CLogger::LogPrintNoStamp ( "Updating accounts table...\n" );

        // Make sure we have a non-unique index to speed up the duplication removal
        m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE INDEX IF NOT EXISTS IDX_ACCOUNTS_NAME on accounts(name)" );
        // Remove any duplicate name entries
        m_pDatabaseManager->Execf ( m_hDbConnection, "DELETE FROM accounts WHERE rowid in "
                                    " (SELECT A.rowid"
                                    " FROM accounts A, accounts B"
                                    " WHERE A.rowid > B.rowid AND A.name = B.name)" );
        // Remove non-unique index
        m_pDatabaseManager->Execf ( m_hDbConnection, "DROP INDEX IF EXISTS IDX_ACCOUNTS_NAME" );
        // Add unique index
        m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE UNIQUE INDEX IF NOT EXISTS IDX_ACCOUNTS_NAME_U on accounts(name)" );
    }

    // Check if unique index on userdata exists
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT name FROM sqlite_master WHERE type='index' AND name='IDX_USERDATA_USERID_KEY_U'" );
    if ( result->nRows == 0 )
    {
        // Need to add unique index on userdata
        if ( !bNewInstallation )
            CLogger::LogPrintNoStamp ( "Updating userdata table...\n" );

        // Make sure we have a non-unique index to speed up the duplication removal
        m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE INDEX IF NOT EXISTS IDX_USERDATA_USERID_KEY on userdata(userid,key)" );
        // Remove any duplicate userid+key entries
        m_pDatabaseManager->Execf ( m_hDbConnection, "DELETE FROM userdata WHERE rowid in "
                                    " (SELECT A.rowid"
                                    " FROM userdata A, userdata B"
                                    " WHERE A.rowid > B.rowid AND A.userid = B.userid AND A.key = B.key)" );
        // Remove non-unique index
        m_pDatabaseManager->Execf ( m_hDbConnection, "DROP INDEX IF EXISTS IDX_USERDATA_USERID_KEY" );
        // Add unique index
        m_pDatabaseManager->Execf ( m_hDbConnection, "CREATE UNIQUE INDEX IF NOT EXISTS IDX_USERDATA_USERID_KEY_U on userdata(userid,key)" );
    }

    // Ensure old indexes are removed
    m_pDatabaseManager->Execf ( m_hDbConnection, "DROP INDEX IF EXISTS IDX_ACCOUNTS_NAME" );
    m_pDatabaseManager->Execf ( m_hDbConnection, "DROP INDEX IF EXISTS IDX_USERDATA_USERID" );
    m_pDatabaseManager->Execf ( m_hDbConnection, "DROP INDEX IF EXISTS IDX_USERDATA_USERID_KEY" );

    //Pull our settings
    m_pDatabaseManager->QueryWithResultf ( m_hDbConnection, &result, "SELECT key, value from settings" );

    //Did we get any results
    if ( result->nRows == 0 )
    {
        //Set our settings and clear the accounts/userdata tables just in case
        m_pDatabaseManager->Execf ( m_hDbConnection, "INSERT INTO settings (key, value) VALUES(?,?)", SQLITE_TEXT, "XMLParsed", SQLITE_INTEGER, 0 );
        //Tell the Server to load the xml file rather than the SQL
        m_bLoadXML = true;
    }
    else
    {
        bool bLoadXMLMissing = true;
        for ( CRegistryResultIterator iter = result->begin() ; iter != result->end() ; ++iter )
        {
            const CRegistryResultRow& row = *iter;
            SString strSetting = (const char *)row[0].pVal;

            //Do we have a result for XMLParsed
            if ( strSetting == "XMLParsed" )
            {
                //Is XMLParsed zero
                if ( row[1].nVal == 0 )
                {
                    //Tell the Server to load the xml file rather than the SQL
                    m_bLoadXML = true;
                }
                bLoadXMLMissing = false;
            }
        }
        //if we didn't load the XMLParsed variable
        if ( bLoadXMLMissing )
        {
            //Insert it
            m_pDatabaseManager->Execf ( m_hDbConnection, "INSERT INTO settings (key, value) VALUES(?,?)", SQLITE_TEXT, "XMLParsed", SQLITE_INTEGER, 0 );
            //Tell the Server to load the xml file rather than the SQL
            m_bLoadXML = true;
        }
    }

    //Check whether autologin was enabled in the main config
    m_bAutoLogin = g_pGame->GetConfig()->IsAutoLoginEnabled();
}