/* * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include using std::map; using std::set; using std::vector; void CClient::UserCommand(CString& sLine) { if (!m_pUser) { return; } if (sLine.empty()) { return; } bool bReturn = false; NETWORKMODULECALL(OnStatusCommand(sLine), m_pUser, m_pNetwork, this, &bReturn); if (bReturn) return; const CString sCommand = sLine.Token(0); if (sCommand.Equals("HELP")) { HelpUser(sLine.Token(1)); } else if (sCommand.Equals("LISTNICKS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sChan = sLine.Token(1); if (sChan.empty()) { PutStatus(t_s("Usage: ListNicks <#chan>")); return; } CChan* pChan = m_pNetwork->FindChan(sChan); if (!pChan) { PutStatus(t_f("You are not on [{1}]")(sChan)); return; } if (!pChan->IsOn()) { PutStatus(t_f("You are not on [{1}] (trying)")(sChan)); return; } const map& msNicks = pChan->GetNicks(); CIRCSock* pIRCSock = m_pNetwork->GetIRCSock(); const CString& sPerms = (pIRCSock) ? pIRCSock->GetPerms() : ""; if (msNicks.empty()) { PutStatus(t_f("No nicks on [{1}]")(sChan)); return; } CTable Table; for (unsigned int p = 0; p < sPerms.size(); p++) { CString sPerm; sPerm += sPerms[p]; Table.AddColumn(sPerm); } Table.AddColumn(t_s("Nick")); Table.AddColumn(t_s("Ident")); Table.AddColumn(t_s("Host")); for (const auto& it : msNicks) { Table.AddRow(); for (unsigned int b = 0; b < sPerms.size(); b++) { if (it.second.HasPerm(sPerms[b])) { CString sPerm; sPerm += sPerms[b]; Table.SetCell(sPerm, sPerm); } } Table.SetCell(t_s("Nick"), it.second.GetNick()); Table.SetCell(t_s("Ident"), it.second.GetIdent()); Table.SetCell(t_s("Host"), it.second.GetHost()); } PutStatus(Table); } else if (sCommand.Equals("ATTACH")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sPatterns = sLine.Token(1, true); if (sPatterns.empty()) { PutStatus(t_s("Usage: Attach <#chans>")); return; } set sChans = MatchChans(sPatterns); unsigned int uAttachedChans = AttachChans(sChans); PutStatus(t_p("There was {1} channel matching [{2}]", "There were {1} channels matching [{2}]", sChans.size())(sChans.size(), sPatterns)); PutStatus(t_p("Attached {1} channel", "Attached {1} channels", uAttachedChans)(uAttachedChans)); } else if (sCommand.Equals("DETACH")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sPatterns = sLine.Token(1, true); if (sPatterns.empty()) { PutStatus(t_s("Usage: Detach <#chans>")); return; } set sChans = MatchChans(sPatterns); unsigned int uDetached = DetachChans(sChans); PutStatus(t_p("There was {1} channel matching [{2}]", "There were {1} channels matching [{2}]", sChans.size())(sChans.size(), sPatterns)); PutStatus(t_p("Detached {1} channel", "Detached {1} channels", uDetached)(uDetached)); } else if (sCommand.Equals("VERSION")) { PutStatus(CZNC::GetTag()); PutStatus(CZNC::GetCompileOptionsString()); } else if (sCommand.Equals("MOTD") || sCommand.Equals("ShowMOTD")) { if (!SendMotd()) { PutStatus(t_s("There is no MOTD set.")); } } else if (m_pUser->IsAdmin() && sCommand.Equals("Rehash")) { CString sRet; if (CZNC::Get().RehashConfig(sRet)) { PutStatus(t_s("Rehashing succeeded!")); } else { PutStatus(t_f("Rehashing failed: {1}")(sRet)); } } else if (m_pUser->IsAdmin() && sCommand.Equals("SaveConfig")) { if (CZNC::Get().WriteConfig()) { PutStatus(t_f("Wrote config to {1}")(CZNC::Get().GetConfigFile())); } else { PutStatus(t_s("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(t_s("Usage: ListClients")); return; } pUser = CZNC::Get().FindUser(sNick); if (!pUser) { PutStatus(t_f("No such user: {1}")(sNick)); return; } } vector vClients = pUser->GetAllClients(); if (vClients.empty()) { PutStatus(t_s("No clients are connected")); return; } CTable Table; Table.AddColumn(t_s("Host", "listclientscmd")); Table.AddColumn(t_s("Network", "listclientscmd")); Table.AddColumn(t_s("Identifier", "listclientscmd")); for (const CClient* pClient : vClients) { Table.AddRow(); Table.SetCell(t_s("Host", "listclientscmd"), pClient->GetRemoteIP()); if (pClient->GetNetwork()) { Table.SetCell(t_s("Network", "listclientscmd"), pClient->GetNetwork()->GetName()); } Table.SetCell(t_s("Identifier", "listclientscmd"), pClient->GetIdentifier()); } PutStatus(Table); } else if (m_pUser->IsAdmin() && sCommand.Equals("LISTUSERS")) { const map& msUsers = CZNC::Get().GetUserMap(); CTable Table; Table.AddColumn(t_s("Username", "listuserscmd")); Table.AddColumn(t_s("Networks", "listuserscmd")); Table.AddColumn(t_s("Clients", "listuserscmd")); for (const auto& it : msUsers) { Table.AddRow(); Table.SetCell(t_s("Username", "listuserscmd"), it.first); Table.SetCell(t_s("Networks", "listuserscmd"), CString(it.second->GetNetworks().size())); Table.SetCell(t_s("Clients", "listuserscmd"), CString(it.second->GetAllClients().size())); } PutStatus(Table); } else if (m_pUser->IsAdmin() && sCommand.Equals("LISTALLUSERNETWORKS")) { const map& msUsers = CZNC::Get().GetUserMap(); CTable Table; Table.AddColumn(t_s("Username", "listallusernetworkscmd")); Table.AddColumn(t_s("Network", "listallusernetworkscmd")); Table.AddColumn(t_s("Clients", "listallusernetworkscmd")); Table.AddColumn(t_s("On IRC", "listallusernetworkscmd")); Table.AddColumn(t_s("IRC Server", "listallusernetworkscmd")); Table.AddColumn(t_s("IRC User", "listallusernetworkscmd")); Table.AddColumn(t_s("Channels", "listallusernetworkscmd")); for (const auto& it : msUsers) { Table.AddRow(); Table.SetCell(t_s("Username", "listallusernetworkscmd"), it.first); Table.SetCell(t_s("Network", "listallusernetworkscmd"), t_s("N/A")); Table.SetCell(t_s("Clients", "listallusernetworkscmd"), CString(it.second->GetUserClients().size())); const vector& vNetworks = it.second->GetNetworks(); for (const CIRCNetwork* pNetwork : vNetworks) { Table.AddRow(); if (pNetwork == vNetworks.back()) { Table.SetCell(t_s("Username", "listallusernetworkscmd"), "`-"); } else { Table.SetCell(t_s("Username", "listallusernetworkscmd"), "|-"); } Table.SetCell(t_s("Network", "listallusernetworkscmd"), pNetwork->GetName()); Table.SetCell(t_s("Clients", "listallusernetworkscmd"), CString(pNetwork->GetClients().size())); if (pNetwork->IsIRCConnected()) { Table.SetCell(t_s("On IRC", "listallusernetworkscmd"), t_s("Yes", "listallusernetworkscmd")); Table.SetCell(t_s("IRC Server", "listallusernetworkscmd"), pNetwork->GetIRCServer()); Table.SetCell(t_s("IRC User", "listallusernetworkscmd"), pNetwork->GetIRCNick().GetNickMask()); Table.SetCell(t_s("Channels", "listallusernetworkscmd"), CString(pNetwork->GetChans().size())); } else { Table.SetCell(t_s("On IRC", "listallusernetworkscmd"), t_s("No", "listallusernetworkscmd")); } } } PutStatus(Table); } else if (m_pUser->IsAdmin() && sCommand.Equals("SetMOTD")) { CString sMessage = sLine.Token(1, true); if (sMessage.empty()) { PutStatus(t_s("Usage: SetMOTD ")); } else { CZNC::Get().SetMotd(sMessage); PutStatus(t_f("MOTD set to: {1}")(sMessage)); } } else if (m_pUser->IsAdmin() && sCommand.Equals("AddMOTD")) { CString sMessage = sLine.Token(1, true); if (sMessage.empty()) { PutStatus(t_s("Usage: AddMOTD ")); } else { CZNC::Get().AddMotd(sMessage); PutStatus(t_f("Added [{1}] to MOTD")(sMessage)); } } else if (m_pUser->IsAdmin() && sCommand.Equals("ClearMOTD")) { CZNC::Get().ClearMotd(); PutStatus(t_s("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()) { // No t_s here because language of user can be different sMessage = (bRestart ? "ZNC is being restarted NOW!" : "ZNC is being shut down NOW!"); } if (!CZNC::Get().WriteConfig() && !bForce) { PutStatus( t_f("ERROR: Writing config file to disk failed! Aborting. Use " "{1} FORCE to ignore.")(sCommand.AsUpper())); } 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(t_s( "You must be connected with a network to use this command")); return; } if (!m_pNetwork->HasServers()) { PutStatus(t_s("You don't have any servers added.")); return; } CString sArgs = sLine.Token(1, true); sArgs.Trim(); CServer* pServer = nullptr; if (!sArgs.empty()) { pServer = m_pNetwork->FindServer(sArgs); if (!pServer) { PutStatus(t_f("Server [{1}] not found")(sArgs)); 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 (!pServer) { pServer = m_pNetwork->GetNextServer(false); } if (GetIRCSock()) { GetIRCSock()->Quit(); if (pServer) PutStatus(t_f("Connecting to {1}...")(pServer->GetName())); else PutStatus(t_s("Jumping to the next server in the list...")); } else { if (pServer) PutStatus(t_f("Connecting to {1}...")(pServer->GetName())); else PutStatus(t_s("Connecting...")); } m_pNetwork->SetIRCConnectEnabled(true); return; } else if (sCommand.Equals("DISCONNECT")) { if (!m_pNetwork) { PutStatus(t_s( "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(t_s("Disconnected from IRC. Use 'connect' to reconnect.")); return; } else if (sCommand.Equals("ENABLECHAN")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sPatterns = sLine.Token(1, true); if (sPatterns.empty()) { PutStatus(t_s("Usage: EnableChan <#chans>")); } else { set sChans = MatchChans(sPatterns); unsigned int uEnabled = 0; for (CChan* pChan : sChans) { if (!pChan->IsDisabled()) continue; uEnabled++; pChan->Enable(); } PutStatus(t_p("There was {1} channel matching [{2}]", "There were {1} channels matching [{2}]", sChans.size())(sChans.size(), sPatterns)); PutStatus(t_p("Enabled {1} channel", "Enabled {1} channels", uEnabled)(uEnabled)); } } else if (sCommand.Equals("DISABLECHAN")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sPatterns = sLine.Token(1, true); if (sPatterns.empty()) { PutStatus(t_s("Usage: DisableChan <#chans>")); } else { set sChans = MatchChans(sPatterns); unsigned int uDisabled = 0; for (CChan* pChan : sChans) { if (pChan->IsDisabled()) continue; uDisabled++; pChan->Disable(); } PutStatus(t_p("There was {1} channel matching [{2}]", "There were {1} channels matching [{2}]", sChans.size())(sChans.size(), sPatterns)); PutStatus(t_p("Disabled {1} channel", "Disabled {1} channels", uDisabled)(uDisabled)); } } else if (sCommand.Equals("MOVECHAN")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } const auto sChan = sLine.Token(1); const auto sTarget = sLine.Token(2); if (sChan.empty() || sTarget.empty()) { PutStatus(t_s("Usage: MoveChan <#chan> ")); return; } unsigned int uIndex = sTarget.ToUInt(); CString sError; if (m_pNetwork->MoveChan(sChan, uIndex - 1, sError)) PutStatus(t_f("Moved channel {1} to index {2}")(sChan, uIndex)); else PutStatus(sError); } else if (sCommand.Equals("SWAPCHANS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } const auto sChan1 = sLine.Token(1); const auto sChan2 = sLine.Token(2); if (sChan1.empty() || sChan2.empty()) { PutStatus(t_s("Usage: SwapChans <#chan1> <#chan2>")); return; } CString sError; if (m_pNetwork->SwapChans(sChan1, sChan2, sError)) PutStatus(t_f("Swapped channels {1} and {2}")(sChan1, sChan2)); else PutStatus(sError); } else if (sCommand.Equals("LISTCHANS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CIRCNetwork* pNetwork = m_pNetwork; const CString sUser = sLine.Token(1); const CString sNetwork = sLine.Token(2); if (!sUser.empty()) { if (!m_pUser->IsAdmin()) { PutStatus(t_s("Usage: ListChans")); return; } CUser* pUser = CZNC::Get().FindUser(sUser); if (!pUser) { PutStatus(t_f("No such user [{1}]")(sUser)); return; } pNetwork = pUser->FindNetwork(sNetwork); if (!pNetwork) { PutStatus(t_f("User [{1}] doesn't have network [{2}]")( sUser, sNetwork)); return; } } const vector& vChans = pNetwork->GetChans(); CIRCSock* pIRCSock = pNetwork->GetIRCSock(); CString sPerms = pIRCSock ? pIRCSock->GetPerms() : ""; if (vChans.empty()) { PutStatus(t_s("There are no channels defined.")); return; } CTable Table; Table.AddColumn(t_s("Index", "listchans")); Table.AddColumn(t_s("Name", "listchans")); Table.AddColumn(t_s("Status", "listchans")); Table.AddColumn(t_s("In config", "listchans")); Table.AddColumn(t_s("Buffer", "listchans")); Table.AddColumn(t_s("Clear", "listchans")); Table.AddColumn(t_s("Modes", "listchans")); Table.AddColumn(t_s("Users", "listchans")); for (char cPerm : sPerms) { Table.AddColumn(CString(cPerm)); } unsigned int uNumDetached = 0, uNumDisabled = 0, uNumJoined = 0, uChanIndex = 1; for (const CChan* pChan : vChans) { Table.AddRow(); Table.SetCell(t_s("Index", "listchans"), CString(uChanIndex)); Table.SetCell(t_s("Name", "listchans"), pChan->GetPermStr() + pChan->GetName()); Table.SetCell( t_s("Status", "listchans"), pChan->IsOn() ? (pChan->IsDetached() ? t_s("Detached", "listchans") : t_s("Joined", "listchans")) : (pChan->IsDisabled() ? t_s("Disabled", "listchans") : t_s("Trying", "listchans"))); Table.SetCell( t_s("In config", "listchans"), CString(pChan->InConfig() ? t_s("yes", "listchans") : "")); Table.SetCell(t_s("Buffer", "listchans"), CString(pChan->HasBufferCountSet() ? "*" : "") + CString(pChan->GetBufferCount())); Table.SetCell( t_s("Clear", "listchans"), CString(pChan->HasAutoClearChanBufferSet() ? "*" : "") + CString(pChan->AutoClearChanBuffer() ? t_s("yes", "listchans") : "")); Table.SetCell(t_s("Modes", "listchans"), pChan->GetModeString()); Table.SetCell(t_s("Users", "listchans"), CString(pChan->GetNickCount())); std::map mPerms = pChan->GetPermCounts(); for (char cPerm : sPerms) { Table.SetCell(CString(cPerm), CString(mPerms[cPerm])); } if (pChan->IsDetached()) uNumDetached++; if (pChan->IsOn()) uNumJoined++; if (pChan->IsDisabled()) uNumDisabled++; uChanIndex++; } PutStatus(Table); PutStatus(t_f("Total: {1}, Joined: {2}, Detached: {3}, Disabled: {4}")( vChans.size(), uNumJoined, uNumDetached, uNumDisabled)); } else if (sCommand.Equals("ADDNETWORK")) { if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) { PutStatus(t_s("Access denied!")); return; } if (!m_pUser->IsAdmin() && !m_pUser->HasSpaceForNewNetwork()) { PutStatus(t_s( "Network number limit reached. Ask an admin to increase the " "limit for you, or delete unneeded networks using /znc " "DelNetwork ")); return; } CString sNetwork = sLine.Token(1); if (sNetwork.empty()) { PutStatus(t_s("Usage: AddNetwork ")); return; } if (!CIRCNetwork::IsValidNetwork(sNetwork)) { PutStatus(t_s("Network name should be alphanumeric")); return; } CString sNetworkAddError; if (m_pUser->AddNetwork(sNetwork, sNetworkAddError)) { PutStatus( t_f("Network added. Use /znc JumpNetwork {1}, or connect to " "ZNC with username {2} (instead of just {3}) to connect to " "it.")(sNetwork, m_pUser->GetUsername() + "/" + sNetwork, m_pUser->GetUsername())); } else { PutStatus(t_s("Unable to add that network")); PutStatus(sNetworkAddError); } } else if (sCommand.Equals("DELNETWORK")) { if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) { PutStatus(t_s("Access denied!")); return; } CString sNetwork = sLine.Token(1); if (sNetwork.empty()) { PutStatus(t_s("Usage: DelNetwork ")); return; } if (m_pNetwork && m_pNetwork->GetName().Equals(sNetwork)) { SetNetwork(nullptr); } if (m_pUser->DeleteNetwork(sNetwork)) { PutStatus(t_s("Network deleted")); } else { PutStatus( t_s("Failed to delete network, 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(t_f("User {1} not found")(sLine.Token(1))); return; } } const vector& vNetworks = pUser->GetNetworks(); CTable Table; Table.AddColumn(t_s("Network", "listnetworks")); Table.AddColumn(t_s("On IRC", "listnetworks")); Table.AddColumn(t_s("IRC Server", "listnetworks")); Table.AddColumn(t_s("IRC User", "listnetworks")); Table.AddColumn(t_s("Channels", "listnetworks")); for (const CIRCNetwork* pNetwork : vNetworks) { Table.AddRow(); Table.SetCell(t_s("Network", "listnetworks"), pNetwork->GetName()); if (pNetwork->IsIRCConnected()) { Table.SetCell(t_s("On IRC", "listnetworks"), t_s("Yes", "listnetworks")); Table.SetCell(t_s("IRC Server", "listnetworks"), pNetwork->GetIRCServer()); Table.SetCell(t_s("IRC User", "listnetworks"), pNetwork->GetIRCNick().GetNickMask()); Table.SetCell(t_s("Channels", "listnetworks"), CString(pNetwork->GetChans().size())); } else { Table.SetCell(t_s("On IRC", "listnetworks"), t_s("No", "listnetworks")); } } if (PutStatus(Table) == 0) { PutStatus(t_s("No networks", "listnetworks")); } } else if (sCommand.Equals("MOVENETWORK")) { if (!m_pUser->IsAdmin()) { PutStatus(t_s("Access denied.")); return; } CString sOldUser = sLine.Token(1); CString sOldNetwork = sLine.Token(2); CString sNewUser = sLine.Token(3); CString sNewNetwork = sLine.Token(4); if (sOldUser.empty() || sOldNetwork.empty() || sNewUser.empty()) { PutStatus(t_s( "Usage: MoveNetwork [new " "network]")); return; } if (sNewNetwork.empty()) { sNewNetwork = sOldNetwork; } CUser* pOldUser = CZNC::Get().FindUser(sOldUser); if (!pOldUser) { PutStatus(t_f("Old user {1} not found.")(sOldUser)); return; } CIRCNetwork* pOldNetwork = pOldUser->FindNetwork(sOldNetwork); if (!pOldNetwork) { PutStatus(t_f("Old network {1} not found.")(sOldNetwork)); return; } CUser* pNewUser = CZNC::Get().FindUser(sNewUser); if (!pNewUser) { PutStatus(t_f("New user {1} not found.")(sNewUser)); return; } if (pNewUser->FindNetwork(sNewNetwork)) { PutStatus(t_f("User {1} already has network {2}.")(sNewUser, sNewNetwork)); return; } if (!CIRCNetwork::IsValidNetwork(sNewNetwork)) { PutStatus(t_f("Invalid network name [{1}]")(sNewNetwork)); return; } const CModules& vMods = pOldNetwork->GetModules(); for (CModule* pMod : vMods) { CString sOldModPath = pOldNetwork->GetNetworkPath() + "/moddata/" + pMod->GetModName(); CString sNewModPath = pNewUser->GetUserPath() + "/networks/" + sNewNetwork + "/moddata/" + pMod->GetModName(); CDir oldDir(sOldModPath); for (CFile* pFile : oldDir) { if (pFile->GetShortName() != ".registry") { PutStatus( t_f("Some files seem to be in {1}. You might want to " "move them to {2}")(sOldModPath, sNewModPath)); break; } } pMod->MoveRegistry(sNewModPath); } CString sNetworkAddError; CIRCNetwork* pNewNetwork = pNewUser->AddNetwork(sNewNetwork, sNetworkAddError); if (!pNewNetwork) { PutStatus(t_f("Error adding network: {1}")(sNetworkAddError)); return; } pNewNetwork->Clone(*pOldNetwork, false); if (m_pNetwork && m_pNetwork->GetName().Equals(sOldNetwork) && m_pUser == pOldUser) { SetNetwork(nullptr); } if (pOldUser->DeleteNetwork(sOldNetwork)) { PutStatus(t_s("Success.")); } else { PutStatus( t_s("Copied the network to new user, but failed to delete old " "network")); } } else if (sCommand.Equals("JUMPNETWORK")) { CString sNetwork = sLine.Token(1); if (sNetwork.empty()) { PutStatus(t_s("No network supplied.")); return; } if (m_pNetwork && (m_pNetwork->GetName() == sNetwork)) { PutStatus(t_s("You are already connected with this network.")); return; } CIRCNetwork* pNetwork = m_pUser->FindNetwork(sNetwork); if (pNetwork) { PutStatus(t_f("Switched to {1}")(sNetwork)); SetNetwork(pNetwork); } else { PutStatus(t_f("You don't have a network named {1}")(sNetwork)); } } else if (sCommand.Equals("ADDSERVER")) { if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) { PutStatus(t_s("Access denied!")); return; } CString sServer = sLine.Token(1, true); if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } if (sServer.empty()) { PutStatus(t_s("Usage: AddServer [[+]port] [pass]")); if (m_pUser->IsAdmin()) { PutStatus(t_s("Or: AddServer unix:[ssl:]/path/to/socket")); } PutStatus(t_s("+ means SSL")); return; } CServer Server = CServer::Parse(sServer); if (Server.IsUnixSocket() && !m_pUser->IsAdmin()) { PutStatus(t_s("Access denied!")); return; } if (m_pNetwork->AddServer(std::move(Server))) { PutStatus(t_s("Server added")); } else { PutStatus( t_s("Unable to add that server. Perhaps the server is already " "added or openssl is disabled?")); } } else if (sCommand.Equals("REMSERVER") || sCommand.Equals("DELSERVER")) { if (!m_pUser->IsAdmin() && m_pUser->DenySetNetwork()) { PutStatus(t_s("Access denied!")); return; } if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CServer Server = CServer::Parse(sLine.Token(1, true)); if (Server.GetName().empty()) { PutStatus(t_s("Usage: DelServer [port] [pass]")); return; } if (!m_pNetwork->HasServers()) { PutStatus(t_s("You don't have any servers added.")); return; } // Unix sockets can be removed with "unix:" prefix and without, both // work. if (m_pNetwork->DelServer(Server)) { PutStatus(t_s("Server removed")); } else { PutStatus(t_s("No such server")); } } else if (sCommand.Equals("LISTSERVERS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } if (m_pNetwork->HasServers()) { const vector& vServers = m_pNetwork->GetServers(); CServer* pCurServ = m_pNetwork->GetCurrentServer(); CTable Table; Table.AddColumn(t_s("Host", "listservers")); Table.AddColumn(t_s("Port", "listservers")); Table.AddColumn(t_s("SSL", "listservers")); Table.AddColumn(t_s("Password", "listservers")); for (const CServer* pServer : vServers) { Table.AddRow(); Table.SetCell( t_s("Host", "listservers"), (pServer->IsUnixSocket() ? pServer->GetString(false) : pServer->GetName()) + (pServer == pCurServ ? "*" : "")); if (!pServer->IsUnixSocket()) Table.SetCell(t_s("Port", "listservers"), CString(pServer->GetPort())); Table.SetCell( t_s("SSL", "listservers"), (pServer->IsSSL()) ? t_s("SSL", "listservers|cell") : ""); Table.SetCell(t_s("Password", "listservers"), pServer->GetPass().empty() ? "" : "******"); } PutStatus(Table); } else { PutStatus(t_s("You don't have any servers added.")); } } else if (sCommand.Equals("AddTrustedServerFingerprint")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sFP = sLine.Token(1); if (sFP.empty()) { PutStatus(t_s("Usage: AddTrustedServerFingerprint ")); return; } m_pNetwork->AddTrustedFingerprint(sFP); PutStatus(t_s("Done.")); } else if (sCommand.Equals("DelTrustedServerFingerprint")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sFP = sLine.Token(1); if (sFP.empty()) { PutStatus(t_s("Usage: DelTrustedServerFingerprint ")); return; } m_pNetwork->DelTrustedFingerprint(sFP); PutStatus(t_s("Done.")); } else if (sCommand.Equals("ListTrustedServerFingerprints")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } const SCString& ssFPs = m_pNetwork->GetTrustedFingerprints(); if (ssFPs.empty()) { PutStatus(t_s("No fingerprints added.")); } else { int k = 0; for (const CString& sFP : ssFPs) { PutStatus(CString(++k) + ". " + sFP); } } } else if (sCommand.Equals("TOPICS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } const vector& vChans = m_pNetwork->GetChans(); CTable Table; Table.AddColumn(t_s("Channel", "topicscmd")); Table.AddColumn(t_s("Set By", "topicscmd")); Table.AddColumn(t_s("Topic", "topicscmd")); for (const CChan* pChan : vChans) { Table.AddRow(); Table.SetCell(t_s("Channel", "topicscmd"), pChan->GetName()); Table.SetCell(t_s("Set By", "topicscmd"), pChan->GetTopicOwner()); Table.SetCell(t_s("Topic", "topicscmd"), pChan->GetTopic()); } PutStatus(Table); } else if (sCommand.Equals("LISTMODS") || sCommand.Equals("LISTMODULES")) { const auto PrintModules = [this](const CModules& Modules) { CTable Table; Table.AddColumn(t_s("Name", "listmods")); Table.AddColumn(t_s("Arguments", "listmods")); Table.SetStyle(CTable::ListStyle); for (const CModule* pMod : Modules) { Table.AddRow(); Table.SetCell(t_s("Name", "listmods"), pMod->GetModName()); Table.SetCell(t_s("Arguments", "listmods"), pMod->GetArgs()); } PutStatus(Table); }; if (m_pUser->IsAdmin()) { const CModules& GModules = CZNC::Get().GetModules(); PutStatus(""); if (!GModules.size()) { PutStatus(t_s("No global modules loaded.")); } else { PutStatus(t_s("Global modules:")); PrintModules(GModules); } } const CModules& UModules = m_pUser->GetModules(); PutStatus(""); if (!UModules.size()) { PutStatus(t_s("Your user has no modules loaded.")); } else { PutStatus(t_s("User modules:")); PrintModules(UModules); } if (m_pNetwork) { const CModules& NetworkModules = m_pNetwork->GetModules(); PutStatus(""); if (NetworkModules.empty()) { PutStatus(t_s("This network has no modules loaded.")); } else { PutStatus(t_s("Network modules:")); PrintModules(NetworkModules); } } return; } else if (sCommand.Equals("LISTAVAILMODS") || sCommand.Equals("LISTAVAILABLEMODULES")) { if (m_pUser->DenyLoadMod()) { PutStatus(t_s("Access denied.")); return; } const auto PrintModules = [this](const set& ssModules) { CTable Table; Table.AddColumn(t_s("Name", "listavailmods")); Table.AddColumn(t_s("Description", "listavailmods")); Table.SetStyle(CTable::ListStyle); for (const CModInfo& Info : ssModules) { Table.AddRow(); Table.SetCell( t_s("Name", "listavailmods"), (CZNC::Get().GetModules().FindModule(Info.GetName()) ? "*" : " ") + Info.GetName()); Table.SetCell(t_s("Description", "listavailmods"), Info.GetDescription().Ellipsize(128)); } PutStatus(Table); }; if (m_pUser->IsAdmin()) { set ssGlobalMods; CZNC::Get().GetModules().GetAvailableMods(ssGlobalMods, CModInfo::GlobalModule); PutStatus(""); if (ssGlobalMods.empty()) { PutStatus(t_s("No global modules available.")); } else { PutStatus(t_s("Global modules:")); PrintModules(ssGlobalMods); } } set ssUserMods; CZNC::Get().GetModules().GetAvailableMods(ssUserMods); PutStatus(""); if (ssUserMods.empty()) { PutStatus(t_s("No user modules available.")); } else { PutStatus(t_s("User modules:")); PrintModules(ssUserMods); } set ssNetworkMods; CZNC::Get().GetModules().GetAvailableMods(ssNetworkMods, CModInfo::NetworkModule); PutStatus(""); if (ssNetworkMods.empty()) { PutStatus(t_s("No network modules available.")); } else { PutStatus(t_s("Network modules:")); PrintModules(ssNetworkMods); } 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); // TODO use proper library for parsing arguments if (sType.Equals("--type=global")) { eType = CModInfo::GlobalModule; } else if (sType.Equals("--type=user")) { eType = CModInfo::UserModule; } else if (sType.Equals("--type=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(t_f("Unable to load {1}: Access denied.")(sMod)); return; } if (sMod.empty()) { PutStatus(t_s( "Usage: LoadMod [--type=global|user|network] [args]")); return; } CModInfo ModInfo; CString sRetMsg; if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) { PutStatus(t_f("Unable to load {1}: {2}")(sMod, sRetMsg)); return; } if (sType.Equals("default")) { eType = ModInfo.GetDefaultType(); } if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) { PutStatus( t_f("Unable to load global module {1}: Access denied.")(sMod)); return; } if (eType == CModInfo::NetworkModule && !m_pNetwork) { PutStatus( t_f("Unable to load network module {1}: Not connected with a " "network.")(sMod)); return; } CString sModRet; bool bLoaded = false; switch (eType) { case CModInfo::GlobalModule: bLoaded = CZNC::Get().GetModules().LoadModule( sMod, sArgs, eType, nullptr, nullptr, sModRet); break; case CModInfo::UserModule: bLoaded = m_pUser->GetModules().LoadModule( sMod, sArgs, eType, m_pUser, nullptr, sModRet); break; case CModInfo::NetworkModule: bLoaded = m_pNetwork->GetModules().LoadModule( sMod, sArgs, eType, m_pUser, m_pNetwork, sModRet); break; default: sModRet = t_s("Unknown module type"); } if (bLoaded) { if (sModRet.empty()) { PutStatus(t_f("Loaded module {1}")(sMod)); } else { PutStatus(t_f("Loaded module {1}: {2}")(sMod, sModRet)); } } else { PutStatus(t_f("Unable to load module {1}: {2}")(sMod, 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); // TODO use proper library for parsing arguments if (sType.Equals("--type=global")) { eType = CModInfo::GlobalModule; } else if (sType.Equals("--type=user")) { eType = CModInfo::UserModule; } else if (sType.Equals("--type=network")) { eType = CModInfo::NetworkModule; } else { sMod = sType; sType = "default"; } if (m_pUser->DenyLoadMod()) { PutStatus(t_f("Unable to unload {1}: Access denied.")(sMod)); return; } if (sMod.empty()) { PutStatus( t_s("Usage: UnloadMod [--type=global|user|network] ")); return; } if (sType.Equals("default")) { CModInfo ModInfo; CString sRetMsg; if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) { PutStatus( t_f("Unable to determine type of {1}: {2}")(sMod, sRetMsg)); return; } eType = ModInfo.GetDefaultType(); } if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) { PutStatus(t_f("Unable to unload global module {1}: Access denied.")( sMod)); return; } if (eType == CModInfo::NetworkModule && !m_pNetwork) { PutStatus( t_f("Unable to unload network module {1}: Not connected with a " "network.")(sMod)); 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 = t_f( "Unable to unload module {1}: Unknown module type")(sMod); } 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(t_s("Unable to reload modules. Access denied.")); return; } // TODO use proper library for parsing arguments if (sType.Equals("--type=global")) { eType = CModInfo::GlobalModule; } else if (sType.Equals("--type=user")) { eType = CModInfo::UserModule; } else if (sType.Equals("--type=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( t_s("Usage: ReloadMod [--type=global|user|network] " "[args]")); return; } if (sType.Equals("default")) { CModInfo ModInfo; CString sRetMsg; if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sMod, sRetMsg)) { PutStatus(t_f("Unable to reload {1}: {2}")(sMod, sRetMsg)); return; } eType = ModInfo.GetDefaultType(); } if (eType == CModInfo::GlobalModule && !m_pUser->IsAdmin()) { PutStatus(t_f("Unable to reload global module {1}: Access denied.")( sMod)); return; } if (eType == CModInfo::NetworkModule && !m_pNetwork) { PutStatus( t_f("Unable to reload network module {1}: Not connected with a " "network.")(sMod)); return; } CString sModRet; switch (eType) { case CModInfo::GlobalModule: CZNC::Get().GetModules().ReloadModule(sMod, sArgs, nullptr, nullptr, sModRet); break; case CModInfo::UserModule: m_pUser->GetModules().ReloadModule(sMod, sArgs, m_pUser, nullptr, sModRet); break; case CModInfo::NetworkModule: m_pNetwork->GetModules().ReloadModule(sMod, sArgs, m_pUser, m_pNetwork, sModRet); break; default: sModRet = t_f( "Unable to reload module {1}: Unknown module type")(sMod); } PutStatus(sModRet); return; } else if ((sCommand.Equals("UPDATEMOD") || sCommand.Equals("UPDATEMODULE")) && m_pUser->IsAdmin()) { CString sMod = sLine.Token(1); if (sMod.empty()) { PutStatus(t_s("Usage: UpdateMod ")); return; } PutStatus(t_f("Reloading {1} everywhere")(sMod)); if (CZNC::Get().UpdateModule(sMod)) { PutStatus(t_s("Done")); } else { PutStatus( t_f("Done, but there were errors, module {1} could not be " "reloaded everywhere.")(sMod)); } } else if ((sCommand.Equals("SETBINDHOST") || sCommand.Equals("SETVHOST")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command. Try " "SetUserBindHost instead")); return; } CString sArg = sLine.Token(1); if (sArg.empty()) { PutStatus(t_s("Usage: SetBindHost ")); return; } if (sArg.Equals(m_pNetwork->GetBindHost())) { PutStatus(t_s("You already have this bind host!")); return; } m_pNetwork->SetBindHost(sArg); PutStatus(t_f("Set bind host for network {1} to {2}")( m_pNetwork->GetName(), m_pNetwork->GetBindHost())); } else if (sCommand.Equals("SETUSERBINDHOST") && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) { CString sArg = sLine.Token(1); if (sArg.empty()) { PutStatus(t_s("Usage: SetUserBindHost ")); return; } if (sArg.Equals(m_pUser->GetBindHost())) { PutStatus(t_s("You already have this bind host!")); return; } m_pUser->SetBindHost(sArg); PutStatus(t_f("Set default bind host to {1}")(m_pUser->GetBindHost())); } else if ((sCommand.Equals("CLEARBINDHOST") || sCommand.Equals("CLEARVHOST")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command. Try " "ClearUserBindHost instead")); return; } m_pNetwork->SetBindHost(""); PutStatus(t_s("Bind host cleared for this network.")); } else if (sCommand.Equals("CLEARUSERBINDHOST") && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) { m_pUser->SetBindHost(""); PutStatus(t_s("Default bind host cleared for your user.")); } else if (sCommand.Equals("SHOWBINDHOST")) { if (m_pUser->GetBindHost().empty()) { PutStatus(t_s("This user's default bind host not set")); } else { PutStatus(t_f("This user's default bind host is {1}")( m_pUser->GetBindHost())); } if (m_pNetwork) { if (m_pNetwork->GetBindHost().empty()) { PutStatus(t_s("This network's bind host not set")); } else { PutStatus(t_f("This network's default bind host is {1}")( m_pNetwork->GetBindHost())); } } } else if (sCommand.Equals("PLAYBUFFER")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sBuffer = sLine.Token(1); if (sBuffer.empty()) { PutStatus(t_s("Usage: PlayBuffer <#chan|query>")); return; } if (m_pNetwork->IsChan(sBuffer)) { CChan* pChan = m_pNetwork->FindChan(sBuffer); if (!pChan) { PutStatus(t_f("You are not on {1}")(sBuffer)); return; } if (!pChan->IsOn()) { PutStatus(t_f("You are not on {1} (trying to join)")(sBuffer)); return; } if (pChan->GetBuffer().IsEmpty()) { PutStatus(t_f("The buffer for channel {1} is empty")(sBuffer)); return; } pChan->SendBuffer(this); } else { CQuery* pQuery = m_pNetwork->FindQuery(sBuffer); if (!pQuery) { PutStatus(t_f("No active query with {1}")(sBuffer)); return; } if (pQuery->GetBuffer().IsEmpty()) { PutStatus(t_f("The buffer for {1} is empty")(sBuffer)); return; } pQuery->SendBuffer(this); } } else if (sCommand.Equals("CLEARBUFFER")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sBuffer = sLine.Token(1); if (sBuffer.empty()) { PutStatus(t_s("Usage: ClearBuffer <#chan|query>")); return; } unsigned int uMatches = 0; vector vChans = m_pNetwork->FindChans(sBuffer); for (CChan* pChan : vChans) { uMatches++; pChan->ClearBuffer(); } vector vQueries = m_pNetwork->FindQueries(sBuffer); for (CQuery* pQuery : vQueries) { uMatches++; m_pNetwork->DelQuery(pQuery->GetName()); } PutStatus(t_p("{1} buffer matching {2} has been cleared", "{1} buffers matching {2} have been cleared", uMatches)(uMatches, sBuffer)); } else if (sCommand.Equals("CLEARALLCHANNELBUFFERS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } for (CChan* pChan : m_pNetwork->GetChans()) { pChan->ClearBuffer(); } PutStatus(t_s("All channel buffers have been cleared")); } else if (sCommand.Equals("CLEARALLQUERYBUFFERS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } m_pNetwork->ClearQueryBuffer(); PutStatus(t_s("All query buffers have been cleared")); } else if (sCommand.Equals("CLEARALLBUFFERS")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } for (CChan* pChan : m_pNetwork->GetChans()) { pChan->ClearBuffer(); } m_pNetwork->ClearQueryBuffer(); PutStatus(t_s("All buffers have been cleared")); } else if (sCommand.Equals("SETBUFFER")) { if (!m_pNetwork) { PutStatus(t_s( "You must be connected with a network to use this command")); return; } CString sBuffer = sLine.Token(1); if (sBuffer.empty()) { PutStatus(t_s("Usage: SetBuffer <#chan|query> [linecount]")); return; } unsigned int uLineCount = sLine.Token(2).ToUInt(); unsigned int uMatches = 0, uFail = 0; vector vChans = m_pNetwork->FindChans(sBuffer); for (CChan* pChan : vChans) { uMatches++; if (!pChan->SetBufferCount(uLineCount)) uFail++; } vector vQueries = m_pNetwork->FindQueries(sBuffer); for (CQuery* pQuery : vQueries) { uMatches++; if (!pQuery->SetBufferCount(uLineCount)) uFail++; } if (uFail > 0) { PutStatus(t_p("Setting buffer size failed for {1} buffer", "Setting buffer size failed for {1} buffers", uFail)(uFail)); PutStatus(t_p("Maximum buffer size is {1} line", "Maximum buffer size is {1} lines", CZNC::Get().GetMaxBufferSize())( CZNC::Get().GetMaxBufferSize())); } else { PutStatus(t_p("Size of every buffer was set to {1} line", "Size of every buffer was set to {1} lines", uLineCount)(uLineCount)); } } else if (m_pUser->IsAdmin() && sCommand.Equals("TRAFFIC")) { CZNC::TrafficStatsPair Users, ZNC, Total; CZNC::TrafficStatsMap traffic = CZNC::Get().GetTrafficStats(Users, ZNC, Total); CTable Table; Table.AddColumn(t_s("Username", "trafficcmd")); Table.AddColumn(t_s("In", "trafficcmd")); Table.AddColumn(t_s("Out", "trafficcmd")); Table.AddColumn(t_s("Total", "trafficcmd")); for (const auto& it : traffic) { Table.AddRow(); Table.SetCell(t_s("Username", "trafficcmd"), it.first); Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(it.second.first)); Table.SetCell(t_s("Out", "trafficcmd"), CString::ToByteStr(it.second.second)); Table.SetCell( t_s("Total", "trafficcmd"), CString::ToByteStr(it.second.first + it.second.second)); } Table.AddRow(); Table.SetCell(t_s("Username", "trafficcmd"), t_s("", "trafficcmd")); Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(Users.first)); Table.SetCell(t_s("Out", "trafficcmd"), CString::ToByteStr(Users.second)); Table.SetCell(t_s("Total", "trafficcmd"), CString::ToByteStr(Users.first + Users.second)); Table.AddRow(); Table.SetCell(t_s("Username", "trafficcmd"), t_s("", "trafficcmd")); Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(ZNC.first)); Table.SetCell(t_s("Out", "trafficcmd"), CString::ToByteStr(ZNC.second)); Table.SetCell(t_s("Total", "trafficcmd"), CString::ToByteStr(ZNC.first + ZNC.second)); Table.AddRow(); Table.SetCell(t_s("Username", "trafficcmd"), t_s("", "trafficcmd")); Table.SetCell(t_s("In", "trafficcmd"), CString::ToByteStr(Total.first)); Table.SetCell(t_s("Out", "trafficcmd"), CString::ToByteStr(Total.second)); Table.SetCell(t_s("Total", "trafficcmd"), CString::ToByteStr(Total.first + Total.second)); PutStatus(Table); } else if (sCommand.Equals("UPTIME")) { PutStatus(t_f("Running for {1}")(CZNC::Get().GetUptime())); } else if (m_pUser->IsAdmin() && (sCommand.Equals("LISTPORTS") || sCommand.Equals("ADDPORT") || sCommand.Equals("DELPORT"))) { UserPortCommand(sLine); } else { PutStatus(t_s("Unknown command, try 'Help'")); } } namespace { struct PortCommandUsage {}; } void CClient::UserPortCommand(CString& sLine) { const CString sCommand = sLine.Token(0); if (sCommand.Equals("LISTPORTS")) { CTable Table; Table.AddColumn(t_s("Port", "listports")); Table.AddColumn(t_s("BindHost", "listports")); Table.AddColumn(t_s("SSL", "listports")); Table.AddColumn(t_s("Protocol", "listports")); Table.AddColumn(t_s("IRC", "listports")); Table.AddColumn(t_s("Web", "listports")); const vector& vpListeners = CZNC::Get().GetListeners(); for (const CListener* pListener : vpListeners) { Table.AddRow(); if (const CTCPListener* pTCPListener = dynamic_cast(pListener)) { Table.SetCell(t_s("Port", "listports"), CString(pTCPListener->GetPort())); Table.SetCell(t_s("BindHost", "listports"), (pTCPListener->GetBindHost().empty() ? CString("*") : pTCPListener->GetBindHost())); EAddrType eAddr = pTCPListener->GetAddrType(); Table.SetCell( t_s("Protocol", "listports"), eAddr == ADDR_ALL ? t_s("IPv4 and IPv6", "listports") : (eAddr == ADDR_IPV4ONLY ? t_s("IPv4", "listports") : t_s("IPv6", "listports"))); } else if (const CUnixListener* pUnixListener = dynamic_cast(pListener)) { Table.SetCell(t_s("Port", "listports"), pUnixListener->GetPath()); } Table.SetCell(t_s("SSL", "listports"), pListener->IsSSL() ? t_s("yes", "listports|ssl") : t_s("no", "listports|ssl")); CListener::EAcceptType eAccept = pListener->GetAcceptType(); Table.SetCell(t_s("IRC", "listports"), eAccept == CListener::ACCEPT_ALL || eAccept == CListener::ACCEPT_IRC ? t_s("yes", "listports|irc") : t_s("no", "listports|irc")); Table.SetCell(t_s("Web", "listports"), eAccept == CListener::ACCEPT_ALL || eAccept == CListener::ACCEPT_HTTP ? t_f("yes, on {1}", "listports|irc")( pListener->GetURIPrefix() + "/") : t_s("no", "listports|web")); } PutStatus(Table); return; } auto ParseEAddr = [](const CString& sAddr) { if (sAddr.Equals("IPV4")) { return ADDR_IPV4ONLY; } else if (sAddr.Equals("IPV6")) { return ADDR_IPV6ONLY; } else if (sAddr.Equals("ALL")) { return ADDR_ALL; } else { throw PortCommandUsage{}; } }; auto ParseEAccept = [](const CString& sAccept) { if (sAccept.Equals("WEB")) { return CListener::ACCEPT_HTTP; } else if (sAccept.Equals("IRC")) { return CListener::ACCEPT_IRC; } else if (sAccept.Equals("ALL")) { return CListener::ACCEPT_ALL; } else { throw PortCommandUsage{}; } }; CString sPort = sLine.Token(1); unsigned short uPort = sPort.ToUShort(); if (sCommand.Equals("ADDPORT")) { try { if (sPort.empty()) { throw PortCommandUsage{}; } std::unique_ptr pListener; if (sPort.TrimPrefix("unix:")) { bool bSSL = sPort.TrimPrefix("+"); const CString& sPath = sPort; CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(2)); CString sURIPrefix = sLine.Token(3); pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept)); } else { bool bSSL = sPort.StartsWith("+"); EAddrType eAddr = ParseEAddr(sLine.Token(2)); CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(3)); const CString sBindHost = sLine.Token(4); const CString sURIPrefix = sLine.Token(5); pListener.reset(new CTCPListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept)); } if (!pListener->Listen()) { auto e = errno; PutStatus(t_f("Unable to bind: {1}")(CString(strerror(e)))); } else { if (CZNC::Get().AddListener(pListener.release())) { PutStatus(t_s("Port added")); } else { PutStatus(t_s("Couldn't add port")); } } } catch (PortCommandUsage) { PutStatus( t_s("Usage: AddPort <[+]port> " "[bindhost [uriprefix]]")); PutStatus( t_s("Or: AddPort unix:[+]/path/to/socket " "[uriprefix]")); PutStatus(t_s("+ means SSL")); } } else if (sCommand.Equals("DELPORT")) { try { if (sPort.empty()) { throw PortCommandUsage{}; } CListener* pListener; if (sPort.TrimPrefix("unix:")) { sPort.TrimPrefix("+"); pListener = CZNC::Get().FindUnixListener(sPort); } else { CString sAddr = sLine.Token(2); CString sBindHost = sLine.Token(3); pListener = CZNC::Get().FindListener( uPort, sBindHost, ParseEAddr(sAddr)); } if (pListener) { CZNC::Get().DelListener(pListener); PutStatus(t_s("Deleted Port")); } else { PutStatus(t_s("Unable to find a matching port")); } } catch (PortCommandUsage) { PutStatus(t_s("Usage: DelPort [bindhost]")); PutStatus(t_s("Or: DelPort unix:/path/to/socket")); } } } void CClient::HelpUser(const CString& sFilter) { CTable Table; Table.AddColumn(t_s("Command", "helpcmd")); Table.AddColumn(t_s("Description", "helpcmd")); Table.SetStyle(CTable::ListStyle); if (sFilter.empty()) { PutStatus( t_s("In the following list all occurrences of <#chan> support " "wildcards (* and ?) except ListNicks")); } const auto AddCommandHelp = [&](const CString& sCmd, const CString& sArgs, const CString& sDesc) { if (sFilter.empty() || sCmd.StartsWith(sFilter) || sCmd.AsLower().WildCmp(sFilter.AsLower())) { Table.AddRow(); Table.SetCell(t_s("Command", "helpcmd"), sCmd + (sArgs.empty() ? "" : " ") + sArgs); Table.SetCell(t_s("Description", "helpcmd"), sDesc); } }; AddCommandHelp( "Version", "", t_s("Print which version of ZNC this is", "helpcmd|Version|desc")); AddCommandHelp("ListMods", "", t_s("List all loaded modules", "helpcmd|ListMods|desc")); AddCommandHelp( "ListAvailMods", "", t_s("List all available modules", "helpcmd|ListAvailMods|desc")); if (!m_pUser->IsAdmin()) { // If they are an admin we will add this command below with an argument AddCommandHelp("ListChans", "", t_s("List all channels", "helpcmd|ListChans|desc")); } AddCommandHelp( "ListNicks", t_s("<#chan>", "helpcmd|ListNicks|args"), t_s("List all nicks on a channel", "helpcmd|ListNicks|desc")); if (!m_pUser->IsAdmin()) { AddCommandHelp("ListClients", "", t_s("List all clients connected to your ZNC user", "helpcmd|ListClients|desc")); } AddCommandHelp("ListServers", "", t_s("List all servers of current IRC network", "helpcmd|ListServers|desc")); AddCommandHelp( "AddNetwork", t_s("", "helpcmd|AddNetwork|args"), t_s("Add a network to your user", "helpcmd|AddNetwork|desc")); AddCommandHelp( "DelNetwork", t_s("", "helpcmd|DelNetwork|args"), t_s("Delete a network from your user", "helpcmd|DelNetwork|desc")); AddCommandHelp("ListNetworks", "", t_s("List all networks", "helpcmd|ListNetworks|desc")); if (m_pUser->IsAdmin()) { AddCommandHelp("MoveNetwork", t_s(" [new network]", "helpcmd|MoveNetwork|args"), t_s("Move an IRC network from one user to another", "helpcmd|MoveNetwork|desc")); } AddCommandHelp( "JumpNetwork", t_s("", "helpcmd|JumpNetwork|args"), t_s("Jump to another network (Alternatively, you can connect to ZNC " "several times, using `user/network` as username)", "helpcmd|JumpNetwork|desc")); AddCommandHelp("AddServer", t_s(" [[+]port] [pass]", "helpcmd|AddServer|args"), t_s("Add a server to the list of alternate/backup servers " "of current IRC network.", "helpcmd|AddServer|desc")); AddCommandHelp("DelServer", t_s(" [port] [pass]", "helpcmd|DelServer|args"), t_s("Remove a server from the list of alternate/backup " "servers of current IRC network", "helpcmd|DelServer|desc")); AddCommandHelp( "AddTrustedServerFingerprint", t_s("", "helpcmd|AddTrustedServerFingerprint|args"), t_s("Add a trusted server SSL certificate fingerprint (SHA-256) to " "current IRC network.", "helpcmd|AddTrustedServerFingerprint|desc")); AddCommandHelp( "DelTrustedServerFingerprint", t_s("", "helpcmd|DelTrustedServerFingerprint|args"), t_s("Delete a trusted server SSL certificate from current IRC network.", "helpcmd|DelTrustedServerFingerprint|desc")); AddCommandHelp( "ListTrustedServerFingerprints", "", t_s("List all trusted server SSL certificates of current IRC network.", "helpcmd|ListTrustedServerFingerprints|desc")); AddCommandHelp("EnableChan", t_s("<#chans>", "helpcmd|EnableChan|args"), t_s("Enable channels", "helpcmd|EnableChan|desc")); AddCommandHelp("DisableChan", t_s("<#chans>", "helpcmd|DisableChan|args"), t_s("Disable channels", "helpcmd|DisableChan|desc")); AddCommandHelp("MoveChan", t_s("<#chan> ", "helpcmd|MoveChan|args"), t_s("Move channel in sort order", "helpcmd|MoveChan|desc")); AddCommandHelp( "SwapChans", t_s("<#chan1> <#chan2>", "helpcmd|SwapChans|args"), t_s("Swap channels in sort order", "helpcmd|SwapChans|desc")); AddCommandHelp("Attach", t_s("<#chans>", "helpcmd|Attach|args"), t_s("Attach to channels", "helpcmd|Attach|desc")); AddCommandHelp("Detach", t_s("<#chans>", "helpcmd|Detach|args"), t_s("Detach from channels", "helpcmd|Detach|desc")); AddCommandHelp( "Topics", "", t_s("Show topics in all your channels", "helpcmd|Topics|desc")); AddCommandHelp( "PlayBuffer", t_s("<#chan|query>", "helpcmd|PlayBuffer|args"), t_s("Play back the specified buffer", "helpcmd|PlayBuffer|desc")); AddCommandHelp( "ClearBuffer", t_s("<#chan|query>", "helpcmd|ClearBuffer|args"), t_s("Clear the specified buffer", "helpcmd|ClearBuffer|desc")); AddCommandHelp("ClearAllBuffers", "", t_s("Clear all channel and query buffers", "helpcmd|ClearAllBuffers|desc")); AddCommandHelp("ClearAllChannelBuffers", "", t_s("Clear the channel buffers", "helpcmd|ClearAllChannelBuffers|desc")); AddCommandHelp( "ClearAllQueryBuffers", "", t_s("Clear the query buffers", "helpcmd|ClearAllQueryBuffers|desc")); AddCommandHelp("SetBuffer", t_s("<#chan|query> [linecount]", "helpcmd|SetBuffer|args"), t_s("Set the buffer count", "helpcmd|SetBuffer|desc")); if (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost()) { AddCommandHelp("SetBindHost", t_s("", "helpcmd|SetBindHost|args"), t_s("Set the bind host for this network", "helpcmd|SetBindHost|desc")); AddCommandHelp( "SetUserBindHost", t_s("", "helpcmd|SetUserBindHost|args"), t_s("Set the default bind host for this user", "helpcmd|SetUserBindHost|desc")); AddCommandHelp("ClearBindHost", "", t_s("Clear the bind host for this network", "helpcmd|ClearBindHost|desc")); AddCommandHelp("ClearUserBindHost", "", t_s("Clear the default bind host for this user", "helpcmd|ClearUserBindHost|desc")); } AddCommandHelp( "ShowBindHost", "", t_s("Show currently selected bind host", "helpcmd|ShowBindHost|desc")); AddCommandHelp( "Jump", t_s("[server]", "helpcmd|Jump|args"), t_s("Jump to the next or the specified server", "helpcmd|Jump|desc")); AddCommandHelp("Disconnect", t_s("[message]", "helpcmd|Disconnect|args"), t_s("Disconnect from IRC", "helpcmd|Disconnect|desc")); AddCommandHelp("Connect", "", t_s("Reconnect to IRC", "helpcmd|Connect|desc")); AddCommandHelp( "Uptime", "", t_s("Show for how long ZNC has been running", "helpcmd|Uptime|desc")); if (!m_pUser->DenyLoadMod()) { AddCommandHelp("LoadMod", t_s("[--type=global|user|network] [args]", "helpcmd|LoadMod|args"), t_s("Load a module", "helpcmd|LoadMod|desc")); AddCommandHelp("UnloadMod", t_s("[--type=global|user|network] ", "helpcmd|UnloadMod|args"), t_s("Unload a module", "helpcmd|UnloadMod|desc")); AddCommandHelp("ReloadMod", t_s("[--type=global|user|network] [args]", "helpcmd|ReloadMod|args"), t_s("Reload a module", "helpcmd|ReloadMod|desc")); if (m_pUser->IsAdmin()) { AddCommandHelp( "UpdateMod", t_s("", "helpcmd|UpdateMod|args"), t_s("Reload a module everywhere", "helpcmd|UpdateMod|desc")); } } AddCommandHelp( "ShowMOTD", "", t_s("Show ZNC's message of the day", "helpcmd|ShowMOTD|desc")); if (m_pUser->IsAdmin()) { AddCommandHelp( "SetMOTD", t_s("", "helpcmd|SetMOTD|args"), t_s("Set ZNC's message of the day", "helpcmd|SetMOTD|desc")); AddCommandHelp( "AddMOTD", t_s("", "helpcmd|AddMOTD|args"), t_s("Append to ZNC's MOTD", "helpcmd|AddMOTD|desc")); AddCommandHelp("ClearMOTD", "", t_s("Clear ZNC's MOTD", "helpcmd|ClearMOTD|desc")); AddCommandHelp( "ListPorts", "", t_s("Show all active listeners", "helpcmd|ListPorts|desc")); AddCommandHelp("AddPort", t_s("<[+]port> [bindhost " "[uriprefix]]", "helpcmd|AddPort|args"), t_s("Add another port for ZNC to listen on", "helpcmd|AddPort|desc")); AddCommandHelp( "DelPort", t_s(" [bindhost]", "helpcmd|DelPort|args"), t_s("Remove a port from ZNC", "helpcmd|DelPort|desc")); AddCommandHelp( "Rehash", "", t_s("Reload global settings, modules, and listeners from znc.conf", "helpcmd|Rehash|desc")); AddCommandHelp("SaveConfig", "", t_s("Save the current settings to disk", "helpcmd|SaveConfig|desc")); AddCommandHelp("ListUsers", "", t_s("List all ZNC users and their connection status", "helpcmd|ListUsers|desc")); AddCommandHelp("ListAllUserNetworks", "", t_s("List all ZNC users and their networks", "helpcmd|ListAllUserNetworks|desc")); AddCommandHelp("ListChans", t_s("[user ]", "helpcmd|ListChans|args"), t_s("List all channels", "helpcmd|ListChans|desc")); AddCommandHelp( "ListClients", t_s("[user]", "helpcmd|ListClients|args"), t_s("List all connected clients", "helpcmd|ListClients|desc")); AddCommandHelp("Traffic", "", t_s("Show basic traffic stats for all ZNC users", "helpcmd|Traffic|desc")); AddCommandHelp("Broadcast", t_s("[message]", "helpcmd|Broadcast|args"), t_s("Broadcast a message to all ZNC users", "helpcmd|Broadcast|desc")); AddCommandHelp( "Shutdown", t_s("[message]", "helpcmd|Shutdown|args"), t_s("Shut down ZNC completely", "helpcmd|Shutdown|desc")); AddCommandHelp("Restart", t_s("[message]", "helpcmd|Restart|args"), t_s("Restart ZNC", "helpcmd|Restart|desc")); } if (Table.empty()) { PutStatus(t_f("No matches for '{1}'")(sFilter)); } else { PutStatus(Table); } }