Ejemplo n.º 1
0
void CGameContext::OnClientConnected(int ClientID, bool Dummy)
{
	m_apPlayers[ClientID] = new(ClientID) CPlayer(this, ClientID, Dummy);
	
	if(Dummy)
		return;

	// send active vote
	if(m_VoteCloseTime)
		SendVoteSet(VOTE_UNKNOWN, ClientID);

	// send motd
	SendMotd(ClientID);

	// send settings
	SendSettings(ClientID);
}
Ejemplo n.º 2
0
void WorldSession::HandlePlayerReconnect()
{
    // stop logout timer if need
    LogoutRequest(0);

    // set loading flag
    m_playerLoading = true;

    // reset all visible objects to be able to resend them
    _player->m_clientGUIDs.clear();

    SetOnline();

    WorldPacket data(SMSG_LOGIN_VERIFY_WORLD, 20);
    data << _player->GetMapId();
    data << _player->GetPositionX();
    data << _player->GetPositionY();
    data << _player->GetPositionZ();
    data << _player->GetOrientation();
    SendPacket(data);

    data.Initialize(SMSG_ACCOUNT_DATA_TIMES, 128);
    for (int i = 0; i < 32; ++i)
        data << uint32(0);
    SendPacket(data);

    // Send Spam records
    SendExpectedSpamRecords();
    SendMotd(_player);

    if (_player->GetGuildId() != 0)
    {
        Guild* guild = sGuildMgr.GetGuildById(_player->GetGuildId());
        if (guild)
        {
            data.Initialize(SMSG_GUILD_EVENT, (1 + 1 + guild->GetMOTD().size() + 1));
            data << uint8(GE_MOTD);
            data << uint8(1);
            data << guild->GetMOTD();
            SendPacket(data);
            DEBUG_LOG("WORLD: Sent guild-motd (SMSG_GUILD_EVENT)");

            guild->BroadcastEvent(GE_SIGNED_ON, _player->GetObjectGuid(), _player->GetName());
        }
        else
        {
            // remove wrong guild data
            sLog.outError("Player %s (GUID: %u) marked as member of nonexistent guild (id: %u), removing guild membership for player.", _player->GetName(), _player->GetGUIDLow(), _player->GetGuildId());
            _player->SetInGuild(0);
        }
    }

    if (!_player->isAlive())
        _player->SendCorpseReclaimDelay(true);

    _player->SendInitialPacketsBeforeAddToMap();

    _player->GetMap()->CreatePlayerOnClient(_player);

    _player->GetSocial()->SendFriendList();
    _player->GetSocial()->SendIgnoreList();

    _player->SendInitWorldStates(_player->GetZoneId());
    _player->CastSpell(_player, 836, TRIGGERED_OLD_TRIGGERED);       // LOGINEFFECT
    _player->SendEnchantmentDurations();                             // must be after add to map
    _player->SendItemDurations();                                    // must be after add to map

    // announce group about member online (must be after add to player list to receive announce to self)
    if (Group* group = _player->GetGroup())
        group->UpdatePlayerOnlineStatus(_player);

    // friend status
    sSocialMgr.SendFriendStatus(_player, FRIEND_ONLINE, _player->GetObjectGuid(), true);

    // show time before shutdown if shutdown planned.
    if (sWorld.IsShutdowning())
        sWorld.ShutdownMsg(true, _player);

    if (sWorld.getConfig(CONFIG_BOOL_ALL_TAXI_PATHS))
        _player->SetTaxiCheater(true);

    if (_player->isGameMaster())
        SendNotification(LANG_GM_ON);

    std::string IP_str = GetRemoteAddress();
    sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid: %u)",
        GetAccountId(), IP_str.c_str(), _player->GetName(), _player->GetGUIDLow());

    // sync client auras timer
    _player->UpdateClientAuras();

    // sync client control (if taxi flying the client is already sync)
    if (_player->IsTaxiFlying())
        _player->TaxiFlightResume(true);
    else if (!_player->IsClientControlled(_player))
        _player->UpdateClientControl(_player, false);

    // initialize client pet bar if need
    _player->SendPetBar();

    // send mirror timers
    _player->SendMirrorTimers(true);

    m_playerLoading = false;
}
Ejemplo n.º 3
0
void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
{
    ObjectGuid playerGuid = holder->GetGuid();

    Player* pCurrChar = new Player(this);
    pCurrChar->GetMotionMaster()->Initialize();
    SetPlayer(pCurrChar);
    m_playerLoading = true;

    SetOnline();

    // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
    if (!pCurrChar->LoadFromDB(playerGuid, holder))
    {
        KickPlayer();                                       // disconnect client, player no set to session and it will not deleted or saved at kick
        delete pCurrChar;                                   // delete it manually
        delete holder;                                      // delete all unprocessed queries
        m_playerLoading = false;

        sLog.outError("HandlePlayerLogin> LoadFromDB failed to load %s, AccountId = %u", pCurrChar->GetGuidStr().c_str(), GetAccountId());

        WorldPacket data(SMSG_CHARACTER_LOGIN_FAILED, 1);
        data << (uint8)CHAR_LOGIN_NO_CHARACTER;
        SendPacket(data, true);
        return;
    }

    WorldPacket data(SMSG_LOGIN_VERIFY_WORLD, 20);
    data << pCurrChar->GetMapId();
    data << pCurrChar->GetPositionX();
    data << pCurrChar->GetPositionY();
    data << pCurrChar->GetPositionZ();
    data << pCurrChar->GetOrientation();
    SendPacket(data);

    data.Initialize(SMSG_ACCOUNT_DATA_TIMES, 128);
    for (int i = 0; i < 32; ++i)
        data << uint32(0);
    SendPacket(data);

    // Send Spam records
    SendExpectedSpamRecords();
    SendMotd(pCurrChar);

    // QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow());
    QueryResult* resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD);

    if (resultGuild)
    {
        Field* fields = resultGuild->Fetch();
        pCurrChar->SetInGuild(fields[0].GetUInt32());
        pCurrChar->SetRank(fields[1].GetUInt32());
        delete resultGuild;
    }
    else if (pCurrChar->GetGuildId())                       // clear guild related fields in case wrong data about nonexistent membership
    {
        pCurrChar->SetInGuild(0);
        pCurrChar->SetRank(0);
    }

    if (pCurrChar->GetGuildId() != 0)
    {
        Guild* guild = sGuildMgr.GetGuildById(pCurrChar->GetGuildId());
        if (guild)
        {
            data.Initialize(SMSG_GUILD_EVENT, (1 + 1 + guild->GetMOTD().size() + 1));
            data << uint8(GE_MOTD);
            data << uint8(1);
            data << guild->GetMOTD();
            SendPacket(data);
            DEBUG_LOG("WORLD: Sent guild-motd (SMSG_GUILD_EVENT)");

            guild->BroadcastEvent(GE_SIGNED_ON, pCurrChar->GetObjectGuid(), pCurrChar->GetName());
        }
        else
        {
            // remove wrong guild data
            sLog.outError("Player %s (GUID: %u) marked as member of nonexistent guild (id: %u), removing guild membership for player.", pCurrChar->GetName(), pCurrChar->GetGUIDLow(), pCurrChar->GetGuildId());
            pCurrChar->SetInGuild(0);
        }
    }

    if (!pCurrChar->isAlive())
        pCurrChar->SendCorpseReclaimDelay(true);

    pCurrChar->SendInitialPacketsBeforeAddToMap();

    // Show cinematic at the first time that player login
    if (!pCurrChar->getCinematic())
    {
        pCurrChar->setCinematic(1);

        if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()))
            pCurrChar->SendCinematicStart(rEntry->CinematicSequence);
    }

    uint32 miscRequirement = 0;
    AreaLockStatus lockStatus = AREA_LOCKSTATUS_OK;
    if (AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(pCurrChar->GetMapId()))
        lockStatus = pCurrChar->GetAreaTriggerLockStatus(at, miscRequirement);
    else
    {
        // Some basic checks in case of a map without areatrigger
        MapEntry const* mapEntry = sMapStore.LookupEntry(pCurrChar->GetMapId());
        if (!mapEntry)
            lockStatus = AREA_LOCKSTATUS_UNKNOWN_ERROR;
    }
    if (lockStatus != AREA_LOCKSTATUS_OK || !pCurrChar->GetMap()->Add(pCurrChar))
    {
        // normal delayed teleport protection not applied (and this correct) for this case (Player object just created)
        AreaTrigger const* at = sObjectMgr.GetGoBackTrigger(pCurrChar->GetMapId());
        if (at)
            lockStatus = pCurrChar->GetAreaTriggerLockStatus(at, miscRequirement);
        if (!at || lockStatus != AREA_LOCKSTATUS_OK || !pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation()))
            pCurrChar->TeleportToHomebind();
    }

    sObjectAccessor.AddObject(pCurrChar);
    // DEBUG_LOG("Player %s added to Map.",pCurrChar->GetName());
    pCurrChar->GetSocial()->SendFriendList();
    pCurrChar->GetSocial()->SendIgnoreList();

    pCurrChar->SendInitialPacketsAfterAddToMap();

    static SqlStatementID updChars;
    static SqlStatementID updAccount;

    SqlStatement stmt = CharacterDatabase.CreateStatement(updChars, "UPDATE characters SET online = 1 WHERE guid = ?");
    stmt.PExecute(pCurrChar->GetGUIDLow());

    stmt = LoginDatabase.CreateStatement(updAccount, "UPDATE account SET active_realm_id = ? WHERE id = ?");
    stmt.PExecute(realmID, GetAccountId());

    pCurrChar->SetInGameTime(WorldTimer::getMSTime());

    // announce group about member online (must be after add to player list to receive announce to self)
    if (Group* group = pCurrChar->GetGroup())
        group->UpdatePlayerOnlineStatus(pCurrChar);

    // friend status
    sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetObjectGuid(), true);

    // Place character in world (and load zone) before some object loading
    pCurrChar->LoadCorpse();

    // setting Ghost+speed if dead
    if (pCurrChar->m_deathState != ALIVE)
    {
        // not blizz like, we must correctly save and load player instead...
        if (pCurrChar->getRace() == RACE_NIGHTELF)
            pCurrChar->CastSpell(pCurrChar, 20584, TRIGGERED_OLD_TRIGGERED);   // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
        pCurrChar->CastSpell(pCurrChar, 8326, TRIGGERED_OLD_TRIGGERED);        // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)

        pCurrChar->SetWaterWalk(true);
    }

    pCurrChar->TaxiFlightResume();

    // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
    pCurrChar->LoadPet();

    // Set FFA PvP for non GM in non-rest mode
    if (sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
        pCurrChar->SetPvPFreeForAll(true);

    if (pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
        pCurrChar->UpdatePvPContested(true);

    // Apply at_login requests
    if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
    {
        pCurrChar->resetSpells();
        SendNotification(LANG_RESET_SPELLS);
    }

    if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
    {
        pCurrChar->resetTalents(true);
        SendNotification(LANG_RESET_TALENTS);               // we can use SMSG_TALENTS_INVOLUNTARILY_RESET here
    }

    if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TAXINODES))
    {
        pCurrChar->InitTaxiNodes();
        pCurrChar->RemoveAtLoginFlag(AT_LOGIN_RESET_TAXINODES, true);
        SendNotification("Your taxi nodes have been reset.");
    }

    if (pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST))
        pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST);

    // show time before shutdown if shutdown planned.
    if (sWorld.IsShutdowning())
        sWorld.ShutdownMsg(true, pCurrChar);

    if (sWorld.getConfig(CONFIG_BOOL_ALL_TAXI_PATHS))
        pCurrChar->SetTaxiCheater(true);

    if (pCurrChar->isGameMaster())
        SendNotification(LANG_GM_ON);

    if (!pCurrChar->isGMVisible())
    {
        SendNotification(LANG_INVISIBLE_INVISIBLE);
        SpellEntry const* invisibleAuraInfo = sSpellTemplate.LookupEntry<SpellEntry>(sWorld.getConfig(CONFIG_UINT32_GM_INVISIBLE_AURA));
        if (invisibleAuraInfo && IsSpellAppliesAura(invisibleAuraInfo))
            pCurrChar->CastSpell(pCurrChar, invisibleAuraInfo, TRIGGERED_OLD_TRIGGERED);
    }

    std::string IP_str = GetRemoteAddress();
    sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid: %u)",
                 GetAccountId(), IP_str.c_str(), pCurrChar->GetName(), pCurrChar->GetGUIDLow());

    if (!pCurrChar->IsStandState() && !pCurrChar->hasUnitState(UNIT_STAT_STUNNED))
        pCurrChar->SetStandState(UNIT_STAND_STATE_STAND);

    m_playerLoading = false;
    delete holder;
}
Ejemplo n.º 4
0
void CClient::UserCommand(CString& sLine) {
	if (!m_pUser) {
		return;
	}

	if (sLine.empty()) {
		return;
	}

	NETWORKMODULECALL(OnStatusCommand(sLine), m_pUser, m_pNetwork, this, return);

	const CString sCommand = sLine.Token(0);

	if (sCommand.Equals("HELP")) {
		HelpUser();
	} else if (sCommand.Equals("LISTNICKS")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CString sChan = sLine.Token(1);

		if (sChan.empty()) {
			PutStatus("Usage: ListNicks <#chan>");
			return;
		}

		CChan* pChan = m_pNetwork->FindChan(sChan);

		if (!pChan) {
			PutStatus("You are not on [" + sChan + "]");
			return;
		}

		if (!pChan->IsOn()) {
			PutStatus("You are not on [" + sChan + "] [trying]");
			return;
		}

		const map<CString,CNick>& msNicks = pChan->GetNicks();
		CIRCSock* pIRCSock = m_pNetwork->GetIRCSock();
		const CString& sPerms = (pIRCSock) ? pIRCSock->GetPerms() : "";

		if (!msNicks.size()) {
			PutStatus("No nicks on [" + sChan + "]");
			return;
		}

		CTable Table;

		for (unsigned int p = 0; p < sPerms.size(); p++) {
			CString sPerm;
			sPerm += sPerms[p];
			Table.AddColumn(sPerm);
		}

		Table.AddColumn("Nick");
		Table.AddColumn("Ident");
		Table.AddColumn("Host");

		for (map<CString,CNick>::const_iterator a = msNicks.begin(); a != msNicks.end(); ++a) {
			Table.AddRow();

			for (unsigned int b = 0; b < sPerms.size(); b++) {
				if (a->second.HasPerm(sPerms[b])) {
					CString sPerm;
					sPerm += sPerms[b];
					Table.SetCell(sPerm, sPerm);
				}
			}

			Table.SetCell("Nick", a->second.GetNick());
			Table.SetCell("Ident", a->second.GetIdent());
			Table.SetCell("Host", a->second.GetHost());
		}

		PutStatus(Table);
	} else if (sCommand.Equals("DETACH")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CString sChan = sLine.Token(1);

		if (sChan.empty()) {
			PutStatus("Usage: Detach <#chan>");
			return;
		}

		const vector<CChan*>& vChans = m_pNetwork->GetChans();
		vector<CChan*>::const_iterator it;
		unsigned int uMatches = 0, uDetached = 0;
		for (it = vChans.begin(); it != vChans.end(); ++it) {
			if (!(*it)->GetName().WildCmp(sChan))
				continue;
			uMatches++;

			if ((*it)->IsDetached())
				continue;
			uDetached++;
			(*it)->DetachUser();
		}

		PutStatus("There were [" + CString(uMatches) + "] channels matching [" + sChan + "]");
		PutStatus("Detached [" + CString(uDetached) + "] channels");
	} else if (sCommand.Equals("VERSION")) {
		PutStatus(CZNC::GetTag());
		PutStatus(CZNC::GetCompileOptionsString());
	} else if (sCommand.Equals("MOTD") || sCommand.Equals("ShowMOTD")) {
		if (!SendMotd()) {
			PutStatus("There is no MOTD set.");
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("Rehash")) {
		CString sRet;

		if (CZNC::Get().RehashConfig(sRet)) {
			PutStatus("Rehashing succeeded!");
		} else {
			PutStatus("Rehashing failed: " + sRet);
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("SaveConfig")) {
		if (CZNC::Get().WriteConfig()) {
			PutStatus("Wrote config to [" + CZNC::Get().GetConfigFile() + "]");
		} else {
			PutStatus("Error while trying to write config.");
		}
	} else if (sCommand.Equals("LISTCLIENTS")) {
		CUser* pUser = m_pUser;
		CString sNick = sLine.Token(1);

		if (!sNick.empty()) {
			if (!m_pUser->IsAdmin()) {
				PutStatus("Usage: ListClients");
				return;
			}

			pUser = CZNC::Get().FindUser(sNick);

			if (!pUser) {
				PutStatus("No such user [" + sNick + "]");
				return;
			}
		}

		vector<CClient*> vClients = pUser->GetAllClients();

		if (vClients.empty()) {
			PutStatus("No clients are connected");
			return;
		}

		CTable Table;
		Table.AddColumn("Host");
		Table.AddColumn("Network");

		for (unsigned int a = 0; a < vClients.size(); a++) {
			Table.AddRow();
			Table.SetCell("Host", vClients[a]->GetRemoteIP());
			if (vClients[a]->GetNetwork()) {
				Table.SetCell("Network", vClients[a]->GetNetwork()->GetName());
			}
		}

		PutStatus(Table);
	} else if (m_pUser->IsAdmin() && sCommand.Equals("LISTUSERS")) {
		const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
		CTable Table;
		Table.AddColumn("Username");
		Table.AddColumn("Networks");
		Table.AddColumn("Clients");

		for (map<CString, CUser*>::const_iterator it = msUsers.begin(); it != msUsers.end(); ++it) {
			Table.AddRow();
			Table.SetCell("Username", it->first);
			Table.SetCell("Networks", CString(it->second->GetNetworks().size()));
			Table.SetCell("Clients", CString(it->second->GetAllClients().size()));
		}

		PutStatus(Table);
	} else if (m_pUser->IsAdmin() && sCommand.Equals("SetMOTD")) {
		CString sMessage = sLine.Token(1, true);

		if (sMessage.empty()) {
			PutStatus("Usage: SetMOTD <Message>");
		} else {
			CZNC::Get().SetMotd(sMessage);
			PutStatus("MOTD set to [" + sMessage + "]");
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("AddMOTD")) {
		CString sMessage = sLine.Token(1, true);

		if (sMessage.empty()) {
			PutStatus("Usage: AddMOTD <Message>");
		} else {
			CZNC::Get().AddMotd(sMessage);
			PutStatus("Added [" + sMessage + "] to MOTD");
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("ClearMOTD")) {
		CZNC::Get().ClearMotd();
		PutStatus("Cleared MOTD");
	} else if (m_pUser->IsAdmin() && sCommand.Equals("BROADCAST")) {
		CZNC::Get().Broadcast(sLine.Token(1, true));
	} else if (m_pUser->IsAdmin() && (sCommand.Equals("SHUTDOWN") || sCommand.Equals("RESTART"))) {
		bool bRestart = sCommand.Equals("RESTART");
		CString sMessage = sLine.Token(1, true);
		bool bForce = false;

		if (sMessage.Token(0).Equals("FORCE")) {
			bForce = true;
			sMessage = sMessage.Token(1, true);
		}

		if (sMessage.empty()) {
			sMessage = (bRestart ? "ZNC is being restarted NOW!" : "ZNC is being shut down NOW!");
		}

		if(!CZNC::Get().WriteConfig() && !bForce) {
			PutStatus("ERROR: Writing config file to disk failed! Aborting. Use " +
				sCommand.AsUpper() + " FORCE to ignore.");
		} else {
			CZNC::Get().Broadcast(sMessage);
			throw CException(bRestart ? CException::EX_Restart : CException::EX_Shutdown);
		}
	} else if (sCommand.Equals("JUMP") || sCommand.Equals("CONNECT")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		if (!m_pNetwork->HasServers()) {
			PutStatus("You don't have any servers added.");
			return;
		}

		CString sArgs = sLine.Token(1, true);
		CServer *pServer = NULL;

		if (!sArgs.empty()) {
			pServer = m_pNetwork->FindServer(sArgs);
			if (!pServer) {
				PutStatus("Server [" + sArgs + "] not found");
				return;
			}
			m_pNetwork->SetNextServer(pServer);

			// If we are already connecting to some server,
			// we have to abort that attempt
			Csock *pIRCSock = GetIRCSock();
			if (pIRCSock && !pIRCSock->IsConnected()) {
				pIRCSock->Close();
			}
		}

		if (GetIRCSock()) {
			GetIRCSock()->Quit();
			if (pServer)
				PutStatus("Connecting to [" + pServer->GetName() + "]...");
			else
				PutStatus("Jumping to the next server in the list...");
		} else {
			if (pServer)
				PutStatus("Connecting to [" + pServer->GetName() + "]...");
			else
				PutStatus("Connecting...");
		}

		m_pNetwork->SetIRCConnectEnabled(true);
		return;
	} else if (sCommand.Equals("DISCONNECT")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		if (GetIRCSock()) {
			CString sQuitMsg = sLine.Token(1, true);
			GetIRCSock()->Quit(sQuitMsg);
		}

		m_pNetwork->SetIRCConnectEnabled(false);
		PutStatus("Disconnected from IRC. Use 'connect' to reconnect.");
		return;
	} else if (sCommand.Equals("ENABLECHAN")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CString sChan = sLine.Token(1, true);

		if (sChan.empty()) {
			PutStatus("Usage: EnableChan <channel>");
		} else {
			const vector<CChan*>& vChans = m_pNetwork->GetChans();
			vector<CChan*>::const_iterator it;
			unsigned int uMatches = 0, uEnabled = 0;
			for (it = vChans.begin(); it != vChans.end(); ++it) {
				if (!(*it)->GetName().WildCmp(sChan))
					continue;
				uMatches++;

				if (!(*it)->IsDisabled())
					continue;
				uEnabled++;
				(*it)->Enable();
			}

			PutStatus("There were [" + CString(uMatches) + "] channels matching [" + sChan + "]");
			PutStatus("Enabled [" + CString(uEnabled) + "] channels");
		}
	} else if (sCommand.Equals("LISTCHANS")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CUser* pUser = m_pUser;
		CIRCNetwork* pNetwork = m_pNetwork;

		const CString sNick = sLine.Token(1);
		const CString sNetwork = sLine.Token(2);

		if (!sNick.empty()) {
			if (!m_pUser->IsAdmin()) {
				PutStatus("Usage: ListChans");
				return;
			}

			pUser = CZNC::Get().FindUser(sNick);

			if (!pUser) {
				PutStatus("No such user [" + sNick + "]");
				return;
			}

			pNetwork = pUser->FindNetwork(sNetwork);
			if (!pNetwork) {
				PutStatus("No such network for user [" + sNetwork + "]");
				return;
			}
		}

		const vector<CChan*>& vChans = pNetwork->GetChans();
		CIRCSock* pIRCSock = pNetwork->GetIRCSock();
		const CString& sPerms = (pIRCSock) ? pIRCSock->GetPerms() : "";

		if (!vChans.size()) {
			PutStatus("There are no channels defined.");
			return;
		}

		CTable Table;
		Table.AddColumn("Name");
		Table.AddColumn("Status");
		Table.AddColumn("Conf");
		Table.AddColumn("Buf");
		Table.AddColumn("Modes");
		Table.AddColumn("Users");

		for (unsigned int p = 0; p < sPerms.size(); p++) {
			CString sPerm;
			sPerm += sPerms[p];
			Table.AddColumn(sPerm);
		}

		unsigned int uNumDetached = 0, uNumDisabled = 0,
			uNumJoined = 0;

		for (unsigned int a = 0; a < vChans.size(); a++) {
			const CChan* pChan = vChans[a];
			Table.AddRow();
			Table.SetCell("Name", pChan->GetPermStr() + pChan->GetName());
			Table.SetCell("Status", ((vChans[a]->IsOn()) ? ((vChans[a]->IsDetached()) ? "Detached" : "Joined") : ((vChans[a]->IsDisabled()) ? "Disabled" : "Trying")));
			Table.SetCell("Conf", CString((pChan->InConfig()) ? "yes" : ""));
			Table.SetCell("Buf", CString((pChan->KeepBuffer()) ? "*" : "") + CString(pChan->GetBufferCount()));
			Table.SetCell("Modes", pChan->GetModeString());
			Table.SetCell("Users", CString(pChan->GetNickCount()));

			map<char, unsigned int> mPerms = pChan->GetPermCounts();
			for (unsigned int b = 0; b < sPerms.size(); b++) {
				char cPerm = sPerms[b];
				Table.SetCell(CString(cPerm), CString(mPerms[cPerm]));
			}

			if(pChan->IsDetached()) uNumDetached++;
			if(pChan->IsOn()) uNumJoined++;
			if(pChan->IsDisabled()) uNumDisabled++;
		}

		PutStatus(Table);
		PutStatus("Total: " + CString(vChans.size()) + " - Joined: " + CString(uNumJoined) +
			" - Detached: " + CString(uNumDetached) + " - Disabled: " + CString(uNumDisabled));
	} else if (sCommand.Equals("ADDNETWORK")) {
#ifndef ENABLE_ADD_NETWORK
		if (!m_pUser->IsAdmin()) {
			PutStatus("Permission denied");
			return;
		}
#endif

		CString sNetwork = sLine.Token(1);

		if (sNetwork.empty()) {
			PutStatus("Usage: AddNetwork <name>");
			return;
		}

		if (m_pUser->AddNetwork(sNetwork)) {
			PutStatus("Network added. Use /znc JumpNetwork " + sNetwork + ", or connect to ZNC with username " + m_pUser->GetUserName() + "/" + sNetwork + " (instead of just " + m_pUser->GetUserName() + ") to connect to it.");
		} else {
			PutStatus("Unable to add that network");
			PutStatus("Perhaps that network is already added");
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("DELNETWORK")) {
		CString sNetwork = sLine.Token(1);

		if (sNetwork.empty()) {
			PutStatus("Usage: DelNetwork <name>");
			return;
		}

		if (m_pNetwork && m_pNetwork->GetName().Equals(sNetwork)) {
			SetNetwork(NULL);
		}

		if (m_pUser->DeleteNetwork(sNetwork)) {
			PutStatus("Network deleted");
		} else {
			PutStatus("Failed to delete network");
			PutStatus("Perhaps this network doesn't exist");
		}
	} else if (sCommand.Equals("LISTNETWORKS")) {
		CUser *pUser = m_pUser;

		if (m_pUser->IsAdmin() && !sLine.Token(1).empty()) {
			pUser = CZNC::Get().FindUser(sLine.Token(1));

			if (!pUser) {
				PutStatus("User not found " + sLine.Token(1));
				return;
			}
		}

		const vector<CIRCNetwork*>& vNetworks = pUser->GetNetworks();

		CTable Table;
		Table.AddColumn("Network");
		Table.AddColumn("OnIRC");
		Table.AddColumn("IRC Server");
		Table.AddColumn("IRC User");
		Table.AddColumn("Channels");

		for (unsigned int a = 0; a < vNetworks.size(); a++) {
			CIRCNetwork* pNetwork = vNetworks[a];
			Table.AddRow();
			Table.SetCell("Network", pNetwork->GetName());
			if (pNetwork->IsIRCConnected()) {
				Table.SetCell("OnIRC", "Yes");
				Table.SetCell("IRC Server", pNetwork->GetIRCServer());
				Table.SetCell("IRC User", pNetwork->GetIRCNick().GetNickMask());
				Table.SetCell("Channels", CString(pNetwork->GetChans().size()));
			} else {
				Table.SetCell("OnIRC", "No");
			}
		}

		if (PutStatus(Table) == 0) {
			PutStatus("No networks");
		}
	} else if (sCommand.Equals("JUMPNETWORK")) {
		CString sNetwork = sLine.Token(1);

		if (sNetwork.empty()) {
			PutStatus("No network supplied.");
			return;
		}

		if (m_pNetwork && (m_pNetwork->GetName() == sNetwork)) {
			PutStatus("You are already connected with this network.");
			return;
		}

		CIRCNetwork *pNetwork = m_pUser->FindNetwork(sNetwork);
		if (pNetwork) {
			PutStatus("Switched to " + sNetwork);
			SetNetwork(pNetwork);
		} else {
			PutStatus("You don't have a network named " + sNetwork);
		}
		} else if (sCommand.Equals("MODE")) {
		CString sNetwork = sLine.Token(1);

		if (sNetwork.empty()) {
			PutStatus("Syntax: MODE <support/default>");
			return;
		}

		if (m_pNetwork && (m_pNetwork->GetName() == sNetwork)) {
			PutStatus("You are already in this mode.");
			return;
		}

		CIRCNetwork *pNetwork = m_pUser->FindNetwork(sNetwork);
		if (pNetwork) {
			PutStatus("You are now in " + sNetwork + " mode.");
			SetNetwork(pNetwork);
		} else {
			PutStatus("ERROR! Contact GeekBouncer admins immediately");
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("ADDSERVER")) {
		CString sServer = sLine.Token(1);

		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		if (sServer.empty()) {
			PutStatus("Usage: AddServer <host> [[+]port] [pass]");
			return;
		}

		if (m_pNetwork->AddServer(sLine.Token(1, true))) {
			PutStatus("Server added");
		} else {
			PutStatus("Unable to add that server");
			PutStatus("Perhaps the server is already added or openssl is disabled?");
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("REMSERVER") || sCommand.Equals("DELSERVER")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}
		
		CString sServer = sLine.Token(1);
		unsigned short uPort = sLine.Token(2).ToUShort();
		CString sPass = sLine.Token(3);

		if (sServer.empty()) {
			PutStatus("Usage: RemServer <host> [port] [pass]");
			return;
		}

		if (!m_pNetwork->HasServers()) {
			PutStatus("You don't have any servers added.");
			return;
		}

		if (m_pNetwork->DelServer(sServer, uPort, sPass)) {
			PutStatus("Server removed");
		} else {
			PutStatus("No such server");
		}
	} else if (sCommand.Equals("LISTSERVERS")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		if (m_pNetwork->HasServers()) {
			const vector<CServer*>& vServers = m_pNetwork->GetServers();
			CServer* pCurServ = m_pNetwork->GetCurrentServer();
			CTable Table;
			Table.AddColumn("Host");
			Table.AddColumn("Port");
			Table.AddColumn("SSL");
			Table.AddColumn("Pass");

			for (unsigned int a = 0; a < vServers.size(); a++) {
				CServer* pServer = vServers[a];
				Table.AddRow();
				Table.SetCell("Host", pServer->GetName() + (pServer == pCurServ ? "*" : ""));
				Table.SetCell("Port", CString(pServer->GetPort()));
				Table.SetCell("SSL", (pServer->IsSSL()) ? "SSL" : "");
				Table.SetCell("Pass", pServer->GetPass());
			}

			PutStatus(Table);
		} else {
			PutStatus("You don't have any servers added.");
		}
	} else if (sCommand.Equals("TOPICS")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		const vector<CChan*>& vChans = m_pNetwork->GetChans();
		CTable Table;
		Table.AddColumn("Name");
		Table.AddColumn("Set By");
		Table.AddColumn("Topic");

		for (unsigned int a = 0; a < vChans.size(); a++) {
			CChan* pChan = vChans[a];
			Table.AddRow();
			Table.SetCell("Name", pChan->GetName());
			Table.SetCell("Set By", pChan->GetTopicOwner());
			Table.SetCell("Topic", pChan->GetTopic());
		}

		PutStatus(Table);
	} else if (sCommand.Equals("LISTMODS") || sCommand.Equals("LISTMODULES")) {
		if (m_pUser->IsAdmin()) {
			CModules& GModules = CZNC::Get().GetModules();

			if (!GModules.size()) {
				PutStatus("No global modules loaded.");
			} else {
				PutStatus("Global modules:");
				CTable GTable;
				GTable.AddColumn("Name");
				GTable.AddColumn("Arguments");

				for (unsigned int b = 0; b < GModules.size(); b++) {
					GTable.AddRow();
					GTable.SetCell("Name", GModules[b]->GetModName());
					GTable.SetCell("Arguments", GModules[b]->GetArgs());
				}

				PutStatus(GTable);
			}
		}

		CModules& Modules = m_pUser->GetModules();

		if (!Modules.size()) {
			PutStatus("Your user has no modules loaded.");
		} else {
			PutStatus("User modules:");
			CTable Table;
			Table.AddColumn("Name");
			Table.AddColumn("Arguments");

			for (unsigned int b = 0; b < Modules.size(); b++) {
				Table.AddRow();
				Table.SetCell("Name", Modules[b]->GetModName());
				Table.SetCell("Arguments", Modules[b]->GetArgs());
			}

			PutStatus(Table);
		}

		if (m_pNetwork) {
			CModules& NetworkModules = m_pNetwork->GetModules();
			if (NetworkModules.empty()) {
				PutStatus("This network has no modules loaded.");
			} else {
				PutStatus("Network modules:");
				CTable Table;
				Table.AddColumn("Name");
				Table.AddColumn("Arguments");

				for (unsigned int b = 0; b < NetworkModules.size(); b++) {
					Table.AddRow();
					Table.SetCell("Name", NetworkModules[b]->GetModName());
					Table.SetCell("Arguments", NetworkModules[b]->GetArgs());
				}

				PutStatus(Table);
			}
		}

		return;
	} else if (sCommand.Equals("LISTAVAILMODS") || sCommand.Equals("LISTAVAILABLEMODULES")) {
		if (m_pUser->DenyLoadMod()) {
			PutStatus("Access Denied.");
			return;
		}

		if (m_pUser->IsAdmin()) {
			set<CModInfo> ssGlobalMods;
			CZNC::Get().GetModules().GetAvailableMods(ssGlobalMods, CModInfo::GlobalModule);

			if (ssGlobalMods.empty()) {
				PutStatus("No global modules available.");
			} else {
				PutStatus("Global modules:");
				CTable GTable;
				GTable.AddColumn("Name");
				GTable.AddColumn("Description");
				set<CModInfo>::iterator it;

				for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) {
					const CModInfo& Info = *it;
					GTable.AddRow();
					GTable.SetCell("Name", (CZNC::Get().GetModules().FindModule(Info.GetName()) ? "*" : " ") + Info.GetName());
					GTable.SetCell("Description", Info.GetDescription().Ellipsize(128));
				}

				PutStatus(GTable);
			}
		}

		set<CModInfo> ssUserMods;
		CZNC::Get().GetModules().GetAvailableMods(ssUserMods);

		if (!ssUserMods.size()) {
			PutStatus("No user modules available.");
		} else {
			PutStatus("User modules:");
			CTable Table;
			Table.AddColumn("Name");
			Table.AddColumn("Description");
			set<CModInfo>::iterator it;

			for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) {
				const CModInfo& Info = *it;
				Table.AddRow();
				Table.SetCell("Name", (m_pUser->GetModules().FindModule(Info.GetName()) ? "*" : " ") + Info.GetName());
				Table.SetCell("Description", Info.GetDescription().Ellipsize(128));
			}

			PutStatus(Table);
		}

		set<CModInfo> ssNetworkMods;
		CZNC::Get().GetModules().GetAvailableMods(ssNetworkMods, CModInfo::NetworkModule);

		if (!ssNetworkMods.size()) {
			PutStatus("No network modules available.");
		} else {
			PutStatus("Network modules:");
			CTable Table;
			Table.AddColumn("Name");
			Table.AddColumn("Description");
			set<CModInfo>::const_iterator it;

			for (it = ssNetworkMods.begin(); it != ssNetworkMods.end(); ++it) {
				const CModInfo& Info = *it;
				Table.AddRow();
				Table.SetCell("Name", ((m_pNetwork && m_pNetwork->GetModules().FindModule(Info.GetName())) ? "*" : " ") + Info.GetName());
				Table.SetCell("Description", Info.GetDescription().Ellipsize(128));
			}

			PutStatus(Table);
		}
		return;
	} else if (sCommand.Equals("LOADMOD") || sCommand.Equals("LOADMODULE")) {
		CModInfo::EModuleType eType;
		CString sType = sLine.Token(1);
		CString sMod = sLine.Token(2);
		CString sArgs = sLine.Token(3, true);

		if (sType.Equals("global")) {
			eType = CModInfo::GlobalModule;
		} else if (sType.Equals("user")) {
			eType = CModInfo::UserModule;
		} else if (sType.Equals("network")) {
			eType = CModInfo::NetworkModule;
		} else {
			sMod = sType;
			sArgs = sLine.Token(2, true);
			sType = "default";
			// Will be set correctly later
			eType = CModInfo::UserModule;
		}

		if (m_pUser->DenyLoadMod()) {
			PutStatus("Unable to load [" + sMod + "]: Access Denied.");
			return;
		}

		if (sMod.empty()) {
			PutStatus("Usage: LoadMod [type] <module> [args]");
			return;
		}

		CModInfo ModInfo;
		CString sRetMsg;
		if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) {
			PutStatus("Unable to find modinfo [" + sMod + "] [" + sRetMsg + "]");
			return;
		}

		if (sType.Equals("default")) {
			eType = ModInfo.GetDefaultType();
		}

		if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) {
			PutStatus("Unable to load global module [" + sMod + "]: Access Denied.");
			return;
		}

		if (eType == CModInfo::NetworkModule && !m_pNetwork) {
			PutStatus("Unable to load network module [" + sMod + "] Not connected with a network.");
			return;
		}

		CString sModRet;
		bool b = false;

		switch (eType) {
		case CModInfo::GlobalModule:
			b = CZNC::Get().GetModules().LoadModule(sMod, sArgs, eType, NULL, NULL, sModRet);
			break;
		case CModInfo::UserModule:
			b = m_pUser->GetModules().LoadModule(sMod, sArgs, eType, m_pUser, NULL, sModRet);
			break;
		case CModInfo::NetworkModule:
				b = m_pNetwork->GetModules().LoadModule(sMod, sArgs, eType, m_pUser, m_pNetwork, sModRet);
				break;
		default:
			sModRet = "Unable to load module [" + sMod + "]: Unknown module type";
		}

		if (b)
			sModRet = "Loaded module [" + sMod + "] " + sModRet;

		PutStatus(sModRet);
		return;
	} else if (sCommand.Equals("UNLOADMOD") || sCommand.Equals("UNLOADMODULE")) {
		CModInfo::EModuleType eType = CModInfo::UserModule;
		CString sType = sLine.Token(1);
		CString sMod = sLine.Token(2);

		if (sType.Equals("global")) {
			eType = CModInfo::GlobalModule;
		} else if (sType.Equals("user")) {
			eType = CModInfo::UserModule;
		} else if (sType.Equals("network")) {
			eType = CModInfo::NetworkModule;
		} else {
			sMod = sType;
			sType = "default";
		}

		if (m_pUser->DenyLoadMod()) {
			PutStatus("Unable to unload [" + sMod + "] Access Denied.");
			return;
		}

		if (sMod.empty()) {
			PutStatus("Usage: UnloadMod [type] <module>");
			return;
		}

		if (sType.Equals("default")) {
			CModInfo ModInfo;
			CString sRetMsg;
			if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) {
				PutStatus("Unable to find modinfo [" + sMod + "] [" + sRetMsg + "]");
				return;
			}

			eType = ModInfo.GetDefaultType();
		}

		if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) {
			PutStatus("Unable to unload global module [" + sMod + "]: Access Denied.");
			return;
		}

		if (eType == CModInfo::NetworkModule && !m_pNetwork) {
			PutStatus("Unable to unload network module [" + sMod + "] Not connected with a network.");
			return;
		}

		CString sModRet;

		switch (eType) {
		case CModInfo::GlobalModule:
			CZNC::Get().GetModules().UnloadModule(sMod, sModRet);
			break;
		case CModInfo::UserModule:
			m_pUser->GetModules().UnloadModule(sMod, sModRet);
			break;
		case CModInfo::NetworkModule:
			m_pNetwork->GetModules().UnloadModule(sMod, sModRet);
			break;
		default:
			sModRet = "Unable to unload module [" + sMod + "]: Unknown module type";
		}

		PutStatus(sModRet);
		return;
	} else if (sCommand.Equals("RELOADMOD") || sCommand.Equals("RELOADMODULE")) {
		CModInfo::EModuleType eType;
		CString sType = sLine.Token(1);
		CString sMod = sLine.Token(2);
		CString sArgs = sLine.Token(3, true);

		if (m_pUser->DenyLoadMod()) {
			PutStatus("Unable to reload modules. Access Denied.");
			return;
		}

		if (sType.Equals("global")) {
			eType = CModInfo::GlobalModule;
		} else if (sType.Equals("user")) {
			eType = CModInfo::UserModule;
		} else if (sType.Equals("network")) {
			eType = CModInfo::NetworkModule;
		} else {
			sMod = sType;
			sArgs = sLine.Token(2, true);
			sType = "default";
			// Will be set correctly later
			eType = CModInfo::UserModule;
		}

		if (sMod.empty()) {
			PutStatus("Usage: ReloadMod [type] <module> [args]");
			return;
		}

		if (sType.Equals("default")) {
			CModInfo ModInfo;
			CString sRetMsg;
			if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) {
				PutStatus("Unable to find modinfo for [" + sMod + "] [" + sRetMsg + "]");
				return;
			}

			eType = ModInfo.GetDefaultType();
		}

		if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) {
			PutStatus("Unable to reload global module [" + sMod + "]: Access Denied.");
			return;
		}

		if (eType == CModInfo::NetworkModule && !m_pNetwork) {
			PutStatus("Unable to load network module [" + sMod + "] Not connected with a network.");
			return;
		}

		CString sModRet;

		switch (eType) {
		case CModInfo::GlobalModule:
			CZNC::Get().GetModules().ReloadModule(sMod, sArgs, NULL, NULL, sModRet);
			break;
		case CModInfo::UserModule:
			m_pUser->GetModules().ReloadModule(sMod, sArgs, m_pUser, NULL, sModRet);
			break;
		case CModInfo::NetworkModule:
			m_pNetwork->GetModules().ReloadModule(sMod, sArgs, m_pUser, m_pNetwork, sModRet);
			break;
		default:
			sModRet = "Unable to reload module [" + sMod + "]: Unknown module type";
		}

		PutStatus(sModRet);
		return;
	} else if ((sCommand.Equals("UPDATEMOD") || sCommand.Equals("UPDATEMODULE")) && m_pUser->IsAdmin() ) {
		CString sMod = sLine.Token(1);

		if (sMod.empty()) {
			PutStatus("Usage: UpdateMod <module>");
			return;
		}

		PutStatus("Reloading [" + sMod + "] everywhere");
		if (CZNC::Get().UpdateModule(sMod)) {
			PutStatus("Done");
		} else {
			PutStatus("Done, but there were errors, [" + sMod + "] could not be loaded everywhere.");
		}
	} else if ((sCommand.Equals("ADDBINDHOST") || sCommand.Equals("ADDVHOST")) && m_pUser->IsAdmin()) {
		CString sHost = sLine.Token(1);

		if (sHost.empty()) {
			PutStatus("Usage: AddBindHost <host>");
			return;
		}

		if (CZNC::Get().AddBindHost(sHost)) {
			PutStatus("Done");
		} else {
			PutStatus("The host [" + sHost + "] is already in the list");
		}
	} else if ((sCommand.Equals("REMBINDHOST") || sCommand.Equals("REMVHOST") || sCommand.Equals("DELVHOST")) && m_pUser->IsAdmin()) {
		CString sHost = sLine.Token(1);

		if (sHost.empty()) {
			PutStatus("Usage: RemBindHost <host>");
			return;
		}

		if (CZNC::Get().RemBindHost(sHost)) {
			PutStatus("Done");
		} else {
			PutStatus("The host [" + sHost + "] is not in the list");
		}
	} else if ((sCommand.Equals("LISTBINDHOSTS") || sCommand.Equals("LISTVHOSTS")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
		const VCString& vsHosts = CZNC::Get().GetBindHosts();

		if (vsHosts.empty()) {
			PutStatus("No bind hosts configured");
			return;
		}

		CTable Table;
		Table.AddColumn("Host");

		VCString::const_iterator it;
		for (it = vsHosts.begin(); it != vsHosts.end(); ++it) {
			Table.AddRow();
			Table.SetCell("Host", *it);
		}
		PutStatus(Table);
	} else if ((sCommand.Equals("SETBINDHOST") || sCommand.Equals("SETVHOST")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
		CString sHost = sLine.Token(1);

		if (sHost.empty()) {
			PutStatus("Usage: SetBindHost <host>");
			return;
		}

		if (sHost.Equals(m_pUser->GetBindHost())) {
			PutStatus("You already have this bind host!");
			return;
		}

		const VCString& vsHosts = CZNC::Get().GetBindHosts();
		if (!m_pUser->IsAdmin() && !vsHosts.empty()) {
			VCString::const_iterator it;
			bool bFound = false;

			for (it = vsHosts.begin(); it != vsHosts.end(); ++it) {
				if (sHost.Equals(*it)) {
					bFound = true;
					break;
				}
			}

			if (!bFound) {
				PutStatus("You may not use this bind host. See [ListBindHosts] for a list");
				return;
			}
		}

		m_pUser->SetBindHost(sHost);
		PutStatus("Set bind host to [" + m_pUser->GetBindHost() + "]");
	} else if ((sCommand.Equals("CLEARBINDHOST") || sCommand.Equals("CLEARVHOST")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
		m_pUser->SetBindHost("");
		PutStatus("Bind Host Cleared");
	} else if (sCommand.Equals("PLAYBUFFER")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CString sChan = sLine.Token(1);

		if (sChan.empty()) {
			PutStatus("Usage: PlayBuffer <#chan>");
			return;
		}

		CChan* pChan = m_pNetwork->FindChan(sChan);

		if (!pChan) {
			PutStatus("You are not on [" + sChan + "]");
			return;
		}

		if (!pChan->IsOn()) {
			PutStatus("You are not on [" + sChan + "] [trying]");
			return;
		}

		if (pChan->GetBuffer().IsEmpty()) {
			PutStatus("The buffer for [" + sChan + "] is empty");
			return;
		}

		pChan->SendBuffer(this);
	} else if (sCommand.Equals("CLEARBUFFER")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CString sChan = sLine.Token(1);

		if (sChan.empty()) {
			PutStatus("Usage: ClearBuffer <#chan>");
			return;
		}

		CChan* pChan = m_pNetwork->FindChan(sChan);

		if (!pChan) {
			PutStatus("You are not on [" + sChan + "]");
			return;
		}

		const vector<CChan*>& vChans = m_pNetwork->GetChans();
		vector<CChan*>::const_iterator it;
		unsigned int uMatches = 0;
		for (it = vChans.begin(); it != vChans.end(); ++it) {
			if (!(*it)->GetName().WildCmp(sChan))
				continue;
			uMatches++;

			(*it)->ClearBuffer();
		}
		PutStatus("The buffer for [" + CString(uMatches) + "] channels matching [" + sChan + "] has been cleared");
	} else if (sCommand.Equals("CLEARALLCHANNELBUFFERS")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		vector<CChan*>::const_iterator it;
		const vector<CChan*>& vChans = m_pNetwork->GetChans();

		for (it = vChans.begin(); it != vChans.end(); ++it) {
			(*it)->ClearBuffer();
		}
		PutStatus("All channel buffers have been cleared");
	} else if (sCommand.Equals("SETBUFFER")) {
		if (!m_pNetwork) {
			PutStatus("You must be connected with a network to use this command");
			return;
		}

		CString sChan = sLine.Token(1);

		if (sChan.empty()) {
			PutStatus("Usage: SetBuffer <#chan> [linecount]");
			return;
		}

		unsigned int uLineCount = sLine.Token(2).ToUInt();

		const vector<CChan*>& vChans = m_pNetwork->GetChans();
		vector<CChan*>::const_iterator it;
		unsigned int uMatches = 0, uFail = 0;
		for (it = vChans.begin(); it != vChans.end(); ++it) {
			if (!(*it)->GetName().WildCmp(sChan))
				continue;
			uMatches++;

			if (!(*it)->SetBufferCount(uLineCount))
				uFail++;
		}

		PutStatus("BufferCount for [" + CString(uMatches - uFail) +
				"] channels was set to [" + CString(uLineCount) + "]");
		if (uFail > 0) {
			PutStatus("Setting BufferCount failed for [" + CString(uFail) + "] channels, "
					"max buffer count is " + CString(CZNC::Get().GetMaxBufferSize()));
		}
	} else if (m_pUser->IsAdmin() && sCommand.Equals("TRAFFIC")) {
		CZNC::TrafficStatsPair Users, ZNC, Total;
		CZNC::TrafficStatsMap traffic = CZNC::Get().GetTrafficStats(Users, ZNC, Total);
		CZNC::TrafficStatsMap::const_iterator it;

		CTable Table;
		Table.AddColumn("Username");
		Table.AddColumn("In");
		Table.AddColumn("Out");
		Table.AddColumn("Total");

		for (it = traffic.begin(); it != traffic.end(); ++it) {
			Table.AddRow();
			Table.SetCell("Username", it->first);
			Table.SetCell("In", CString::ToByteStr(it->second.first));
			Table.SetCell("Out", CString::ToByteStr(it->second.second));
			Table.SetCell("Total", CString::ToByteStr(it->second.first + it->second.second));
		}

		Table.AddRow();
		Table.SetCell("Username", "<Users>");
		Table.SetCell("In", CString::ToByteStr(Users.first));
		Table.SetCell("Out", CString::ToByteStr(Users.second));
		Table.SetCell("Total", CString::ToByteStr(Users.first + Users.second));

		Table.AddRow();
		Table.SetCell("Username", "<ZNC>");
		Table.SetCell("In", CString::ToByteStr(ZNC.first));
		Table.SetCell("Out", CString::ToByteStr(ZNC.second));
		Table.SetCell("Total", CString::ToByteStr(ZNC.first + ZNC.second));

		Table.AddRow();
		Table.SetCell("Username", "<Total>");
		Table.SetCell("In", CString::ToByteStr(Total.first));
		Table.SetCell("Out", CString::ToByteStr(Total.second));
		Table.SetCell("Total", CString::ToByteStr(Total.first + Total.second));

		PutStatus(Table);
	} else if (sCommand.Equals("UPTIME")) {
		PutStatus("Running for " + CZNC::Get().GetUptime());
	} else if (m_pUser->IsAdmin() &&
			(sCommand.Equals("LISTPORTS") || sCommand.Equals("ADDPORT") || sCommand.Equals("DELPORT"))) {
		UserPortCommand(sLine);
	} else {
		PutStatus("Unknown command [" + sCommand + "] try 'Help'");
	}
}