Exemple #1
0
void CUser::JoinChans() {
	// Avoid divsion by zero, it's bad!
	if (m_vChans.empty())
		return;

	// We start at a random offset into the channel list so that if your
	// first 3 channels are invite-only and you got MaxJoins == 3, ZNC will
	// still be able to join the rest of your channels.
	unsigned int start = rand() % m_vChans.size();
	unsigned int uJoins = m_uMaxJoins;
	set<CChan*> sChans;
	for (unsigned int a = 0; a < m_vChans.size(); a++) {
		unsigned int idx = (start + a) % m_vChans.size();
		CChan* pChan = m_vChans[idx];
		if (!pChan->IsOn() && !pChan->IsDisabled()) {
			if (!JoinChan(pChan))
				continue;

			sChans.insert(pChan);

			// Limit the number of joins
			if (uJoins != 0 && --uJoins == 0)
				break;
		}
	}

	while (!sChans.empty())
		JoinChans(sChans);
}
Exemple #2
0
bool CClient::OnPartMessage(CPartMessage& Message) {
    CString sChans = Message.GetTarget();

    VCString vsChans;
    sChans.Split(",", vsChans, false);
    sChans.clear();

    for (CString& sChan : vsChans) {
        bool bContinue = false;
        Message.SetTarget(sChan);
        NETWORKMODULECALL(OnUserPartMessage(Message), m_pUser, m_pNetwork, this,
                          &bContinue);
        if (bContinue) continue;

        sChan = Message.GetTarget();

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

        if (pChan && !pChan->IsOn()) {
            PutStatusNotice("Removing channel [" + sChan + "]");
            m_pNetwork->DelChan(sChan);
        } else {
            sChans += (sChans.empty()) ? sChan : CString("," + sChan);
        }
    }

    if (sChans.empty()) {
        return true;
    }

    Message.SetTarget(sChans);

    return false;
}
Exemple #3
0
bool CClient::OnModeMessage(CModeMessage& Message)
{
	CString sTarget = Message.GetTarget();
	CString sModes = Message.GetModes();

	bool bReturn = false;
	NETWORKMODULECALL(OnUserModeMessage(Message), m_pUser, m_pNetwork, this, &bReturn);
	if (bReturn) return true;

	if (m_pNetwork && m_pNetwork->IsChan(sTarget) && sModes.empty()) {
		// If we are on that channel and already received a
		// /mode reply from the server, we can answer this
		// request ourself.

		CChan *pChan = m_pNetwork->FindChan(sTarget);
		if (pChan && pChan->IsOn() && !pChan->GetModeString().empty()) {
			PutClient(":" + m_pNetwork->GetIRCServer() + " 324 " + GetNick() + " " + sTarget + " " + pChan->GetModeString());
			if (pChan->GetCreationDate() > 0) {
				PutClient(":" + m_pNetwork->GetIRCServer() + " 329 " + GetNick() + " " + sTarget + " " + CString(pChan->GetCreationDate()));
			}
			return true;
		}
	}

	return false;
}
	virtual void RunJob()
	{
		if (!m_pUser->GetIRCSock())
			return;

		for (MCString::iterator it = BeginNV(); it != EndNV(); ++it)
		{
			CChan *pChan = m_pUser->FindChan(it->first);
			if (!pChan) {
				pChan = new CChan(it->first, m_pUser, true);
				if (!it->second.empty())
					pChan->SetKey(it->second);
				if (!m_pUser->AddChan(pChan)) {
					/* AddChan() deleted that channel */
					PutModule("Could not join [" + it->first
							+ "] (# prefix missing?)");
					continue;
				}
			}
			if (!pChan->IsOn()) {
				PutModule("Joining [" + pChan->GetName() + "]");
				PutIRC("JOIN " + pChan->GetName() + (pChan->GetKey().empty()
							? "" : " " + pChan->GetKey()));
			}
		}
	}
