From f601db2cd89818a18a169e2d6aedf4801b8490fc Mon Sep 17 00:00:00 2001 From: psychon Date: Wed, 16 May 2007 22:13:17 +0000 Subject: [PATCH] merge with rev 932 psychon branch: - module call for /me's - timestamps for playback and query buffer - WALLOP stuff / fix - properly join channels _after_ namesx or uhnames were set up - dont screw up raws on reconnect when you ran /lusers - change default quit msg and version reply to CZNC::GetTag(false) - change CZNC::GetTag() to point to sf.net - kind of an rewrite for partyline, added fixed channels (this doesnt work on irssi, did it ever work?) - add the timestamp support to webadmin too - add ConnectDelay config option to avoid being killed because we connected too fast - make znc handle usermodes correctly git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@799 726aef4b-f618-498e-8847-2d620e286838 --- Buffer.cpp | 12 + Buffer.h | 10 + Client.cpp | 11 +- IRCSock.cpp | 77 +++- IRCSock.h | 2 + Modules.cpp | 11 + Modules.h | 8 + User.cpp | 48 ++- User.h | 12 + modules/partyline.cpp | 375 ++++++++++++++---- modules/webadmin.cpp | 26 +- .../webadmin/skins/default.de/UserPage.tmpl | 5 +- modules/webadmin/skins/default/UserPage.tmpl | 4 + .../webadmin/skins/graphiX.de/UserPage.tmpl | 10 + modules/webadmin/skins/graphiX/UserPage.tmpl | 10 + modules/webadmin/skins/ice.de/UserPage.tmpl | 4 + modules/webadmin/skins/ice/UserPage.tmpl | 4 + znc.cpp | 34 +- znc.h | 1 + 19 files changed, 541 insertions(+), 123 deletions(-) diff --git a/Buffer.cpp b/Buffer.cpp index 95c24757..09ed3d91 100644 --- a/Buffer.cpp +++ b/Buffer.cpp @@ -36,6 +36,18 @@ int CBuffer::AddLine(const CString& sPre, const CString& sPost, bool bIncNick) { return size(); } +int CBuffer::UpdateLine(const CString& sPre, const CString& sPost, bool bIncNick) { + for(iterator it = begin(); it != end(); it++) { + if(it->GetPre() == sPre) { + it->SetPost(sPost); + it->SetIncNick(bIncNick); + return size(); + } + } + + return AddLine(sPre, sPost, bIncNick); +} + bool CBuffer::GetLine(const CString& sTarget, CString& sRet, unsigned int uIdx) { if (uIdx >= size()) { return false; diff --git a/Buffer.h b/Buffer.h index 5fd31566..53881e39 100644 --- a/Buffer.h +++ b/Buffer.h @@ -13,6 +13,14 @@ public: virtual ~CBufLine(); void GetLine(const CString& sTarget, CString& sRet); + const CString& GetPre() const { return m_sPre; } + const CString& GetPost() const { return m_sPost; } + bool GetIncNick() const { return m_bIncNick; } + + void SetPre(const CString& s) { m_sPre = s; } + void SetPost(const CString& s) { m_sPost = s; } + void SetIncNick(bool b) { m_bIncNick = b; } + private: protected: CString m_sPre; @@ -26,6 +34,8 @@ public: virtual ~CBuffer(); int AddLine(const CString& sPre, const CString& sPost, bool bIncNick = true); + /// Same as AddLine, but if there is already a line with sPre it is replaced. + int UpdateLine(const CString& sPre, const CString& sPost, bool bIncNick = true); bool GetNextLine(const CString& sTarget, CString& sRet); bool GetLine(const CString& sTarget, CString& sRet, unsigned int uIdx); bool IsEmpty() { return empty(); } diff --git a/Client.cpp b/Client.cpp index 3d77eaab..b495d269 100644 --- a/Client.cpp +++ b/Client.cpp @@ -282,7 +282,7 @@ void CClient::ReadLine(const CString& sData) { CChan* pChan = m_pUser->FindChan(sTarget); if ((pChan) && (pChan->KeepBuffer())) { - pChan->AddBuffer(":" + GetNickMask() + " NOTICE " + sTarget + " :" + sMsg); + pChan->AddBuffer(":" + GetNickMask() + " NOTICE " + sTarget + " :" + m_pUser->AddTimestamp(sMsg)); } // Relay to the rest of the clients that may be connected to this user @@ -426,8 +426,11 @@ void CClient::ReadLine(const CString& sData) { CChan* pChan = m_pUser->FindChan(sTarget); if (sCTCP.Token(0).CaseCmp("ACTION") == 0) { + CString sMessage = sCTCP.Token(1, true); + MODULECALL(OnUserAction(sTarget, sMessage), m_pUser, this, return); + if (pChan && pChan->KeepBuffer()) { - pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :\001" + sCTCP + "\001"); + pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + m_pUser->AddTimestamp(sMessage) + "\001"); } // Relay to the rest of the clients that may be connected to this user @@ -442,10 +445,8 @@ void CClient::ReadLine(const CString& sData) { } } } -#ifdef _MODULES } else { MODULECALL(OnUserCTCP(sTarget, sCTCP), m_pUser, this, return); -#endif } PutIRC("PRIVMSG " + sTarget + " :\001" + sCTCP + "\001"); @@ -496,7 +497,7 @@ void CClient::ReadLine(const CString& sData) { CChan* pChan = m_pUser->FindChan(sTarget); if ((pChan) && (pChan->KeepBuffer())) { - pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :" + sMsg); + pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :" + m_pUser->AddTimestamp(sMsg)); } PutIRC("PRIVMSG " + sTarget + " :" + sMsg); diff --git a/IRCSock.cpp b/IRCSock.cpp index 7018e02f..1b93ef9b 100644 --- a/IRCSock.cpp +++ b/IRCSock.cpp @@ -114,13 +114,6 @@ void CIRCSock::ReadLine(const CString& sData) { m_pUser->ClearRawBuffer(); m_pUser->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); - // Now that we are connected, we need to join our chans - const vector& vChans = m_pUser->GetChans(); - - for (unsigned int a = 0; a < vChans.size(); a++) { - PutIRC("JOIN " + vChans[a]->GetName() + " " + vChans[a]->GetKey()); - } - CZNC::Get().ReleaseISpoof(); m_bISpoofReleased = true; @@ -128,6 +121,8 @@ void CIRCSock::ReadLine(const CString& sData) { } case 5: ParseISupport(sRest); + m_pUser->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); + break; case 2: case 3: case 4: @@ -138,10 +133,10 @@ void CIRCSock::ReadLine(const CString& sData) { case 255: // client count case 265: // local users case 266: // global users - m_pUser->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); + m_pUser->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 422: // MOTD File is missing - case 375: // begin motd + case 375: // begin motd m_pUser->ClearMotdBuffer(); case 372: // motd case 376: // end motd @@ -508,16 +503,42 @@ void CIRCSock::ReadLine(const CString& sData) { return; } } else if (sCmd.CaseCmp("MODE") == 0) { - CString sChan = sRest.Token(0); + CString sTarget = sRest.Token(0); CString sModes = sRest.Token(1, true); + if(sModes.Left(1) == ":") + sModes = sModes.substr(1); - CChan* pChan = m_pUser->FindChan(sChan); + CChan* pChan = m_pUser->FindChan(sTarget); if (pChan) { pChan->ModeChange(sModes, Nick.GetNick()); if (pChan->IsDetached()) { return; } + } else if (sTarget == m_Nick.GetNick()) { + CString sModeArg = sModes.Token(0); +// CString sArgs = sModes.Token(1, true); Usermode changes got no params + bool bAdd = true; +/* no module call defined (yet?) +#ifdef _MODULES + MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs), m_pUser, NULL, ); +#endif +*/ + 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.CaseCmp("KICK") == 0) { // :opnick!ident@host.com KICK #chan nick :msg @@ -637,7 +658,12 @@ void CIRCSock::ReadLine(const CString& sData) { } else if (sCmd.CaseCmp("WALLOPS") == 0) { // :blub!dummy@rox-8DBEFE92 WALLOPS :this is a test CString sMsg = sRest.Token(0, true); - m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " WALLOPS ", sMsg, false); + + if(sMsg.Left(1) == ":") { + sMsg.LeftChomp(); + } + + m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " WALLOPS ", ":" + m_pUser->AddTimestamp(sMsg), false); } } } @@ -661,7 +687,12 @@ bool CIRCSock::OnCTCPReply(CNick& Nick, CString& sMessage) { } bool CIRCSock::OnPrivCTCP(CNick& Nick, CString& sMessage) { - MODULECALL(OnPrivCTCP(Nick, sMessage), m_pUser, NULL, return true); + if (sMessage.Token(0).CaseCmp("ACTION") == 0) { + sMessage = sMessage.Token(1, true); + MODULECALL(OnPrivAction(Nick, sMessage), m_pUser, NULL, return true); + } else { + MODULECALL(OnPrivCTCP(Nick, sMessage), m_pUser, NULL, return true); + } if (strncasecmp(sMessage.c_str(), "DCC ", 4) == 0 && m_pUser && m_pUser->BounceDCCs() && m_pUser->IsUserAttached()) { // DCC CHAT chat 2453612361 44592 @@ -722,7 +753,7 @@ bool CIRCSock::OnPrivCTCP(CNick& Nick, CString& sMessage) { if (sReply.empty() && !m_pUser->IsUserAttached()) { if (sQuery == "VERSION") { - sReply = "ZNC by prozac - http://znc.sourceforge.net"; + sReply = CZNC::GetTag(); } else if (sQuery == "PING") { sReply = sMessage.Token(1, true); } @@ -742,7 +773,7 @@ bool CIRCSock::OnPrivNotice(CNick& Nick, CString& sMessage) { if (!m_pUser->IsUserAttached()) { // If the user is detached, add to the buffer - m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " NOTICE ", " :" + sMessage); + m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " NOTICE ", " :" + m_pUser->AddTimestamp(sMessage)); } return false; @@ -753,7 +784,7 @@ bool CIRCSock::OnPrivMsg(CNick& Nick, CString& sMessage) { if (!m_pUser->IsUserAttached()) { // If the user is detached, add to the buffer - m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " PRIVMSG ", " :" + sMessage); + m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " PRIVMSG ", " :" + m_pUser->AddTimestamp(sMessage)); } return false; @@ -763,8 +794,11 @@ bool CIRCSock::OnChanCTCP(CNick& Nick, const CString& sChan, CString& sMessage) CChan* pChan = m_pUser->FindChan(sChan); if (pChan) { // Record a /me - if (sMessage.Token(0).CaseCmp("ACTION") == 0 && (pChan->KeepBuffer() || !m_pUser->IsUserAttached())) { - pChan->AddBuffer(":" + Nick.GetNickMask() + " PRIVMSG " + sChan + " :\001" + sMessage + "\001"); + if (sMessage.Token(0).CaseCmp("ACTION") == 0) { + if(pChan->KeepBuffer() || !m_pUser->IsUserAttached()) { + pChan->AddBuffer(":" + Nick.GetNickMask() + " PRIVMSG " + sChan + " :\001ACTION " + m_pUser->AddTimestamp(sMessage.Token(1, true)) + "\001"); + } + MODULECALL(OnChanAction(Nick, *pChan, sMessage), m_pUser, NULL, return true); } else { MODULECALL(OnChanCTCP(Nick, *pChan, sMessage), m_pUser, NULL, return true); } @@ -779,7 +813,7 @@ bool CIRCSock::OnChanNotice(CNick& Nick, const CString& sChan, CString& sMessage MODULECALL(OnChanNotice(Nick, *pChan, sMessage), m_pUser, NULL, return true); if ((pChan->KeepBuffer()) || (!m_pUser->IsUserAttached())) { - pChan->AddBuffer(":" + Nick.GetNickMask() + " NOTICE " + sChan + " :" + sMessage); + pChan->AddBuffer(":" + Nick.GetNickMask() + " NOTICE " + sChan + " :" + m_pUser->AddTimestamp(sMessage)); } } @@ -792,7 +826,7 @@ bool CIRCSock::OnChanMsg(CNick& Nick, const CString& sChan, CString& sMessage) { MODULECALL(OnChanMsg(Nick, *pChan, sMessage), m_pUser, NULL, return true); if (pChan->KeepBuffer() || !m_pUser->IsUserAttached()) { - pChan->AddBuffer(":" + Nick.GetNickMask() + " PRIVMSG " + sChan + " :" + sMessage); + pChan->AddBuffer(":" + Nick.GetNickMask() + " PRIVMSG " + sChan + " :" + m_pUser->AddTimestamp(sMessage)); } } @@ -832,6 +866,7 @@ void CIRCSock::Disconnected() { m_pUser->ClearMotdBuffer(); ResetChans(); + m_scUserModes.clear(); } void CIRCSock::SockError(int iErrno) { @@ -843,6 +878,7 @@ void CIRCSock::SockError(int iErrno) { m_pUser->ClearMotdBuffer(); ResetChans(); + m_scUserModes.clear(); } void CIRCSock::Timeout() { @@ -854,6 +890,7 @@ void CIRCSock::Timeout() { m_pUser->ClearMotdBuffer(); ResetChans(); + m_scUserModes.empty(); } void CIRCSock::ConnectionRefused() { diff --git a/IRCSock.h b/IRCSock.h index 3dfbe7e4..c0dc6893 100644 --- a/IRCSock.h +++ b/IRCSock.h @@ -68,6 +68,7 @@ public: bool IsOrigNickPending() const { return m_bOrigNickPending; } bool HasNamesx() const { return m_bNamesx; } bool HasUHNames() const { return m_bUHNames; } + const set& GetUserModes() const { return m_scUserModes; } // !Getters private: void SetNick(const CString& sNick); @@ -80,6 +81,7 @@ protected: bool m_bUHNames; CString m_sPerms; CString m_sPermModes; + set m_scUserModes; map m_mueChanModes; CUser* m_pUser; CNick m_Nick; diff --git a/Modules.cpp b/Modules.cpp index ad57abb1..16d42a33 100644 --- a/Modules.cpp +++ b/Modules.cpp @@ -502,6 +502,7 @@ void CModule::OnUserDetached() {} CModule::EModRet CModule::OnUserRaw(CString& sLine) { return CONTINUE; } CModule::EModRet CModule::OnUserCTCPReply(CString& sTarget, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnUserCTCP(CString& sTarget, CString& sMessage) { return CONTINUE; } +CModule::EModRet CModule::OnUserAction(CString& sTarget, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnUserMsg(CString& sTarget, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnUserNotice(CString& sTarget, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnUserJoin(CString& sChannel, CString& sKey) { return CONTINUE; } @@ -510,6 +511,8 @@ CModule::EModRet CModule::OnUserPart(CString& sChannel, CString& sMessage) { ret CModule::EModRet CModule::OnCTCPReply(CNick& Nick, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnPrivCTCP(CNick& Nick, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; } +CModule::EModRet CModule::OnPrivAction(CNick& Nick, CString& sMessage) { return CONTINUE; } +CModule::EModRet CModule::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnPrivMsg(CNick& Nick, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnPrivNotice(CNick& Nick, CString& sMessage) { return CONTINUE; } @@ -537,6 +540,7 @@ bool CModule::PutModNotice(const CString& sLine, const CString& sIdent, const CS // CGlobalModule // /////////////////// CModule::EModRet CGlobalModule::OnConfigLine(const CString& sName, const CString& sValue, CUser* pUser, CChan* pChan) { return CONTINUE; } +void CGlobalModule::OnFinishedConfig() {} CModule::EModRet CGlobalModule::OnDeleteUser(CUser& User) { return CONTINUE; } CModule::EModRet CGlobalModule::OnLoginAttempt(CSmartPtr Auth) { return CONTINUE; } void CGlobalModule::OnFailedLogin(const CString& sUsername, const CString& sRemoteIP) {} @@ -591,6 +595,7 @@ bool CModules::OnUserDetached() { MODUNLOADCHK(OnUserDetached()); return false; bool CModules::OnUserRaw(CString& sLine) { MODHALTCHK(OnUserRaw(sLine)); } bool CModules::OnUserCTCPReply(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserCTCPReply(sTarget, sMessage)); } bool CModules::OnUserCTCP(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserCTCP(sTarget, sMessage)); } +bool CModules::OnUserAction(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserAction(sTarget, sMessage)); } bool CModules::OnUserMsg(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserMsg(sTarget, sMessage)); } bool CModules::OnUserNotice(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserNotice(sTarget, sMessage)); } bool CModules::OnUserJoin(CString& sChannel, CString& sKey) { MODHALTCHK(OnUserJoin(sChannel, sKey)); } @@ -604,6 +609,8 @@ bool CModules::OnPart(const CNick& Nick, CChan& Channel) { MODUNLOADCHK(OnPart(N bool CModules::OnCTCPReply(CNick& Nick, CString& sMessage) { MODHALTCHK(OnCTCPReply(Nick, sMessage)); } bool CModules::OnPrivCTCP(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivCTCP(Nick, sMessage)); } bool CModules::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanCTCP(Nick, Channel, sMessage)); } +bool CModules::OnPrivAction(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivAction(Nick, sMessage)); } +bool CModules::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanAction(Nick, Channel, sMessage)); } bool CModules::OnPrivMsg(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivMsg(Nick, sMessage)); } bool CModules::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanMsg(Nick, Channel, sMessage)); } bool CModules::OnPrivNotice(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivNotice(Nick, sMessage)); } @@ -620,6 +627,10 @@ bool CGlobalModules::OnConfigLine(const CString& sName, const CString& sValue, C GLOBALMODHALTCHK(OnConfigLine(sName, sValue, pUser, pChan)); } +void CGlobalModules::OnFinishedConfig() { + GLOBALMODCALL(OnFinishedConfig()); +} + bool CGlobalModules::OnDeleteUser(CUser& User) { GLOBALMODHALTCHK(OnDeleteUser(User)); } diff --git a/Modules.h b/Modules.h index 29c48aab..c36f581e 100644 --- a/Modules.h +++ b/Modules.h @@ -233,6 +233,7 @@ public: virtual EModRet OnUserRaw(CString& sLine); virtual EModRet OnUserCTCPReply(CString& sTarget, CString& sMessage); virtual EModRet OnUserCTCP(CString& sTarget, CString& sMessage); + virtual EModRet OnUserAction(CString& sTarget, CString& sMessage); virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage); virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage); virtual EModRet OnUserJoin(CString& sChannel, CString& sKey); @@ -241,6 +242,8 @@ public: virtual EModRet OnCTCPReply(CNick& Nick, CString& sMessage); virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage); virtual EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage); + virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage); + virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage); virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage); virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage); virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage); @@ -361,6 +364,7 @@ public: virtual bool OnUserRaw(CString& sLine); virtual bool OnUserCTCPReply(CString& sTarget, CString& sMessage); virtual bool OnUserCTCP(CString& sTarget, CString& sMessage); + virtual bool OnUserAction(CString& sTarget, CString& sMessage); virtual bool OnUserMsg(CString& sTarget, CString& sMessage); virtual bool OnUserNotice(CString& sTarget, CString& sMessage); virtual bool OnUserJoin(CString& sChannel, CString& sKey); @@ -369,6 +373,8 @@ public: virtual bool OnCTCPReply(CNick& Nick, CString& sMessage); virtual bool OnPrivCTCP(CNick& Nick, CString& sMessage); virtual bool OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage); + virtual bool OnPrivAction(CNick& Nick, CString& sMessage); + virtual bool OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage); virtual bool OnPrivMsg(CNick& Nick, CString& sMessage); virtual bool OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage); virtual bool OnPrivNotice(CNick& Nick, CString& sMessage); @@ -394,6 +400,7 @@ public: virtual ~CGlobalModule() {} virtual EModRet OnConfigLine(const CString& sName, const CString& sValue, CUser* pUser, CChan* pChan); + virtual void OnFinishedConfig(); virtual EModRet OnDeleteUser(CUser& User); virtual EModRet OnLoginAttempt(CSmartPtr Auth); virtual void OnFailedLogin(const CString& sUsername, const CString& sRemoteIP); @@ -406,6 +413,7 @@ public: virtual ~CGlobalModules() {} virtual bool OnConfigLine(const CString& sName, const CString& sValue, CUser* pUser, CChan* pChan); + virtual void OnFinishedConfig(); virtual bool OnDeleteUser(CUser& User); virtual bool OnLoginAttempt(CSmartPtr Auth); virtual void OnFailedLogin(const CString& sUsername, const CString& sRemoteIP); diff --git a/User.cpp b/User.cpp index ca16ef19..dc784b23 100644 --- a/User.cpp +++ b/User.cpp @@ -37,6 +37,8 @@ CUser::CUser(const CString& sUserName) { m_bKeepBuffer = false; m_bAutoCycle = true; m_bBeingDeleted = false; + m_sTimestampFormat = "[%H:%M:%S]"; + m_bAppendTimestamp = false; m_pKeepNickTimer = new CKeepNickTimer(this); m_pJoinTimer = new CJoinTimer(this); m_pMiscTimer = new CMiscTimer(this); @@ -125,6 +127,31 @@ CString& CUser::ExpandString(const CString& sStr, CString& sRet) const { return sRet; } +CString CUser::AddTimestamp(const CString& sStr) const { + CString sRet; + return AddTimestamp(sStr, sRet); +} + +CString& CUser::AddTimestamp(const CString& sStr, CString& sRet) const { + char szTimestamp[1024]; + time_t tm; + + if(GetTimestampFormat().empty()) { + sRet = sStr; + } else { + time(&tm); + strftime(szTimestamp, sizeof(szTimestamp) / sizeof(char), GetTimestampFormat().c_str(), localtime(&tm)); + if(m_bAppendTimestamp) { + sRet = sStr + " "; + sRet += szTimestamp; + } else { + sRet = szTimestamp; + sRet += " " + sStr; + } + } + return sRet; +} + void CUser::BounceAllClients() { for (unsigned int a = 0; a < m_vClients.size(); a++) { m_vClients[a]->BouncedOff(); @@ -164,6 +191,18 @@ void CUser::UserConnected(CClient* pClient) { } } + if(GetIRCSock() != NULL) { + CString sUserMode(""); + const set& scUserModes = GetIRCSock()->GetUserModes(); + for (set::iterator it = scUserModes.begin(); + it != scUserModes.end(); it++) { + sUserMode += *it; + } + if(!sUserMode.empty()) { + pClient->PutClient(":" + GetIRCNick().GetNick() + " MODE " + GetIRCNick().GetNick() + " :+" + sUserMode); + } + } + const vector& vChans = GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { if ((vChans[a]->IsOn()) && (!vChans[a]->IsDetached())) { @@ -347,6 +386,8 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet) { SetUseClientIP(User.UseClientIP()); SetDenyLoadMod(User.DenyLoadMod()); SetAdmin(User.IsAdmin()); + SetTimestampAppend(User.GetTimestampAppend()); + SetTimestampFormat(User.GetTimestampFormat()); // !Flags return true; @@ -376,6 +417,9 @@ bool CUser::IsHostAllowed(const CString& sHostMask) { return false; } +const CString& CUser::GetTimestampFormat() const { return m_sTimestampFormat; } +bool CUser::GetTimestampAppend() const { return m_bAppendTimestamp; } + bool CUser::IsValidUserName(const CString& sUserName) { const char* p = sUserName.c_str(); @@ -485,6 +529,8 @@ bool CUser::WriteConfig(CFile& File) { PrintLine(File, "DenyLoadMod", CString((DenyLoadMod()) ? "true" : "false")); PrintLine(File, "Admin", CString((IsAdmin()) ? "true" : "false")); PrintLine(File, "DCCLookupMethod", CString((UseClientIP()) ? "client" : "default")); + PrintLine(File, "TimestampFormat", GetTimestampFormat()); + PrintLine(File, "AppendTimestamp", CString((GetTimestampAppend()) ? "yes" : "no")); File.Write("\r\n"); // Allow Hosts @@ -981,7 +1027,7 @@ const vector& CUser::GetChans() const { return m_vChans; } const vector& CUser::GetServers() const { return m_vServers; } const CNick& CUser::GetIRCNick() const { return m_IRCNick; } const CString& CUser::GetIRCServer() const { return m_sIRCServer; } -CString CUser::GetQuitMsg() const { return (!m_sQuitMsg.empty()) ? m_sQuitMsg : "ZNC by prozac - http://znc.sourceforge.net"; } +CString CUser::GetQuitMsg() const { return (!m_sQuitMsg.empty()) ? m_sQuitMsg : CZNC::GetTag(false); } const MCString& CUser::GetCTCPReplies() const { return m_mssCTCPReplies; } unsigned int CUser::GetBufferCount() const { return m_uBufferCount; } bool CUser::KeepBuffer() const { return m_bKeepBuffer; } diff --git a/User.h b/User.h index a18d5f57..85590829 100644 --- a/User.h +++ b/User.h @@ -69,6 +69,9 @@ public: void AddRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.AddLine(sPre, sPost, bIncNick); } void AddMotdBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_MotdBuffer.AddLine(sPre, sPost, bIncNick); } void AddQueryBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_QueryBuffer.AddLine(sPre, sPost, bIncNick); } + void UpdateRawBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_RawBuffer.UpdateLine(sPre, sPost, bIncNick); } + void UpdateMotdBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_MotdBuffer.UpdateLine(sPre, sPost, bIncNick); } + void UpdateQueryBuffer(const CString& sPre, const CString& sPost, bool bIncNick = true) { m_QueryBuffer.UpdateLine(sPre, sPost, bIncNick); } void ClearRawBuffer() { m_RawBuffer.Clear(); } void ClearMotdBuffer() { m_MotdBuffer.Clear(); } void ClearQueryBuffer() { m_QueryBuffer.Clear(); } @@ -93,6 +96,9 @@ public: CString ExpandString(const CString& sStr) const; CString& ExpandString(const CString& sStr, CString& sRet) const; + CString AddTimestamp(const CString& sStr) const; + CString& AddTimestamp(const CString& sStr, CString& sRet) const; + bool SendFile(const CString& sRemoteNick, const CString& sFileName, const CString& sModuleName = ""); bool GetFile(const CString& sRemoteNick, const CString& sRemoteIP, unsigned short uRemotePort, const CString& sFileName, unsigned long uFileSize, const CString& sModuleName = ""); bool ResumeFile(const CString& sRemoteNick, unsigned short uPort, unsigned long uFileSize); @@ -125,6 +131,8 @@ public: void SetAutoCycle(bool b); void SetChanPrefixes(const CString& s) { m_sChanPrefixes = (s.empty()) ? "#&" : s; } void SetBeingDeleted(bool b) { m_bBeingDeleted = b; } + void SetTimestampFormat(const CString& s) { m_sTimestampFormat = s; } + void SetTimestampAppend(bool b) { m_bAppendTimestamp = b; } // !Setters // Getters @@ -141,6 +149,8 @@ public: const CString& GetPass() const; bool IsPassHashed() const; const set& GetAllowedHosts() const; + const CString& GetTimestampFormat() const; + bool GetTimestampAppend() const; const CString& GetChanPrefixes() const { return m_sChanPrefixes; } bool IsChan(const CString& sChan) const { return (sChan.size() && GetChanPrefixes().find(sChan[0]) != CString::npos); } @@ -186,6 +196,7 @@ protected: CString m_sIRCServer; CString m_sQuitMsg; MCString m_mssCTCPReplies; + CString m_sTimestampFormat; // Paths CString m_sUserPath; @@ -206,6 +217,7 @@ protected: bool m_bKeepBuffer; bool m_bAutoCycle; bool m_bBeingDeleted; + bool m_bAppendTimestamp; CKeepNickTimer* m_pKeepNickTimer; CJoinTimer* m_pJoinTimer; diff --git a/modules/partyline.cpp b/modules/partyline.cpp index 9767ba86..0ffae279 100644 --- a/modules/partyline.cpp +++ b/modules/partyline.cpp @@ -9,11 +9,43 @@ #include "HTTPSock.h" #include "Server.h" +class CPartylineChannel { +public: + CPartylineChannel(const CString& sName) { m_sName = sName.AsLower(); } + ~CPartylineChannel() {} + + const CString& GetTopic() const { return m_sTopic; } + const CString& GetName() const { return m_sName; } + const set& GetNicks() const { return m_ssNicks; } + + void SetTopic(const CString& s) { m_sTopic = s; } + + void AddNick(const CString& s) { m_ssNicks.insert(s); } + void DelNick(const CString& s) { m_ssNicks.erase(s); } + + void AddFixedNick(const CString& s) { m_ssFixedNicks.insert(s); } + void DelFixedNick(const CString& s) { m_ssFixedNicks.erase(s); } + + bool IsInChannel(const CString& s) { return m_ssNicks.find(s) != m_ssNicks.end(); } + bool IsFixedChan(const CString& s) { return m_ssFixedNicks.find(s) != m_ssFixedNicks.end(); } + +protected: + CString m_sTopic; + CString m_sName; + set m_ssNicks; + set m_ssFixedNicks; +}; + class CPartylineMod : public CGlobalModule { public: GLOBALMODCONSTRUCTOR(CPartylineMod) {} - virtual ~CPartylineMod() {} + virtual ~CPartylineMod() { + while(m_ssChannels.size()) { + delete *m_ssChannels.begin(); + m_ssChannels.erase(m_ssChannels.begin()); + } + } virtual bool OnBoot() { return true; @@ -35,12 +67,43 @@ public: unsigned int a = 0; while (!(sChan = sArgs.Token(a++)).empty()) { if (sChan.Left(2) == "~#") { + sChan = sChan.Left(32); m_ssDefaultChans.insert(sChan); } } + Load(); + return true; } + + void OnFinishedConfig() { + Load(); + } + + void Load() { + VCString vsChannels; + for (MCString::iterator it = BeginNV(); it != EndNV(); it++) { + CUser* pUser = CZNC::Get().FindUser(it->first); + CPartylineChannel* pChannel; + it->second.Split(",", vsChannels, false); + + if(!pUser) { + // TODO: give some usefull message? + continue; + } + + for(VCString::iterator i = vsChannels.begin(); i != vsChannels.end(); i++) { + if(i->Trim_n().empty()) + continue; + pChannel = GetChannel(*i); + JoinUser(pUser, pChannel); + pChannel->AddFixedNick(it->first); + } + } + + return; + } virtual EModRet OnDeleteUser(CUser& User) { const CString& sNick = User.GetUserName(); @@ -49,9 +112,9 @@ public: CUser* pTmp = m_pUser; m_pUser = &User; - for (map >::iterator it = m_msChans.begin(); it != m_msChans.end(); it++) { // Loop through each chan - const CString& sChannel = it->first; - set& ssNicks = it->second; + for (set::iterator it = m_ssChannels.begin(); it != m_ssChannels.end(); it++) { // Loop through each chan + const CString& sChannel = (*it)->GetName(); + const set& ssNicks = (*it)->GetNicks(); if (ssNicks.find(User.GetUserName()) != ssNicks.end()) { // If the user is on this chan User.PutUser(":*" + GetModName() + "!znc@rottenboy.com KICK " + sChannel + " " + sNick + " :User Deleted"); @@ -89,34 +152,42 @@ public: // Make sure this user is in the default channels for (set::iterator a = m_ssDefaultChans.begin(); a != m_ssDefaultChans.end(); a++) { - m_msChans[*a].insert(m_pUser->GetUserName()); + CPartylineChannel* pChannel = GetChannel(*a); + const CString& sNick = m_pUser->GetUserName(); + CString sHost = m_pUser->GetVHost(); + const set& ssNicks = pChannel->GetNicks(); + + if (sHost.empty()) { + sHost = m_pUser->GetIRCNick().GetHost(); + } + PutChan(ssNicks, ":?" + sNick + "!" + m_pUser->GetIdent() + "@" + sHost + " JOIN " + *a, false); + pChannel->AddNick(sNick); } - for (map >::iterator it = m_msChans.begin(); it != m_msChans.end(); it++) { - set& ssNicks = it->second; + for (set::iterator it = m_ssChannels.begin(); it != m_ssChannels.end(); it++) { + const set& ssNicks = (*it)->GetNicks(); - if (ssNicks.find(m_pUser->GetUserName()) != ssNicks.end()) { - MCString::iterator itb = m_msTopics.find(it->first.AsLower()); + if ((*it)->IsInChannel(m_pUser->GetUserName())) { - m_pClient->PutClient(":" + m_pUser->GetIRCNick().GetNickMask() + " JOIN " + it->first); + m_pClient->PutClient(":" + m_pUser->GetIRCNick().GetNickMask() + " JOIN " + (*it)->GetName()); - if (itb != m_msTopics.end()) { - m_pClient->PutClient(":" + m_pUser->GetIRCServer() + " 332 " + m_pUser->GetIRCNick().GetNickMask() + " " + it->first + " :" + itb->second); + if (!(*it)->GetTopic().empty()) { + m_pClient->PutClient(":" + m_pUser->GetIRCServer() + " 332 " + m_pUser->GetIRCNick().GetNickMask() + " " + (*it)->GetName() + " :" + (*it)->GetTopic()); } - SendNickList(ssNicks, it->first); - PutChan(ssNicks, ":*" + GetModName() + "!znc@rottenboy.com MODE " + it->first + " +" + CString(m_pUser->IsAdmin() ? "o" : "v") + " ?" + m_pUser->GetUserName(), true); + SendNickList(m_pUser, ssNicks, (*it)->GetName()); + PutChan(ssNicks, ":*" + GetModName() + "!znc@rottenboy.com MODE " + (*it)->GetName() + " +" + CString(m_pUser->IsAdmin() ? "o" : "v") + " ?" + m_pUser->GetUserName(), true); } } } virtual void OnUserDetached() { if (!m_pUser->IsUserAttached() && !m_pUser->IsBeingDeleted()) { - for (map >::iterator it = m_msChans.begin(); it != m_msChans.end(); it++) { - set& ssNicks = it->second; + for (set::iterator it = m_ssChannels.begin(); it != m_ssChannels.end(); it++) { + const set& ssNicks = (*it)->GetNicks(); if (ssNicks.find(m_pUser->GetUserName()) != ssNicks.end()) { - PutChan(ssNicks, ":*" + GetModName() + "!znc@rottenboy.com MODE " + it->first + " -ov ?" + m_pUser->GetUserName() + " ?" + m_pUser->GetUserName(), true); + PutChan(ssNicks, ":*" + GetModName() + "!znc@rottenboy.com MODE " + (*it)->GetName() + " -ov ?" + m_pUser->GetUserName() + " ?" + m_pUser->GetUserName(), true); } } } @@ -135,19 +206,19 @@ public: sTopic.LeftChomp(); } - set& ssNicks = m_msChans[sChannel.AsLower()]; // @todo do a lookup first - const CString& sNick = m_pUser->GetUserName(); + CPartylineChannel* pChannel = FindChannel(sChannel); - if (ssNicks.find(sNick) != ssNicks.end()) { + if (pChannel && pChannel->IsInChannel(m_pUser->GetUserName())) { + const set& ssNicks = pChannel->GetNicks(); if (!sTopic.empty()) { if (m_pUser->IsAdmin()) { PutChan(ssNicks, ":" + m_pUser->GetIRCNick().GetNickMask() + " TOPIC " + sChannel + " :" + sTopic); - m_msTopics[sChannel.AsLower()] = sTopic; + pChannel->SetTopic(sTopic); } else { m_pUser->PutUser(":irc.znc.com 482 " + m_pUser->GetIRCNick().GetNick() + " " + sChannel + " :You're not channel operator"); } } else { - sTopic = m_msTopics[sChannel.AsLower()]; + sTopic = pChannel->GetTopic(); if (sTopic.empty()) { m_pUser->PutUser(":irc.znc.com 331 " + m_pUser->GetIRCNick().GetNick() + " " + sChannel + " :No topic is set."); @@ -174,29 +245,42 @@ public: return HALT; } - set& ssNicks = m_msChans[sChannel.AsLower()]; // @todo do a lookup first - const CString& sNick = m_pUser->GetUserName(); + CPartylineChannel* pChannel = FindChannel(sChannel); - if (ssNicks.find(sNick) != ssNicks.end()) { - ssNicks.erase(sNick); - CString sHost = m_pUser->GetVHost(); - - if (sHost.empty()) { - sHost = m_pUser->GetIRCNick().GetHost(); - } - - m_pUser->PutUser(":" + m_pUser->GetIRCNick().GetNickMask() + " PART " + sChannel); - PutChan(ssNicks, ":?" + sNick + "!" + m_pUser->GetIdent() + "@" + sHost + " PART " + sChannel, false); - - if (ssNicks.empty()) { - m_msChans.erase(sChannel.AsLower()); - m_msTopics.erase(sChannel.AsLower()); - } - } + PartUser(m_pUser, pChannel); return HALT; } + void PartUser(CUser* pUser, CPartylineChannel* pChannel, bool bForce = false) { + if (!pChannel || !pChannel->IsInChannel(pUser->GetUserName())) { + return; + } + + if (!pChannel->IsFixedChan(pUser->GetUserName()) || bForce) { + pChannel->DelNick(pUser->GetUserName()); + pChannel->DelFixedNick(pUser->GetUserName()); + + const set& ssNicks = pChannel->GetNicks(); + CString sHost = pUser->GetVHost(); + + if (sHost.empty()) { + sHost = pUser->GetIRCNick().GetHost(); + } + + pUser->PutUser(":" + pUser->GetIRCNick().GetNickMask() + " PART " + pChannel->GetName()); + PutChan(ssNicks, ":?" + pUser->GetUserName() + "!" + pUser->GetIdent() + "@" + sHost + " PART " + pChannel->GetName(), false); + + if (ssNicks.empty()) { + delete pChannel; + m_ssChannels.erase(pChannel); + } + } else { + // some clients dont wait for the server to send an answer to a part, so we need to make them join again + pUser->PutUser(":" + pUser->GetIRCNick().GetNickMask() + " JOIN " + pChannel->GetName()); + } + } + virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) { if (sChannel.Left(1) != "~") { return CONTINUE; @@ -208,37 +292,39 @@ public: } sChannel = sChannel.Left(32); - set& ssNicks = m_msChans[sChannel.AsLower()]; - const CString& sNick = m_pUser->GetUserName(); + CPartylineChannel* pChannel = GetChannel(sChannel); - if (ssNicks.find(sNick) == ssNicks.end()) { - ssNicks.insert(sNick); - - CString sHost = m_pUser->GetVHost(); - - if (sHost.empty()) { - sHost = m_pUser->GetIRCNick().GetHost(); - } - - m_pUser->PutUser(":" + m_pUser->GetIRCNick().GetNickMask() + " JOIN " + sChannel); - - MCString::iterator it = m_msTopics.find(sChannel.AsLower()); - - if (it != m_msTopics.end()) { - m_pUser->PutUser(":" + m_pUser->GetIRCServer() + " 332 " + m_pUser->GetIRCNick().GetNickMask() + " " + sChannel + " :" + it->second); - } - - PutChan(ssNicks, ":?" + sNick + "!" + m_pUser->GetIdent() + "@" + sHost + " JOIN " + sChannel, false); - SendNickList(ssNicks, sChannel); - - if (m_pUser->IsAdmin()) { - PutChan(ssNicks, ":*" + GetModName() + "!znc@rottenboy.com MODE " + sChannel + " +o ?" + sNick, false); - } - } + JoinUser(m_pUser, pChannel); return HALT; } + void JoinUser(CUser* pUser, CPartylineChannel* pChannel) { + if (pChannel && !pChannel->IsInChannel(pUser->GetUserName())) { + const set& ssNicks = pChannel->GetNicks(); + const CString& sNick = pUser->GetUserName(); + pChannel->AddNick(sNick); + + CString sHost = pUser->GetVHost(); + + if (sHost.empty()) { + sHost = pUser->GetIRCNick().GetHost(); + } + + pUser->PutUser(":" + pUser->GetIRCNick().GetNickMask() + " JOIN " + pChannel->GetName()); + PutChan(ssNicks, ":?" + sNick + "!" + pUser->GetIdent() + "@" + sHost + " JOIN " + pChannel->GetName(), false); + + if (!pChannel->GetTopic().empty()) { + pUser->PutUser(":" + pUser->GetIRCServer() + " 332 " + pUser->GetIRCNick().GetNickMask() + " " + pChannel->GetName() + " :" + pChannel->GetTopic()); + } + SendNickList(pUser, ssNicks, pChannel->GetName()); + + if (pUser->IsAdmin()) { + PutChan(ssNicks, ":*" + GetModName() + "!znc@rottenboy.com MODE " + pChannel->GetName() + " +o ?" + pUser->GetUserName(), (pUser == m_pUser) ? false : true, pUser); + } + } + } + virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) { if (sTarget.empty()) { return CONTINUE; @@ -257,7 +343,7 @@ public: } if (cPrefix == '~') { - if (m_msChans.find(sTarget.AsLower()) == m_msChans.end()) { + if (FindChannel(sTarget) == NULL) { m_pClient->PutClient(":" + m_pUser->GetIRCServer() + " 403 " + m_pUser->GetIRCNick().GetNick() + " " + sTarget + " :No such channel"); return HALT; } @@ -288,6 +374,10 @@ public: Table.AddRow(); Table.SetCell("Command", "Help"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "List all partyline commands"); Table.AddRow(); Table.SetCell("Command", "List"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "List all open channels"); + Table.AddRow(); Table.SetCell("Command", "AddFixChan"); Table.SetCell("Arguments", " "); Table.SetCell("Description", "Force a user into a channel which he cant part"); + Table.AddRow(); Table.SetCell("Command", "DelFixChan"); Table.SetCell("Arguments", " "); Table.SetCell("Description", "Remove a user from such a channel"); + Table.AddRow(); Table.SetCell("Command", "ListFixChans"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Show which channels a user can not part"); + Table.AddRow(); Table.SetCell("Command", "ListFixUsers"); Table.SetCell("Arguments", ""); Table.SetCell("Description", "Show which users can not part this channel"); unsigned int uTableIdx = 0; CString sLine; @@ -296,7 +386,7 @@ public: PutModule(sLine); } } else if (sCommand.CaseCmp("LIST") == 0) { - if (!m_msChans.size()) { + if (!m_ssChannels.size()) { PutModule("There are no open channels."); return; } @@ -306,11 +396,11 @@ public: Table.AddColumn("Channel"); Table.AddColumn("Users"); - for (map >::const_iterator a = m_msChans.begin(); a != m_msChans.end(); a++) { + for (set::const_iterator a = m_ssChannels.begin(); a != m_ssChannels.end(); a++) { Table.AddRow(); - Table.SetCell("Channel", a->first); - Table.SetCell("Users", CString(a->second.size())); + Table.SetCell("Channel", (*a)->GetName()); + Table.SetCell("Users", CString((*a)->GetNicks().size())); } unsigned int uTableIdx = 0; @@ -319,30 +409,124 @@ public: while (Table.GetLine(uTableIdx++, sLine)) { PutModule(sLine); } - } else { + } else if (sCommand.CaseCmp("ADDFIXCHAN") == 0) { + if(!m_pUser->IsAdmin()) { + PutModule("Access denied"); + return; + } + CString sUser = sLine.Token(1); + CString sChan = sLine.Token(2).Left(32); + CUser* pUser = CZNC::Get().FindUser(sUser); + CPartylineChannel* pChan; + + if(sChan.Left(2) != "~#") { + PutModule("Invalid channel name"); + return; + } + + if(pUser == NULL) { + PutModule("Unknown User '" + sUser + "'"); + return; + } + + pChan = GetChannel(sChan); + JoinUser(pUser, pChan); + pChan->AddFixedNick(sUser); + + // Save the fixed channel + // every channel has a , at its start, so its easier + // to remove one. + SetNV(sUser, GetNV(sUser) + "," + sChan); + + PutModule("Fixed " + sUser + " to channel " + sChan); + } else if (sCommand.CaseCmp("DELFIXCHAN") == 0) { + if(!m_pUser->IsAdmin()) { + PutModule("Access denied"); + return; + } + CString sUser = sLine.Token(1); + CString sChan = sLine.Token(2).Left(32); + CUser* pUser = CZNC::Get().FindUser(sUser); + CPartylineChannel* pChan = FindChannel(sChan); + + if(!pChan || pChan->IsFixedChan(sUser)) { + PutModule(sUser + " is not in " + sChan + " or isnt fixed to it"); + return; + } + + PartUser(pUser, pChan, true); + + CString sFixed = GetNV(sUser); + sFixed.Replace("," + sChan, ""); + SetNV(sUser, sChan); + + PutModule("Removed " + sUser + " from " + sChan); + } else if (sCommand.CaseCmp("LISTFIXCHANS") == 0) { + if(!m_pUser->IsAdmin()) { + PutModule("Access denied"); + return; + } + CString sUser = sLine.Token(1); + CUser* pUser = CZNC::Get().FindUser(sUser); + if(!pUser) { + PutModule("User not found!"); + return; + } + + for (set::const_iterator a = m_ssChannels.begin(); a != m_ssChannels.end(); a++) { + if((*a)->IsFixedChan(sUser)) { + PutModule((*a)->GetName()); + } + } + PutModule("--- End of list"); + } else if (sCommand.CaseCmp("LISTFIXUSERS") == 0) { + if(!m_pUser->IsAdmin()) { + PutModule("Access denied"); + return; + } + CString sChan = sLine.Token(1).Left(32); + CPartylineChannel* pChan = FindChannel(sChan); + + if(!pChan) { + PutModule("Channel does not exist!"); + return; + } + const set& sNicks = pChan->GetNicks(); + for(set::const_iterator it = sNicks.begin(); it != sNicks.end(); it++) { + if(pChan->IsFixedChan(*it)) { + PutModule(*it); + } + } + PutModule("--- End of list"); + } else { PutModule("Unkown command, try 'HELP'"); } } - bool PutChan(const CString& sChan, const CString& sLine, bool bIncludeCurUser = true, bool bIncludeClient = true) { - map >::iterator it = m_msChans.find(sChan.AsLower()); + bool PutChan(const CString& sChan, const CString& sLine, bool bIncludeCurUser = true, bool bIncludeClient = true, CUser* pUser = NULL, CClient* pClient = NULL) { + CPartylineChannel* pChannel = FindChannel(sChan); - if (it != m_msChans.end()) { - PutChan(it->second, sLine, bIncludeCurUser, bIncludeClient); + if (pChannel != NULL) { + PutChan(pChannel->GetNicks(), sLine, bIncludeCurUser, bIncludeClient, pUser, pClient); return true; } return false; } - void PutChan(const set& ssNicks, const CString& sLine, bool bIncludeCurUser = true, bool bIncludeClient = true) { + void PutChan(const set& ssNicks, const CString& sLine, bool bIncludeCurUser = true, bool bIncludeClient = true, CUser* pUser = NULL, CClient* pClient = NULL) { const map& msUsers = CZNC::Get().GetUserMap(); + if(!pUser) + pUser = m_pUser; + if(!pClient) + pClient = m_pClient; + for (map::const_iterator it = msUsers.begin(); it != msUsers.end(); it++) { if (ssNicks.find(it->first) != ssNicks.end()) { - if (it->second == m_pUser) { + if (it->second == pUser) { if (bIncludeCurUser) { - it->second->PutUser(sLine, NULL, (bIncludeClient ? NULL : m_pClient)); + it->second->PutUser(sLine, NULL, (bIncludeClient ? NULL : pClient)); } } else { it->second->PutUser(sLine); @@ -351,7 +535,7 @@ public: } } - void SendNickList(set& ssNicks, const CString& sChan) { + void SendNickList(CUser* pUser, const set& ssNicks, const CString& sChan) { CString sNickList; for (set::iterator it = ssNicks.begin(); it != ssNicks.end(); it++) { CUser* pUser = CZNC::Get().FindUser(*it); @@ -363,23 +547,44 @@ public: sNickList += "?" + (*it) + " "; if (sNickList.size() >= 500) { - m_pUser->PutUser(":" + m_pUser->GetIRCServer() + " 353 " + m_pUser->GetIRCNick().GetNick() + " @ " + sChan + " :" + sNickList); + pUser->PutUser(":" + pUser->GetIRCServer() + " 353 " + pUser->GetIRCNick().GetNick() + " @ " + sChan + " :" + sNickList); sNickList.clear(); } } if (sNickList.size()) { - m_pUser->PutUser(":" + m_pUser->GetIRCServer() + " 353 " + m_pUser->GetIRCNick().GetNick() + " @ " + sChan + " :" + sNickList); + pUser->PutUser(":" + pUser->GetIRCServer() + " 353 " + pUser->GetIRCNick().GetNick() + " @ " + sChan + " :" + sNickList); } - m_pUser->PutUser(":" + m_pUser->GetIRCServer() + " 366 " + m_pUser->GetIRCNick().GetNick() + " " + sChan + " :End of /NAMES list."); + pUser->PutUser(":" + pUser->GetIRCServer() + " 366 " + pUser->GetIRCNick().GetNick() + " " + sChan + " :End of /NAMES list."); + } + + CPartylineChannel* FindChannel(const CString& sChan) { + CString sChannel = sChan.AsLower(); + + for(set::iterator it = m_ssChannels.begin(); it != m_ssChannels.end(); it++) { + if((*it)->GetName().AsLower() == sChannel) + return *it; + } + + return NULL; + } + + CPartylineChannel* GetChannel(const CString& sChannel) { + CPartylineChannel* pChannel = FindChannel(sChannel); + + if(pChannel == NULL) { + pChannel = new CPartylineChannel(sChannel.AsLower()); + m_ssChannels.insert(pChannel); + } + + return pChannel; } private: - map > m_msChans; + set m_ssChannels; set m_spInjectedPrefixes; set m_ssDefaultChans; - MCString m_msTopics; }; GLOBALMODULEDEFS(CPartylineMod, "Internal channels and queries for users connected to znc"); diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp index eb2e345f..29b54d39 100644 --- a/modules/webadmin.cpp +++ b/modules/webadmin.cpp @@ -805,6 +805,7 @@ bool CWebAdminSock::UserPage(CString& sPageRet, CUser* pUser) { m_Template["QuitMsg"] = pUser->GetQuitMsg(); m_Template["DefaultChanModes"] = pUser->GetDefaultChanModes(); m_Template["BufferCount"] = CString(pUser->GetBufferCount()); + m_Template["TimestampFormat"] = pUser->GetTimestampFormat(); const set& ssAllowedHosts = pUser->GetAllowedHosts(); for (set::const_iterator it = ssAllowedHosts.begin(); it != ssAllowedHosts.end(); it++) { @@ -917,17 +918,22 @@ bool CWebAdminSock::UserPage(CString& sPageRet, CUser* pUser) { o6["DisplayName"] = "Use Client IP"; if (pUser && pUser->UseClientIP()) { o6["Checked"] = "true"; } - if (IsAdmin()) { - CTemplate& o7 = m_Template.AddRow("OptionLoop"); - o7["Name"] = "denyloadmod"; - o7["DisplayName"] = "Deny LoadMod"; - if (pUser && pUser->DenyLoadMod()) { o7["Checked"] = "true"; } + CTemplate& o7 = m_Template.AddRow("OptionLoop"); + o7["Name"] = "appendtimestamp"; + o7["DisplayName"] = "Append Timestamp"; + if (pUser && pUser->GetTimestampAppend()) { o7["Checked"] = "true"; } + if (IsAdmin()) { CTemplate& o8 = m_Template.AddRow("OptionLoop"); - o8["Name"] = "isadmin"; - o8["DisplayName"] = "Admin"; - if (pUser && pUser->IsAdmin()) { o8["Checked"] = "true"; } - if (pUser && pUser == CZNC::Get().FindUser(GetUser())) { o8["Disabled"] = "true"; } + o8["Name"] = "denyloadmod"; + o8["DisplayName"] = "Deny LoadMod"; + if (pUser && pUser->DenyLoadMod()) { o8["Checked"] = "true"; } + + CTemplate& o9 = m_Template.AddRow("OptionLoop"); + o9["Name"] = "isadmin"; + o9["DisplayName"] = "Admin"; + if (pUser && pUser->IsAdmin()) { o9["Checked"] = "true"; } + if (pUser && pUser == CZNC::Get().FindUser(GetUser())) { o9["Disabled"] = "true"; } } PrintPage(sPageRet, "UserPage.tmpl"); @@ -1080,6 +1086,7 @@ CUser* CWebAdminSock::GetNewUser(CString& sPageRet, CUser* pUser) { sArg = GetParam("vhost"); if (!sArg.empty()) { pNewUser->SetVHost(sArg); } sArg = GetParam("quitmsg"); if (!sArg.empty()) { pNewUser->SetQuitMsg(sArg); } sArg = GetParam("chanmodes"); if (!sArg.empty()) { pNewUser->SetDefaultChanModes(sArg); } + sArg = GetParam("timestampformat"); if (!sArg.empty()) { pNewUser->SetTimestampFormat(sArg); } pNewUser->SetBufferCount(GetParam("bufsize").ToUInt()); pNewUser->SetKeepBuffer(GetParam("keepbuffer").ToBool()); @@ -1088,6 +1095,7 @@ CUser* CWebAdminSock::GetNewUser(CString& sPageRet, CUser* pUser) { pNewUser->SetAutoCycle(GetParam("autocycle").ToBool()); pNewUser->SetKeepNick(GetParam("keepnick").ToBool()); pNewUser->SetUseClientIP(GetParam("useclientip").ToBool()); + pNewUser->SetTimestampAppend(GetParam("appendtimestamp").ToBool()); if (IsAdmin()) { pNewUser->SetDenyLoadMod(GetParam("denyloadmod").ToBool()); diff --git a/modules/webadmin/skins/default.de/UserPage.tmpl b/modules/webadmin/skins/default.de/UserPage.tmpl index 8d4e331c..129fcea4 100644 --- a/modules/webadmin/skins/default.de/UserPage.tmpl +++ b/modules/webadmin/skins/default.de/UserPage.tmpl @@ -178,9 +178,12 @@
-
Größe des :
+
Größe des Playbackbuffers:
+
Zeitstempel:
+
+
diff --git a/modules/webadmin/skins/default/UserPage.tmpl b/modules/webadmin/skins/default/UserPage.tmpl index 9a3248c0..5a837abb 100644 --- a/modules/webadmin/skins/default/UserPage.tmpl +++ b/modules/webadmin/skins/default/UserPage.tmpl @@ -181,6 +181,10 @@
Playback Buffer Size:
+
+
Timestamp Format:
+
+
diff --git a/modules/webadmin/skins/graphiX.de/UserPage.tmpl b/modules/webadmin/skins/graphiX.de/UserPage.tmpl index f4e9f22b..96e4e2e7 100644 --- a/modules/webadmin/skins/graphiX.de/UserPage.tmpl +++ b/modules/webadmin/skins/graphiX.de/UserPage.tmpl @@ -253,6 +253,16 @@ + Zeitstempel: + + + + + + + + + Optionen: diff --git a/modules/webadmin/skins/graphiX/UserPage.tmpl b/modules/webadmin/skins/graphiX/UserPage.tmpl index cf5db46b..596ed3cc 100755 --- a/modules/webadmin/skins/graphiX/UserPage.tmpl +++ b/modules/webadmin/skins/graphiX/UserPage.tmpl @@ -251,6 +251,16 @@ + + + Timestamp Format: + + + + + + + Options: diff --git a/modules/webadmin/skins/ice.de/UserPage.tmpl b/modules/webadmin/skins/ice.de/UserPage.tmpl index 652d40dd..78d7a632 100644 --- a/modules/webadmin/skins/ice.de/UserPage.tmpl +++ b/modules/webadmin/skins/ice.de/UserPage.tmpl @@ -180,6 +180,10 @@
Größe des Wiedergabebuffers:
+
+
Zeitstempel:
+
+
diff --git a/modules/webadmin/skins/ice/UserPage.tmpl b/modules/webadmin/skins/ice/UserPage.tmpl index 1bde2f56..4612f1f2 100644 --- a/modules/webadmin/skins/ice/UserPage.tmpl +++ b/modules/webadmin/skins/ice/UserPage.tmpl @@ -180,6 +180,10 @@
Playback Buffer Size:
+
+
Timestamp Format:
+
+
diff --git a/znc.cpp b/znc.cpp index 8d4efd7a..f5443c64 100644 --- a/znc.cpp +++ b/znc.cpp @@ -20,6 +20,7 @@ CZNC::CZNC() { m_pModules = new CGlobalModules(); #endif m_pISpoofLockFile = NULL; + m_uiConnectDelay = 30; SetISpoofFormat(""); // Set ISpoofFormat to default } @@ -53,12 +54,12 @@ CZNC::~CZNC() { CString CZNC::GetTag(bool bIncludeVersion) { if (!bIncludeVersion) { - return "ZNC - by prozac@rottenboy.com"; + return "ZNC by prozac - http://znc.sourceforge.net"; } char szBuf[128]; memset(szBuf, 0, 128); - snprintf(szBuf, 127, "ZNC %1.3f - by prozac@rottenboy.com", VERSION); + snprintf(szBuf, 127, "ZNC %1.3f by prozac - http://znc.sourceforge.net", VERSION); return szBuf; } @@ -82,6 +83,7 @@ bool CZNC::OnBoot() { int CZNC::Loop() { m_Manager.SetSelectTimeout(10000); m_itUserIter = m_msUsers.begin(); + time_t tNextConnect = 0; while (true) { // Check for users that need to be deleted @@ -131,6 +133,10 @@ int CZNC::Loop() { continue; } + if (tNextConnect > time(NULL)) { + continue; + } + CString sSockName = "IRC::" + m_itUserIter->first; CUser* pUser = m_itUserIter->second; @@ -149,6 +155,8 @@ int CZNC::Loop() { continue; } + tNextConnect = time(NULL) + m_uiConnectDelay; + if(!WriteISpoof(pUser)) { DEBUG_ONLY(cout << "ISpoof could not be written" << endl); pUser->PutStatus("ISpoof could not be written, retrying..."); @@ -986,6 +994,23 @@ bool CZNC::ParseConfig(const CString& sConfig) { } else if (sName.CaseCmp("Chan") == 0) { pUser->AddChan(sValue, true); continue; + } else if (sName.CaseCmp("TimestampFormat") == 0) { + pUser->SetTimestampFormat(sValue); + continue; + } else if (sName.CaseCmp("AppendTimestamp") == 0) { + pUser->SetTimestampAppend(sValue.ToBool()); + continue; + } else if (sName.CaseCmp("Timestamp") == 0) { + if(sValue.Trim_n().CaseCmp("true") != 0) { + if(sValue.Trim_n().CaseCmp("append") == 0) { + pUser->SetTimestampAppend(true); + } else if(sValue.Trim_n().CaseCmp("false") == 0) { + pUser->SetTimestampFormat(""); + } else { + pUser->SetTimestampFormat(sValue); + } + } + continue; } else if (sName.CaseCmp("LoadModule") == 0) { CString sModName = sValue.Token(0); CUtils::PrintAction("Loading Module [" + sModName + "]"); @@ -1140,6 +1165,9 @@ bool CZNC::ParseConfig(const CString& sConfig) { } else if (sName.CaseCmp("StatusPrefix") == 0) { m_sStatusPrefix = sValue; continue; + } else if (sName.CaseCmp("ConnectDelay") == 0) { + m_uiConnectDelay = sValue.ToUInt(); + continue; } } } @@ -1159,6 +1187,8 @@ bool CZNC::ParseConfig(const CString& sConfig) { return false; } + GetModules().OnFinishedConfig(); + return true; } diff --git a/znc.h b/znc.h index 3a9abf36..aba7b89e 100644 --- a/znc.h +++ b/znc.h @@ -175,6 +175,7 @@ protected: CLockFile m_LockFile; CLockFile* m_pISpoofLockFile; map::iterator m_itUserIter; // This needs to be reset to m_msUsers.begin() if anything is added or removed to the map + uint m_uiConnectDelay; #ifdef _MODULES CGlobalModules* m_pModules; #endif