diff --git a/User.cpp b/User.cpp index 0b6f8783..846f76e8 100644 --- a/User.cpp +++ b/User.cpp @@ -24,6 +24,7 @@ CUser::CUser(const CString& sUserName) { m_bUseClientIP = false; m_bKeepNick = false; m_bDenyLoadMod = false; + m_bAdmin= false; m_sStatusPrefix = "*"; m_uBufferCount = 50; m_bKeepBuffer = false; @@ -218,6 +219,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet) { SetBounceDCCs(User.BounceDCCs()); SetUseClientIP(User.UseClientIP()); SetDenyLoadMod(User.DenyLoadMod()); + SetAdmin(User.IsAdmin()); // !Flags return true; @@ -353,6 +355,7 @@ bool CUser::WriteConfig(CFile& File) { PrintLine(File, "BounceDCCs", CString((BounceDCCs()) ? "true" : "false")); PrintLine(File, "AutoCycle", CString((AutoCycle()) ? "true" : "false")); PrintLine(File, "DenyLoadMod", CString((DenyLoadMod()) ? "true" : "false")); + PrintLine(File, "Admin", CString((IsAdmin()) ? "true" : "false")); PrintLine(File, "DCCLookupMethod", CString((UseClientIP()) ? "client" : "default")); File.Write("\r\n"); @@ -618,6 +621,17 @@ bool CUser::PutStatus(const CString& sLine) { return true; } +bool CUser::PutStatusNotice(const CString& sLine) { + CUserSock* pUserSock = GetUserSock(); + + if (!pUserSock) { + return false; + } + + pUserSock->PutStatusNotice(sLine); + return true; +} + bool CUser::PutModule(const CString& sModule, const CString& sLine) { CUserSock* pUserSock = GetUserSock(); @@ -726,6 +740,7 @@ void CUser::SetBounceDCCs(bool b) { m_bBounceDCCs = b; } void CUser::SetUseClientIP(bool b) { m_bUseClientIP = b; } void CUser::SetKeepNick(bool b) { m_bKeepNick = b; } void CUser::SetDenyLoadMod(bool b) { m_bDenyLoadMod = b; } +void CUser::SetAdmin(bool b) { m_bAdmin = b; } void CUser::SetDefaultChanModes(const CString& s) { m_sDefaultChanModes = s; } void CUser::SetIRCNick(const CNick& n) { m_IRCNick = n; } void CUser::SetIRCServer(const CString& s) { m_sIRCServer = s; } @@ -780,6 +795,7 @@ bool CUser::ConnectPaused() { bool CUser::UseClientIP() const { return m_bUseClientIP; } bool CUser::GetKeepNick() const { return m_bKeepNick; } bool CUser::DenyLoadMod() const { return m_bDenyLoadMod; } +bool CUser::IsAdmin() const { return m_bAdmin; } bool CUser::BounceDCCs() const { return m_bBounceDCCs; } const CString& CUser::GetStatusPrefix() const { return m_sStatusPrefix; } const CString& CUser::GetDefaultChanModes() const { return m_sDefaultChanModes; } diff --git a/User.h b/User.h index ebd1260c..cec7ec4b 100644 --- a/User.h +++ b/User.h @@ -61,6 +61,7 @@ public: bool PutIRC(const CString& sLine); bool PutUser(const CString& sLine); bool PutStatus(const CString& sLine); + bool PutStatusNotice(const CString& sLine); bool PutModule(const CString& sModule, const CString& sLine); CString GetLocalIP(); @@ -84,6 +85,7 @@ public: void SetUseClientIP(bool b); void SetKeepNick(bool b); void SetDenyLoadMod(bool b); + void SetAdmin(bool b); bool SetStatusPrefix(const CString& s); void SetDefaultChanModes(const CString& s); void SetIRCNick(const CNick& n); @@ -115,6 +117,7 @@ public: bool UseClientIP() const; bool GetKeepNick() const; bool DenyLoadMod() const; + bool IsAdmin() const; bool BounceDCCs() const; const CString& GetStatusPrefix() const; const CString& GetDefaultChanModes() const; @@ -156,6 +159,7 @@ protected: bool m_bUseClientIP; bool m_bKeepNick; bool m_bDenyLoadMod; + bool m_bAdmin; bool m_bKeepBuffer; bool m_bAutoCycle; diff --git a/UserSock.cpp b/UserSock.cpp index 8b6db3c5..bd8a28ed 100644 --- a/UserSock.cpp +++ b/UserSock.cpp @@ -488,12 +488,11 @@ void CUserSock::UserCommand(const CString& sLine) { } } else if (sCommand.CaseCmp("VERSION") == 0) { PutStatus(CZNC::GetTag()); - } else if (sCommand.CaseCmp("SHUTDOWN") == 0) { - CString sQuitMsg = sLine.Token(1, true); - - if (!sQuitMsg.empty()) { - m_pUser->SetQuitMsg(sQuitMsg); - } + } else if (m_pUser->IsAdmin() && sCommand.CaseCmp("BROADCAST") == 0) { + CZNC::Get().Broadcast(sLine.Token(1, true)); + } else if (m_pUser->IsAdmin() && sCommand.CaseCmp("SHUTDOWN") == 0) { + CZNC::Get().Broadcast(sLine.Token(1, true)); + usleep(100000); // Sleep for 10ms to attempt to allow the previous Broadcast() to go through to all users throw CException(CException::EX_Shutdown); } else if (sCommand.CaseCmp("JUMP") == 0) { @@ -895,7 +894,7 @@ void CUserSock::UserCommand(const CString& sLine) { PutStatus("BufferCount for [" + sChan + "] set to [" + CString::ToString(pChan->GetBufferCount()) + "]"); } else { - PutStatus("I'm too stupid to help you with [" + sCommand + "]"); + PutStatus("Unknown command [" + sCommand + "] try 'Help'"); } } @@ -915,13 +914,22 @@ void CUserSock::HelpUser() { Table.AddRow(); Table.SetCell("Command", "RemServer"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Remove a server from the list"); Table.AddRow(); Table.SetCell("Command", "Topics"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Show topics in all channels"); Table.AddRow(); Table.SetCell("Command", "SetBuffer"); Table.SetCell("Arguments", "<#chan> [linecount]"); Table.SetCell("Description", "Set the buffer count for a channel"); - Table.AddRow(); Table.SetCell("Command", "Shutdown"); Table.SetCell("Arguments", "[message]"); Table.SetCell("Description", "Shutdown znc completely"); Table.AddRow(); Table.SetCell("Command", "Jump"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Jump to the next server in the list"); Table.AddRow(); Table.SetCell("Command", "Send"); Table.SetCell("Arguments", " "); Table.SetCell("Description", "Send a shell file to a nick on IRC"); Table.AddRow(); Table.SetCell("Command", "Get"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Send a shell file to yourself"); - Table.AddRow(); Table.SetCell("Command", "LoadMod"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Load a module"); - Table.AddRow(); Table.SetCell("Command", "UnloadMod"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Unload a module"); - Table.AddRow(); Table.SetCell("Command", "ReloadMod"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Reload a module"); + + if (m_pUser) { + if (!m_pUser->DenyLoadMod()) { + Table.AddRow(); Table.SetCell("Command", "LoadMod"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Load a module"); + Table.AddRow(); Table.SetCell("Command", "UnloadMod"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Unload a module"); + Table.AddRow(); Table.SetCell("Command", "ReloadMod"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Reload a module"); + } + + if (m_pUser->IsAdmin()) { + Table.AddRow(); Table.SetCell("Command", "Broadcast"); Table.SetCell("Arguments", "[message]"); Table.SetCell("Description", "Broadcast a message to all users"); + Table.AddRow(); Table.SetCell("Command", "Shutdown"); Table.SetCell("Arguments", "[message]"); Table.SetCell("Description", "Shutdown znc completely"); + } + } if (Table.size()) { unsigned int uTableIdx = 0; diff --git a/znc.cpp b/znc.cpp index 0666472c..63a2c736 100644 --- a/znc.cpp +++ b/znc.cpp @@ -445,6 +445,8 @@ bool CZNC::WriteNewConfig(const CString& sConfig) { CUtils::PrintMessage("Now we need to setup a user..."); CUtils::PrintMessage(""); + bool bFirstUser = true; + do { vsLines.push_back(""); CString sNick; @@ -454,6 +456,13 @@ bool CZNC::WriteNewConfig(const CString& sConfig) { vsLines.push_back(""); sAnswer = CUtils::GetHashPass(); vsLines.push_back("\tPass = " + sAnswer + " -"); + + if (CUtils::GetBoolInput("Would you like this user to be an admin?", bFirstUser)) { + vsLines.push_back("\tAdmin = true"); + } else { + vsLines.push_back("\tAdmin = false"); + } + CUtils::GetInput("Nick", sNick, sUser); vsLines.push_back("\tNick = " + sNick); CUtils::GetInput("Alt Nick", sAnswer, sNick + "_"); if (!sAnswer.empty()) { vsLines.push_back("\tAltNick = " + sAnswer); } CUtils::GetInput("Ident", sAnswer, sNick); vsLines.push_back("\tIdent = " + sAnswer); @@ -564,6 +573,7 @@ bool CZNC::WriteNewConfig(const CString& sConfig) { vsLines.push_back(""); CUtils::PrintMessage(""); + bFirstUser = false; } while (CUtils::GetBoolInput("Would you like to setup another user?", false)); // !User @@ -842,6 +852,9 @@ bool CZNC::ParseConfig(const CString& sConfig) { } else if (sName.CaseCmp("DenyLoadMod") == 0) { pUser->SetDenyLoadMod((sValue.CaseCmp("TRUE") == 0)); continue; + } else if (sName.CaseCmp("Admin") == 0) { + pUser->SetAdmin((sValue.CaseCmp("TRUE") == 0)); + continue; } else if (sName.CaseCmp("StatusPrefix") == 0) { if (!pUser->SetStatusPrefix(sValue)) { CUtils::PrintError("Invalid StatusPrefix [" + sValue + "] Must be 1-5 chars, no spaces."); @@ -1045,6 +1058,17 @@ bool CZNC::RemVHost(const CString& sHost) { return true; } +void CZNC::Broadcast(const CString& sMessage, CUser* pUser) { + for (map::iterator a = m_msUsers.begin(); a != m_msUsers.end(); a++) { + if (a->second != pUser) { + CUser* m_pUser = a->second; // This is a semi-hack because MODULECALLCONT below expects the user to be stored in m_pUser + CString sMsg = sMessage; + MODULECALLCONT(OnBroadcast(sMsg)); + a->second->PutStatusNotice("*** " + sMsg); + } + } +} + CString CZNC::FindModPath(const CString& sModule) const { CString sModPath = GetCurPath() + "/modules/" + sModule; sModPath += (sModule.find(".") == CString::npos) ? ".so" : ""; diff --git a/znc.h b/znc.h index 8ff960dc..4e7f966f 100644 --- a/znc.h +++ b/znc.h @@ -38,6 +38,7 @@ public: void ClearVHosts(); bool AddVHost(const CString& sHost); bool RemVHost(const CString& sHost); + void Broadcast(const CString& sMessage, CUser* pUser = NULL); // Setters void SetStatusPrefix(const CString& s) { m_sStatusPrefix = (s.empty()) ? "*" : s; }