Exemple #5
0
void CIRCNetwork::JoinChans() {
    bool bHaveKey = false;
    size_t joinLength = 4;  // join
    CString sChannels, sKeys;

    for (vector<CChan*>::iterator it = m_vChans.begin(); it != m_vChans.end(); ++it) {
        CChan *pChan = *it;

        if (pChan->IsOn() || pChan->IsDisabled() || !JoinChan(pChan)) {
            continue;
        }

        size_t length = pChan->GetName().length() + pChan->GetKey().length() + 2;  // +2 for either space or commas

        if ((joinLength + length) >= 510) {
            // Sent what we got, and cleanup
            PutIRC("JOIN " + sChannels + (bHaveKey ? (" " + sKeys) : ""));

            sChannels = "";
            sKeys = "";
            joinLength = 4;  // join
            bHaveKey = false;
        }

        if (!sChannels.empty()) {
            sChannels += ",";
            sKeys += ",";
        }

        if (!pChan->GetKey().empty()) {
            bHaveKey = true;
            sKeys += pChan->GetKey();
        }

        sChannels += pChan->GetName();
        joinLength += length;
    }

    if (!sChannels.empty()) {
        PutIRC("JOIN " + sChannels + (bHaveKey ? (" " + sKeys) : ""));
    }
}
Exemple #6
0
void CIRCSock::ReadLine(const CString& sData) {
	CString sLine = sData;

	sLine.TrimRight("\n\r");

	DEBUG("(" << m_pUser->GetUserName() << ") IRC -> ZNC [" << sLine << "]");

	MODULECALL(OnRaw(sLine), m_pUser, NULL, return);

	if (sLine.Equals("PING ", false, 5)) {
		// Generate a reply and don't forward this to any user,
		// we don't want any PING forwarded
		PutIRC("PONG " + sLine.substr(5));
		return;
	} else if (sLine.Token(1).Equals("PONG")) {
		// Block PONGs, we already responded to the pings
		return;
	} else if (sLine.Equals("ERROR ", false, 6)) {
		//ERROR :Closing Link: nick[24.24.24.24] (Excess Flood)
		CString sError(sLine.substr(6));

		if (sError.Left(1) == ":") {
			sError.LeftChomp();
		}

		m_pUser->PutStatus("Error from Server [" + sError + "]");
		return;
	}

	CString sCmd = sLine.Token(1);

	if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
		CString sServer = sLine.Token(0); sServer.LeftChomp();
		unsigned int uRaw = sCmd.ToUInt();
		CString sNick = sLine.Token(2);
		CString sRest = sLine.Token(3, true);

		switch (uRaw) {
			case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay Network nick
				if (m_bAuthed && sServer == "irc.znc.in") {
					// m_bAuthed == true => we already received another 001 => we might be in a traffic loop
					m_pUser->PutStatus("ZNC seems to be connected to itself, disconnecting...");
					Quit();
					return;
				}

				m_pUser->SetIRCServer(sServer);
				SetTimeout(240, TMO_READ);  // Now that we are connected, let nature take its course
				PutIRC("WHO " + sNick);

				m_bAuthed = true;
				m_pUser->PutStatus("Connected!");

				vector<CClient*>& vClients = m_pUser->GetClients();

				for (unsigned int a = 0; a < vClients.size(); a++) {
					CClient* pClient = vClients[a];
					CString sClientNick = pClient->GetNick(false);

					if (!sClientNick.Equals(sNick)) {
						// If they connected with a nick that doesn't match the one we got on irc, then we need to update them
						pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick);
					}
				}

				SetNick(sNick);

				MODULECALL(OnIRCConnected(), m_pUser, NULL, );

				m_pUser->ClearRawBuffer();
				m_pUser->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);

				CZNC::Get().ReleaseISpoof();
				m_bISpoofReleased = true;

				break;
			}
			case 5:
				ParseISupport(sRest);
				m_pUser->UpdateExactRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
				break;
			case 2:
			case 3:
			case 4:
			case 250:  // highest connection count
			case 251:  // user count
			case 252:  // oper count
			case 254:  // channel count
			case 255:  // client count
			case 265:  // local users
			case 266:  // global users
				m_pUser->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
				break;
			case 305:
				m_pUser->SetIRCAway(false);
				break;
			case 306:
				m_pUser->SetIRCAway(true);
				break;
			case 324: {  // MODE
				sRest.Trim();
				CChan* pChan = m_pUser->FindChan(sRest.Token(0));

				if (pChan) {
					pChan->SetModes(sRest.Token(1, true));
				}
			}
				break;
			case 329: {
				sRest.Trim();
				CChan* pChan = m_pUser->FindChan(sRest.Token(0));

				if (pChan) {
					unsigned long ulDate = sLine.Token(4).ToULong();
					pChan->SetCreationDate(ulDate);
				}
			}
				break;
			case 331: {
				// :irc.server.com 331 yournick #chan :No topic is set.
				CChan* pChan = m_pUser->FindChan(sLine.Token(3));

				if (pChan) {
					pChan->SetTopic("");
				}

				break;
			}
			case 332: {
				// :irc.server.com 332 yournick #chan :This is a topic
				CChan* pChan = m_pUser->FindChan(sLine.Token(3));

				if (pChan) {
					CString sTopic = sLine.Token(4, true);
					sTopic.LeftChomp();
					pChan->SetTopic(sTopic);
				}

				break;
			}
			case 333: {
				// :irc.server.com 333 yournick #chan setternick 1112320796
				CChan* pChan = m_pUser->FindChan(sLine.Token(3));

				if (pChan) {
					sNick = sLine.Token(4);
					unsigned long ulDate = sLine.Token(5).ToULong();

					pChan->SetTopicOwner(sNick);
					pChan->SetTopicDate(ulDate);
				}

				break;
			}
			case 352: {
				// :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
				sServer = sLine.Token(0);
				sNick = sLine.Token(7);
				CString sIdent = sLine.Token(4);
				CString sHost = sLine.Token(5);

				sServer.LeftChomp();

				if (sNick.Equals(GetNick())) {
					m_Nick.SetIdent(sIdent);
					m_Nick.SetHost(sHost);
				}

				m_pUser->SetIRCNick(m_Nick);
				m_pUser->SetIRCServer(sServer);

				const vector<CChan*>& vChans = m_pUser->GetChans();

				for (unsigned int a = 0; a < vChans.size(); a++) {
					vChans[a]->OnWho(sNick, sIdent, sHost);
				}

				break;
			}
			case 353: {  // NAMES
				sRest.Trim();
				// Todo: allow for non @+= server msgs
				CChan* pChan = m_pUser->FindChan(sRest.Token(1));
				// If we don't know that channel, some client might have
				// requested a /names for it and we really should forward this.
				if (pChan) {
					CString sNicks = sRest.Token(2, true);
					if (sNicks.Left(1) == ":") {
						sNicks.LeftChomp();
					}

					pChan->AddNicks(sNicks);
				}

				ForwardRaw353(sLine);

				// We forwarded it already, so return
				return;
			}
			case 366: {  // end of names list
				m_pUser->PutUser(sLine);  // First send them the raw

				// :irc.server.com 366 nick #chan :End of /NAMES list.
				CChan* pChan = m_pUser->FindChan(sRest.Token(0));

				if (pChan) {
					if (pChan->IsOn()) {
						// If we are the only one in the chan, set our default modes
						if (pChan->GetNickCount() == 1) {
							CString sModes = pChan->GetDefaultModes();

							if (sModes.empty()) {
								sModes = m_pUser->GetDefaultChanModes();
							}

							if (!sModes.empty()) {
								PutIRC("MODE " + pChan->GetName() + " " + sModes);
							}
						}
					}
				}

				return;  // return so we don't send them the raw twice
			}
			case 375:  // begin motd
			case 422:  // MOTD File is missing
				m_pUser->ClearMotdBuffer();
			case 372:  // motd
			case 376:  // end motd
				m_pUser->AddMotdBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
				break;
			case 437:
				// :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable
				// :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable
				// :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel
				if (m_pUser->IsChan(sRest.Token(0)) || sNick != "*")
					break;
			case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal characters
			case 433: {
				CString sBadNick = sRest.Token(0);

				if (!m_bAuthed) {
					SendAltNick(sBadNick);
					return;
				}
				break;
			}
			case 451:
				// :irc.server.com 451 CAP :You have not registered
				// Servers that dont support CAP will give us this error, dont send it to the client
				if (sNick.Equals("CAP"))
					return;
			case 470: {
				// :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2
				// :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel

				// freenode style numeric
				CChan* pChan = m_pUser->FindChan(sRest.Token(0));
				if (!pChan) {
					// unreal style numeric
					pChan = m_pUser->FindChan(sRest.Token(1));
				}
				if (pChan) {
					pChan->Disable();
					m_pUser->PutStatus("Channel [" + pChan->GetName() + "] is linked to "
							"another channel and was thus disabled.");
				}
				break;
			}
		}
	} else {
Exemple #7
0
void CIRCSock::ReadLine(const CString& sData) {
	CString sLine = sData;

	sLine.TrimRight("\n\r");

	DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") IRC -> ZNC [" << sLine << "]");

	NETWORKMODULECALL(OnRaw(sLine), m_pNetwork->GetUser(), m_pNetwork, NULL, return);

	if (sLine.Equals("PING ", false, 5)) {
		// Generate a reply and don't forward this to any user,
		// we don't want any PING forwarded
		PutIRC("PONG " + sLine.substr(5));
		return;
	} else if (sLine.Token(1).Equals("PONG")) {
		// Block PONGs, we already responded to the pings
		return;
	} else if (sLine.Equals("ERROR ", false, 6)) {
		//ERROR :Closing Link: nick[24.24.24.24] (Excess Flood)
		CString sError(sLine.substr(6));
		sError.TrimPrefix();
		m_pNetwork->PutStatus("Error from Server [" + sError + "]");
		return;
	}

	CString sCmd = sLine.Token(1);

	if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
		CString sServer = sLine.Token(0).LeftChomp_n();
		unsigned int uRaw = sCmd.ToUInt();
		CString sNick = sLine.Token(2);
		CString sRest = sLine.Token(3, true);

		switch (uRaw) {
			case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay Network nick
				if (m_bAuthed && sServer == "irc.znc.in") {
					// m_bAuthed == true => we already received another 001 => we might be in a traffic loop
					m_pNetwork->PutStatus("ZNC seems to be connected to itself, disconnecting...");
					Quit();
					return;
				}

				m_pNetwork->SetIRCServer(sServer);
				SetTimeout(540, TMO_READ);  // Now that we are connected, let nature take its course
				PutIRC("WHO " + sNick);

				m_bAuthed = true;
				m_pNetwork->PutStatus("Connected!");

				vector<CClient*>& vClients = m_pNetwork->GetClients();

				for (unsigned int a = 0; a < vClients.size(); a++) {
					CClient* pClient = vClients[a];
					CString sClientNick = pClient->GetNick(false);

					if (!sClientNick.Equals(sNick)) {
						// If they connected with a nick that doesn't match the one we got on irc, then we need to update them
						pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick);
					}
				}

				SetNick(sNick);

				NETWORKMODULECALL(OnIRCConnected(), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING);

				m_pNetwork->ClearRawBuffer();
				m_pNetwork->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);

				break;
			}
			case 5:
				ParseISupport(sRest);
				m_pNetwork->UpdateExactRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
				break;
			case 10: { // :irc.server.com 010 nick <hostname> <port> :<info>
				CString sHost = sRest.Token(0);
				CString sPort = sRest.Token(1);
				CString sInfo = sRest.Token(2, true).TrimPrefix_n();
				m_pNetwork->PutStatus("Server [" + m_pNetwork->GetCurrentServer()->GetString(false) +
						"] redirects us to [" + sHost + ":" + sPort + "] with reason [" + sInfo + "]");
				m_pNetwork->PutStatus("Perhaps you want to add it as a new server.");
				// Don't send server redirects to the client
				return;
			}
			case 2:
			case 3:
			case 4:
			case 250:  // highest connection count
			case 251:  // user count
			case 252:  // oper count
			case 254:  // channel count
			case 255:  // client count
			case 265:  // local users
			case 266:  // global users
				m_pNetwork->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
				break;
			case 305:
				m_pNetwork->SetIRCAway(false);
				break;
			case 306:
				m_pNetwork->SetIRCAway(true);
				break;
			case 324: {  // MODE
				sRest.Trim();
				CChan* pChan = m_pNetwork->FindChan(sRest.Token(0));

				if (pChan) {
					pChan->SetModes(sRest.Token(1, true));

					// We don't SetModeKnown(true) here,
					// because a 329 will follow
					if (!pChan->IsModeKnown()) {
						// When we JOIN, we send a MODE
						// request. This makes sure the
						// reply isn't forwarded.
						return;
					}
				}
			}
				break;
			case 329: {
				sRest.Trim();
				CChan* pChan = m_pNetwork->FindChan(sRest.Token(0));

				if (pChan) {
					unsigned long ulDate = sLine.Token(4).ToULong();
					pChan->SetCreationDate(ulDate);

					if (!pChan->IsModeKnown()) {
						pChan->SetModeKnown(true);
						// When we JOIN, we send a MODE
						// request. This makes sure the
						// reply isn't forwarded.
						return;
					}
				}
			}
				break;
			case 331: {
				// :irc.server.com 331 yournick #chan :No topic is set.
				CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));

				if (pChan) {
					pChan->SetTopic("");
				}

				break;
			}
			case 332: {
				// :irc.server.com 332 yournick #chan :This is a topic
				CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));

				if (pChan) {
					CString sTopic = sLine.Token(4, true);
					sTopic.LeftChomp();
					pChan->SetTopic(sTopic);
				}

				break;
			}
			case 333: {
				// :irc.server.com 333 yournick #chan setternick 1112320796
				CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));

				if (pChan) {
					sNick = sLine.Token(4);
					unsigned long ulDate = sLine.Token(5).ToULong();

					pChan->SetTopicOwner(sNick);
					pChan->SetTopicDate(ulDate);
				}

				break;
			}
			case 352: {
				// :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
				sServer = sLine.Token(0);
				sNick = sLine.Token(7);
				CString sIdent = sLine.Token(4);
				CString sHost = sLine.Token(5);

				sServer.LeftChomp();

				if (sNick.Equals(GetNick())) {
					m_Nick.SetIdent(sIdent);
					m_Nick.SetHost(sHost);
				}

				m_pNetwork->SetIRCNick(m_Nick);
				m_pNetwork->SetIRCServer(sServer);

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

				for (unsigned int a = 0; a < vChans.size(); a++) {
					vChans[a]->OnWho(sNick, sIdent, sHost);
				}

				break;
			}
			case 353: {  // NAMES
				sRest.Trim();
				// Todo: allow for non @+= server msgs
				CChan* pChan = m_pNetwork->FindChan(sRest.Token(1));
				// If we don't know that channel, some client might have
				// requested a /names for it and we really should forward this.
				if (pChan) {
					CString sNicks = sRest.Token(2, true).TrimPrefix_n();
					pChan->AddNicks(sNicks);
				}

				ForwardRaw353(sLine);

				// We forwarded it already, so return
				return;
			}
			case 366: {  // end of names list
				m_pNetwork->PutUser(sLine);  // First send them the raw

				// :irc.server.com 366 nick #chan :End of /NAMES list.
				CChan* pChan = m_pNetwork->FindChan(sRest.Token(0));

				if (pChan) {
					if (pChan->IsOn()) {
						// If we are the only one in the chan, set our default modes
						if (pChan->GetNickCount() == 1) {
							CString sModes = pChan->GetDefaultModes();

							if (sModes.empty()) {
								sModes = m_pNetwork->GetUser()->GetDefaultChanModes();
							}

							if (!sModes.empty()) {
								PutIRC("MODE " + pChan->GetName() + " " + sModes);
							}
						}
					}
				}

				return;  // return so we don't send them the raw twice
			}
			case 375:  // begin motd
			case 422:  // MOTD File is missing
				m_pNetwork->ClearMotdBuffer();
			case 372:  // motd
			case 376:  // end motd
				m_pNetwork->AddMotdBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
				break;
			case 437:
				// :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable
				// :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable
				// :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel
				if (m_pNetwork->IsChan(sRest.Token(0)) || sNick != "*")
					break;
			case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal characters
			case 433: {
				CString sBadNick = sRest.Token(0);

				if (!m_bAuthed) {
					SendAltNick(sBadNick);
					return;
				}
				break;
			}
			case 451:
				// :irc.server.com 451 CAP :You have not registered
				// Servers that dont support CAP will give us this error, dont send it to the client
				if (sNick.Equals("CAP"))
					return;
			case 470: {
				// :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2
				// :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel

				// freenode style numeric
				CChan* pChan = m_pNetwork->FindChan(sRest.Token(0));
				if (!pChan) {
					// unreal style numeric
					pChan = m_pNetwork->FindChan(sRest.Token(1));
				}
				if (pChan) {
					pChan->Disable();
					m_pNetwork->PutStatus("Channel [" + pChan->GetName() + "] is linked to "
							"another channel and was thus disabled.");
				}
				break;
			}
		}
	} else {
		CNick Nick(sLine.Token(0).TrimPrefix_n());
		sCmd = sLine.Token(1);
		CString sRest = sLine.Token(2, true);

		if (sCmd.Equals("NICK")) {
			CString sNewNick = sRest.TrimPrefix_n();
			bool bIsVisible = false;

			vector<CChan*> vFoundChans;
			const vector<CChan*>& vChans = m_pNetwork->GetChans();

			for (unsigned int a = 0; a < vChans.size(); a++) {
				CChan* pChan = vChans[a];

				if (pChan->ChangeNick(Nick.GetNick(), sNewNick)) {
					vFoundChans.push_back(pChan);

					if (!pChan->IsDetached()) {
						bIsVisible = true;
					}
				}
			}

			// Todo: use nick compare function here
			if (Nick.GetNick().Equals(GetNick())) {
				// We are changing our own nick, the clients always must see this!
				bIsVisible = true;
				SetNick(sNewNick);
			}

			NETWORKMODULECALL(OnNick(Nick, sNewNick, vFoundChans), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING);

			if (!bIsVisible) {
				return;
			}
		} else if (sCmd.Equals("QUIT")) {
			CString sMessage = sRest.TrimPrefix_n();
			bool bIsVisible = false;

			// :[email protected] QUIT :message

			if (Nick.GetNick().Equals(GetNick())) {
				m_pNetwork->PutStatus("You quit [" + sMessage + "]");
				// We don't call module hooks and we don't
				// forward this quit to clients (Some clients
				// disconnect if they receive such a QUIT)
				return;
			}

			vector<CChan*> vFoundChans;
			const vector<CChan*>& vChans = m_pNetwork->GetChans();

			for (unsigned int a = 0; a < vChans.size(); a++) {
				CChan* pChan = vChans[a];

				if (pChan->RemNick(Nick.GetNick())) {
					vFoundChans.push_back(pChan);

					if (!pChan->IsDetached()) {
						bIsVisible = true;
					}
				}
			}

			NETWORKMODULECALL(OnQuit(Nick, sMessage, vFoundChans), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING);

			if (!bIsVisible) {
				return;
			}
		} else if (sCmd.Equals("JOIN")) {
			CString sChan = sRest.Token(0).TrimPrefix_n();
			CChan* pChan;

			// Todo: use nick compare function
			if (Nick.GetNick().Equals(GetNick())) {
				m_pNetwork->AddChan(sChan, false);
				pChan = m_pNetwork->FindChan(sChan);
				if (pChan) {
					pChan->ResetJoinTries();
					pChan->Enable();
					pChan->SetIsOn(true);
					PutIRC("MODE " + sChan);
				}
			} else {
				pChan = m_pNetwork->FindChan(sChan);
			}

			if (pChan) {
				pChan->AddNick(Nick.GetNickMask());
				NETWORKMODULECALL(OnJoin(Nick.GetNickMask(), *pChan), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING);

				if (pChan->IsDetached()) {
					return;
				}
			}
		} else if (sCmd.Equals("PART")) {
			CString sChan = sRest.Token(0).TrimPrefix_n();
			CString sMsg = sRest.Token(1, true).TrimPrefix_n();

			CChan* pChan = m_pNetwork->FindChan(sChan);
			bool bDetached = false;
			if (pChan) {
				pChan->RemNick(Nick.GetNick());
				NETWORKMODULECALL(OnPart(Nick.GetNickMask(), *pChan, sMsg), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING);

				if (pChan->IsDetached())
					bDetached = true;
			}

			// Todo: use nick compare function
			if (Nick.GetNick().Equals(GetNick())) {
				m_pNetwork->DelChan(sChan);
			}

			/*
			 * We use this boolean because
			 * m_pNetwork->DelChan() will delete this channel
			 * and thus we would dereference an
			 * already-freed pointer!
			 */
			if (bDetached) {
				return;
			}
		} else if (sCmd.Equals("MODE")) {
			CString sTarget = sRest.Token(0);
			CString sModes = sRest.Token(1, true);
			if (sModes.Left(1) == ":")
				sModes = sModes.substr(1);

			CChan* pChan = m_pNetwork->FindChan(sTarget);
			if (pChan) {
				pChan->ModeChange(sModes, &Nick);

				if (pChan->IsDetached()) {
					return;
				}
			} else if (sTarget == m_Nick.GetNick()) {
				CString sModeArg = sModes.Token(0);
				bool bAdd = true;
/* no module call defined (yet?)
				MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs), m_pNetwork->GetUser(), NULL, );
*/
				for (unsigned int a = 0; a < sModeArg.size(); a++) {
					const unsigned char& uMode = sModeArg[a];

					if (uMode == '+') {
						bAdd = true;
					} else if (uMode == '-') {
						bAdd = false;
					} else {
						if (bAdd) {
							m_scUserModes.insert(uMode);
						} else {
							m_scUserModes.erase(uMode);
						}
					}
				}
			}
		} else if (sCmd.Equals("KICK")) {
			// :[email protected] KICK #chan nick :msg
			CString sChan = sRest.Token(0);
			CString sKickedNick = sRest.Token(1);
			CString sMsg = sRest.Token(2, true);
			sMsg.LeftChomp();

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

			if (pChan) {
				NETWORKMODULECALL(OnKick(Nick, sKickedNick, *pChan, sMsg), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING);
				// do not remove the nick till after the OnKick call, so modules
				// can do Chan.FindNick or something to get more info.
				pChan->RemNick(sKickedNick);
			}

			if (GetNick().Equals(sKickedNick) && pChan) {
				pChan->SetIsOn(false);

				// Don't try to rejoin!
				pChan->Disable();
			}

			if ((pChan) && (pChan->IsDetached())) {
				return;
			}
		} else if (sCmd.Equals("NOTICE")) {
			// :[email protected] NOTICE #chan :Message
			CString sTarget = sRest.Token(0);
			CString sMsg = sRest.Token(1, true);
			sMsg.LeftChomp();

			if (sMsg.WildCmp("\001*\001")) {
				sMsg.LeftChomp();
				sMsg.RightChomp();

				if (sTarget.Equals(GetNick())) {
					if (OnCTCPReply(Nick, sMsg)) {
						return;
					}
				}

				m_pNetwork->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :\001" + sMsg + "\001");
				return;
			} else {
				if (sTarget.Equals(GetNick())) {
					if (OnPrivNotice(Nick, sMsg)) {
						return;
					}
				} else {
					if (OnChanNotice(Nick, sTarget, sMsg)) {
						return;
					}
				}
			}

			if (Nick.GetNick().Equals(m_pNetwork->GetIRCServer())) {
				m_pNetwork->PutUser(":" + Nick.GetNick() + " NOTICE " + sTarget + " :" + sMsg);
			} else {
				m_pNetwork->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :" + sMsg);
			}

			return;
		} else if (sCmd.Equals("TOPIC")) {
			// :[email protected] TOPIC #chan :This is a topic
			CChan* pChan = m_pNetwork->FindChan(sLine.Token(2));

			if (pChan) {
				CString sTopic = sLine.Token(3, true);
				sTopic.LeftChomp();

				NETWORKMODULECALL(OnTopic(Nick, *pChan, sTopic), m_pNetwork->GetUser(), m_pNetwork, NULL, return);

				pChan->SetTopicOwner(Nick.GetNick());
				pChan->SetTopicDate((unsigned long) time(NULL));
				pChan->SetTopic(sTopic);

				if (pChan->IsDetached()) {
					return; // Don't forward this
				}

				sLine = ":" + Nick.GetNickMask() + " TOPIC " + pChan->GetName() + " :" + sTopic;
			}
		} else if (sCmd.Equals("PRIVMSG")) {
Exemple #8
0
bool CIRCSock::OnNumericMessage(CNumericMessage& Message) {
    const CString& sCmd = Message.GetCommand();
    CString sServer = Message.GetNick().GetHostMask();
    unsigned int uRaw = Message.GetCode();
    CString sNick = Message.GetParam(0);

    bool bResult = false;
    IRCSOCKMODULECALL(OnNumericMessage(Message), &bResult);
    if (bResult) return true;

    switch (uRaw) {
    case 1: {  // :irc.server.com 001 nick :Welcome to the Internet Relay
        if (m_bAuthed && sServer == "irc.znc.in") {
            // m_bAuthed == true => we already received another 001 => we
            // might be in a traffic loop
            m_pNetwork->PutStatus(
                "ZNC seems to be connected to itself, disconnecting...");
            Quit();
            return true;
        }

        m_pNetwork->SetIRCServer(sServer);
        // Now that we are connected, let nature take its course
        SetTimeout(CIRCNetwork::NO_TRAFFIC_TIMEOUT, TMO_READ);
        PutIRC("WHO " + sNick);

        m_bAuthed = true;
        m_pNetwork->PutStatus("Connected!");

        const vector<CClient*>& vClients = m_pNetwork->GetClients();

        for (CClient* pClient : vClients) {
            CString sClientNick = pClient->GetNick(false);

            if (!sClientNick.Equals(sNick)) {
                // If they connected with a nick that doesn't match the one
                // we got on irc, then we need to update them
                pClient->PutClient(":" + sClientNick + "!" +
                                   m_Nick.GetIdent() + "@" +
                                   m_Nick.GetHost() + " NICK :" + sNick);
            }
        }

        SetNick(sNick);

        IRCSOCKMODULECALL(OnIRCConnected(), NOTHING);

        m_pNetwork->ClearRawBuffer();
        m_pNetwork->AddRawBuffer(BufferMessage(Message));

        m_pNetwork->IRCConnected();

        break;
    }
    case 5:
        ParseISupport(Message);
        m_pNetwork->UpdateExactRawBuffer(BufferMessage(Message));
        break;
    case 10: {  // :irc.server.com 010 nick <hostname> <port> :<info>
        CString sHost = Message.GetParam(1);
        CString sPort = Message.GetParam(2);
        CString sInfo = Message.GetParam(3);
        m_pNetwork->PutStatus(
            "Server [" + m_pNetwork->GetCurrentServer()->GetString(false) +
            "] redirects us to [" + sHost + ":" + sPort +
            "] with reason [" + sInfo + "]");
        m_pNetwork->PutStatus(
            "Perhaps you want to add it as a new server.");
        // Don't send server redirects to the client
        return true;
    }
    case 2:
    case 3:
    case 4:
    case 250:  // highest connection count
    case 251:  // user count
    case 252:  // oper count
    case 254:  // channel count
    case 255:  // client count
    case 265:  // local users
    case 266:  // global users
        m_pNetwork->UpdateRawBuffer(sCmd, BufferMessage(Message));
        break;
    case 305:
        m_pNetwork->SetIRCAway(false);
        break;
    case 306:
        m_pNetwork->SetIRCAway(true);
        break;
    case 324: {  // MODE
        // :irc.server.com 324 nick #chan +nstk key
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));

        if (pChan) {
            pChan->SetModes(Message.GetParams(2));

            // We don't SetModeKnown(true) here,
            // because a 329 will follow
            if (!pChan->IsModeKnown()) {
                // When we JOIN, we send a MODE
                // request. This makes sure the
                // reply isn't forwarded.
                return true;
            }
            if (pChan->IsDetached()) {
                return true;
            }
        }
    }
    break;
    case 329: {
        // :irc.server.com 329 nick #chan 1234567890
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));

        if (pChan) {
            unsigned long ulDate = Message.GetParam(2).ToULong();
            pChan->SetCreationDate(ulDate);

            if (!pChan->IsModeKnown()) {
                pChan->SetModeKnown(true);
                // When we JOIN, we send a MODE
                // request. This makes sure the
                // reply isn't forwarded.
                return true;
            }
            if (pChan->IsDetached()) {
                return true;
            }
        }
    }
    break;
    case 331: {
        // :irc.server.com 331 yournick #chan :No topic is set.
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));

        if (pChan) {
            pChan->SetTopic("");
            if (pChan->IsDetached()) {
                return true;
            }
        }

        break;
    }
    case 332: {
        // :irc.server.com 332 yournick #chan :This is a topic
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));

        if (pChan) {
            CString sTopic = Message.GetParam(2);
            pChan->SetTopic(sTopic);
            if (pChan->IsDetached()) {
                return true;
            }
        }

        break;
    }
    case 333: {
        // :irc.server.com 333 yournick #chan setternick 1112320796
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));

        if (pChan) {
            sNick = Message.GetParam(2);
            unsigned long ulDate = Message.GetParam(3).ToULong();

            pChan->SetTopicOwner(sNick);
            pChan->SetTopicDate(ulDate);

            if (pChan->IsDetached()) {
                return true;
            }
        }

        break;
    }
    case 352: {  // WHO
        // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
        sNick = Message.GetParam(5);
        CString sChan = Message.GetParam(1);
        CString sIdent = Message.GetParam(2);
        CString sHost = Message.GetParam(3);

        if (sNick.Equals(GetNick())) {
            m_Nick.SetIdent(sIdent);
            m_Nick.SetHost(sHost);
        }

        m_pNetwork->SetIRCNick(m_Nick);
        m_pNetwork->SetIRCServer(sServer);

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

        for (CChan* pChan : vChans) {
            pChan->OnWho(sNick, sIdent, sHost);
        }

        CChan* pChan = m_pNetwork->FindChan(sChan);
        if (pChan && pChan->IsDetached()) {
            return true;
        }

        break;
    }
    case 353: {  // NAMES
        // :irc.server.com 353 nick @ #chan :nick1 nick2
        // Todo: allow for non @+= server msgs
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(2));
        // If we don't know that channel, some client might have
        // requested a /names for it and we really should forward this.
        if (pChan) {
            CString sNicks = Message.GetParam(3);
            pChan->AddNicks(sNicks);
            if (pChan->IsDetached()) {
                return true;
            }
        }

        break;
    }
    case 366: {  // end of names list
        // :irc.server.com 366 nick #chan :End of /NAMES list.
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));

        if (pChan) {
            if (pChan->IsOn()) {
                // If we are the only one in the chan, set our default modes
                if (pChan->GetNickCount() == 1) {
                    CString sModes = pChan->GetDefaultModes();

                    if (sModes.empty()) {
                        sModes =
                            m_pNetwork->GetUser()->GetDefaultChanModes();
                    }

                    if (!sModes.empty()) {
                        PutIRC("MODE " + pChan->GetName() + " " + sModes);
                    }
                }
            }
            if (pChan->IsDetached()) {
                // don't put it to clients
                return true;
            }
        }

        break;
    }
    case 375:  // begin motd
    case 422:  // MOTD File is missing
        if (m_pNetwork->GetIRCServer().Equals(sServer)) {
            m_pNetwork->ClearMotdBuffer();
        }
    case 372:  // motd
    case 376:  // end motd
        if (m_pNetwork->GetIRCServer().Equals(sServer)) {
            m_pNetwork->AddMotdBuffer(BufferMessage(Message));
        }
        break;
    case 437:
        // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable
        // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable
        // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel
        if (m_pNetwork->IsChan(Message.GetParam(1)) || sNick != "*") break;
    case 432:
    // :irc.server.com 432 * nick :Erroneous Nickname: Illegal chars
    case 433: {
        CString sBadNick = Message.GetParam(1);

        if (!m_bAuthed) {
            SendAltNick(sBadNick);
            return true;
        }
        break;
    }
    case 451:
        // :irc.server.com 451 CAP :You have not registered
        // Servers that don't support CAP will give us this error, don't send
        // it to the client
        if (sNick.Equals("CAP")) return true;
    case 470: {
        // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2
        // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel

        // freenode style numeric
        CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
        if (!pChan) {
            // unreal style numeric
            pChan = m_pNetwork->FindChan(Message.GetParam(2));
        }
        if (pChan) {
            pChan->Disable();
            m_pNetwork->PutStatus("Channel [" + pChan->GetName() +
                                  "] is linked to "
                                  "another channel and was thus disabled.");
        }
        break;
    }
    case 670:
        // :hydra.sector5d.org 670 kylef :STARTTLS successful, go ahead with TLS handshake
        //
        // 670 is a response to `STARTTLS` telling the client to switch to
        // TLS
        if (!GetSSL()) {
            StartTLS();
            m_pNetwork->PutStatus("Switched to SSL (STARTTLS)");
        }

        return true;
    }

    return false;
}
Exemple #9
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'");
	}
}