#include "main.h" #include "User.h" #include "Nick.h" #include "Modules.h" #include "Chan.h" #include "znc.h" #include "HTTPSock.h" #include "Server.h" class CWebAdminMod; class CWebAdminSock : public CHTTPSock { public: CWebAdminSock(CWebAdminMod* pModule); CWebAdminSock(CWebAdminMod* pModule, const CString& sHostname, unsigned short uPort, int iTimeout = 60); virtual ~CWebAdminSock(); virtual bool OnPageRequest(const CString& sURI, CString& sPageRet); virtual bool OnLogin(const CString& sUser, const CString& sPass); CString Header(const CString& sTitle); CString Footer(); void PrintMainPage(CString& sPageRet) { sPageRet = Header("Main Page"); sPageRet += "Welcome to the ZNC webadmin module.\r\n"; sPageRet += Footer(); } void GetErrorPage(CString& sPageRet, const CString& sError) { sPageRet = Header("Error"); sPageRet += "

" + sError.Escape_n(CString::EHTML) + "

\r\n"; sPageRet += Footer(); } void ListUsersPage(CString& sPageRet); bool SettingsPage(CString& sPageRet); bool UserPage(CString& sPageRet, CUser* pUser = NULL); CUser* GetNewUser(CString& sPageRet, CUser* pUser); void ListPage(CString& sPageRet) { VCString vsParams; const map& msvsParams = GetParams(); sPageRet = Header("fooooooo"); if (msvsParams.empty()) { sPageRet += "You passed in no params.\r\n"; } else { sPageRet += "foo [" + GetParamString().Escape_n(CString::EHTML) + "]

"; for (map::const_iterator it = msvsParams.begin(); it != msvsParams.end(); it++) { sPageRet += "

" + it->first + "

\r\n\r\n"; } } sPageRet += Footer(); } virtual Csock* GetSockObj(const CString& sHost, unsigned short uPort); bool IsAdmin() const { return m_bAdmin; } private: protected: CWebAdminMod* m_pModule; CUser* m_pUser; bool m_bAdmin; }; class CWebAdminMod : public CGlobalModule { public: CWebAdminMod(void *pDLL, CZNC* pZNC, const CString& sModName) : CGlobalModule(pDLL, pZNC, sModName) { m_uPort = 8080; } virtual ~CWebAdminMod() { for (set::iterator it = m_sSocks.begin(); it != m_sSocks.end(); it++) { m_pManager->DelSockByAddr(*it); } } virtual bool OnBoot() { return true; } virtual bool OnLoad(const CString& sArgs) { bool bSSL = false; CString sPort = sArgs.Token(0); if (sPort.Left(1) == "+") { #ifdef HAVE_LIBSSL sPort.TrimLeft("+"); bSSL = true; #else return false; #endif } m_uPort = sPort.ToUInt(); m_sUser = sArgs.Token(1); m_sPass = sArgs.Token(2); if (m_sPass.empty()) { return false; } CWebAdminSock* pListenSock = new CWebAdminSock(this); #ifdef HAVE_LIBSSL if (bSSL) { pListenSock->SetPemLocation(m_pZNC->GetPemLocation()); } #endif return m_pManager->ListenAll(m_uPort, "WebAdmin::Listener", bSSL, SOMAXCONN, pListenSock); } void AddSock(CWebAdminSock* pSock) { m_sSocks.insert(pSock); } void SockDestroyed(CWebAdminSock* pSock) { m_sSocks.erase(pSock); } const CString& GetUser() const { return m_sUser; } const CString& GetPass() const { return m_sPass; } private: unsigned int m_uPort; CString m_sUser; CString m_sPass; set m_sSocks; }; CString CWebAdminSock::Header(const CString& sTitle) { CString sRet = "\r\n" "\r\nZNC - " + sTitle.Escape_n(CString::EHTML) + "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "

" + sTitle.Escape_n(CString::EHTML) + "

\r\n"; if (!m_pUser) { sRet += "[Home]
\r\n" "[Settings]
\r\n" "[Add User]
\r\n" "[List Users]
\r\n"; } sRet += "
\r\n"; return sRet; } CString CWebAdminSock::Footer() { return "
" + m_pModule->GetZNC()->GetTag() + "
\r\n\r\n"; } bool CWebAdminSock::OnLogin(const CString& sUser, const CString& sPass) { if (GetUser() == m_pModule->GetUser() && GetPass() == m_pModule->GetPass()) { m_bAdmin = true; return true; } CUser* pUser = m_pModule->GetZNC()->FindUser(GetUser()); if (pUser && pUser->CheckPass(GetPass())) { m_pUser = pUser; return true; } return false; } void CWebAdminSock::ListUsersPage(CString& sPageRet) { const map& msUsers = m_pModule->GetZNC()->GetUserMap(); sPageRet = Header("List Users"); if (!msUsers.size()) { sPageRet += "There are no users defined. Click here if you would like to add one.\r\n"; } else { sPageRet += "\r\n" "\t\r\n"; unsigned int a = 0; for (map::const_iterator it = msUsers.begin(); it != msUsers.end(); it++, a++) { CServer* pServer = it->second->GetCurrentServer(); sPageRet += "\t\r\n\t\t\r\n" "\t\t\r\n" "\t\t\r\n" "\t"; } sPageRet +="\r\n
ActionUsernameCurrent Server
[second->GetUserName().Escape_n(CString::EURL) + "\">Edit] [second->GetUserName().Escape_n(CString::EURL) + "\">Delete]" + it->second->GetUserName().Escape_n(CString::EHTML) + "" + CString((pServer) ? pServer->GetName().Escape_n(CString::EHTML) : "-N/A-") + "
\r\n"; } sPageRet += Footer(); } Csock* CWebAdminSock::GetSockObj(const CString& sHost, unsigned short uPort) { CWebAdminSock* pSock = new CWebAdminSock(m_pModule, sHost, uPort); pSock->SetSockName("WebAdmin::Client"); pSock->SetTimeout(120); m_pModule->AddSock(pSock); return pSock; } CWebAdminSock::CWebAdminSock(CWebAdminMod* pModule) : CHTTPSock() { m_pModule = pModule; m_pUser = NULL; m_bAdmin = false; m_pModule->AddSock(this); } CWebAdminSock::CWebAdminSock(CWebAdminMod* pModule, const CString& sHostname, unsigned short uPort, int iTimeout) : CHTTPSock(sHostname, uPort, iTimeout) { m_pModule = pModule; m_pUser = NULL; m_bAdmin = false; m_pModule->AddSock(this); } CWebAdminSock::~CWebAdminSock() { m_pModule->SockDestroyed(this); } bool CWebAdminSock::OnPageRequest(const CString& sURI, CString& sPageRet) { DEBUG_ONLY(cout << "Request for [" << sURI << "] "); if (!ForceLogin()) { DEBUG_ONLY(cout << "- User not logged in!" << endl); return false; } if (sURI == "/") { if (m_pUser) { Redirect("/edituser"); return false; } PrintMainPage(sPageRet); } else if (sURI == "/settings") { if (m_pUser) { return false; } if (!SettingsPage(sPageRet)) { DEBUG_ONLY(cout << "- 302 Redirect" << endl); return false; } } else if (sURI == "/adduser") { if (m_pUser) { return false; } if (!UserPage(sPageRet)) { DEBUG_ONLY(cout << "- 302 Redirect" << endl); return false; } } else if (sURI == "/edituser") { CUser* pUser = (m_pUser) ? m_pUser : m_pModule->GetZNC()->FindUser(GetParam("user")); if (pUser) { if (!UserPage(sPageRet, pUser)) { DEBUG_ONLY(cout << "- 302 Redirect" << endl); return false; } } else { GetErrorPage(sPageRet, "No such username"); } } else if (sURI == "/listusers") { if (m_pUser) { return false; } ListUsersPage(sPageRet); } else if (sURI == "/deluser") { if (m_pUser) { return false; } if (m_pModule->GetZNC()->DeleteUser(GetParam("user"))) { DEBUG_ONLY(cout << "- 302 Redirect" << endl); Redirect("/listusers"); return false; } else { GetErrorPage(sPageRet, "No such username"); } //} else if (sURI == "/list") { // ListPage(sPageRet); } else { DEBUG_ONLY(cout << "- 404 Not Found!" << endl); return false; } DEBUG_ONLY(cout << "- 200 OK!" << endl); return true; } bool CWebAdminSock::SettingsPage(CString& sPageRet) { if (!GetParam("submitted").ToUInt()) { sPageRet = Header("Settings"); CString sVHosts; const VCString& vsVHosts = m_pModule->GetZNC()->GetVHosts(); for (unsigned int a = 0; a < vsVHosts.size(); a++) { sVHosts += vsVHosts[a] + "\r\n"; } sPageRet += "
\r\n" "\r\n" "
Global Settings
\r\n" "
Status Prefix:
\r\n" "

\r\n" "
ISpoofFile:
\r\n" "
\r\n" "
ISpoofFormat:
\r\n" "
\r\n" "
VHosts:
\r\n" "
\r\n" "


\r\n" "
Global Modules
\r\n"; set ssGlobalMods; m_pModule->GetZNC()->GetModules().GetAvailableMods(ssGlobalMods, m_pModule->GetZNC(), true); for (set::iterator it = ssGlobalMods.begin(); it != ssGlobalMods.end(); it++) { const CModInfo& Info = *it; sPageRet += "" " (" + Info.GetDescription().Escape_n(CString::EHTML) + ")
"; } sPageRet += "


\r\n" "\r\n" "
\r\n"; sPageRet += Footer(); return true; } CString sArg; sArg = GetParam("statusprefix"); m_pModule->GetZNC()->SetStatusPrefix(sArg); sArg = GetParam("ispooffile"); m_pModule->GetZNC()->SetISpoofFile(sArg); sArg = GetParam("ispoofformat"); m_pModule->GetZNC()->SetISpoofFormat(sArg); //sArg = GetParam(""); if (!sArg.empty()) { m_pModule->GetZNC()->Set(sArg); } VCString vsArgs = GetParam("vhosts").Split("\n"); m_pModule->GetZNC()->ClearVHosts(); unsigned int a = 0; for (a = 0; a < vsArgs.size(); a++) { m_pModule->GetZNC()->AddVHost(vsArgs[a].Trim_n()); } set ssArgs; GetParamValues("loadmod", ssArgs); for (set::iterator it = ssArgs.begin(); it != ssArgs.end(); it++) { CString sModRet; sArg = (*it).TrimRight_n("\r"); if (!sArg.empty()) { try { if (!m_pModule->GetZNC()->GetModules().FindModule(sArg)) { m_pModule->GetZNC()->GetModules().LoadModule(sArg, "", NULL, sModRet); } } catch(...) {} } } const CModules& vCurMods = m_pModule->GetZNC()->GetModules(); set ssUnloadMods; for (a = 0; a < vCurMods.size(); a++) { CModule* pCurMod = vCurMods[a]; if (ssArgs.find(pCurMod->GetModName()) == ssArgs.end() && pCurMod->GetModName() != m_pModule->GetModName()) { ssUnloadMods.insert(pCurMod->GetModName()); } } for (set::iterator it2 = ssUnloadMods.begin(); it2 != ssUnloadMods.end(); it2++) { m_pModule->GetZNC()->GetModules().UnloadModule(*it2); } if (!m_pModule->GetZNC()->WriteConfig()) { GetErrorPage(sPageRet, "Settings changed, but config was not written"); return true; } Redirect("/"); return false; } bool CWebAdminSock::UserPage(CString& sPageRet, CUser* pUser) { if (!GetParam("submitted").ToUInt()) { sPageRet = Header((pUser) ? CString("Edit User [" + pUser->GetUserName() + "]") : CString("Add User")); CString sAllowedHosts, sServers, sChans, sCTCPReplies; if (pUser) { const set& ssAllowedHosts = pUser->GetAllowedHosts(); for (set::const_iterator it = ssAllowedHosts.begin(); it != ssAllowedHosts.end(); it++) { sAllowedHosts += *it + "\r\n"; } const vector& vServers = pUser->GetServers(); for (unsigned int a = 0; a < vServers.size(); a++) { sServers += vServers[a]->GetString() + "\r\n"; } const vector& vChans = pUser->GetChans(); for (unsigned int b = 0; b < vChans.size(); b++) { CChan* pChan = vChans[b]; if (pChan->InConfig()) { sChans += vChans[b]->GetName() + "\r\n"; } } const MCString& msCTCPReplies = pUser->GetCTCPReplies(); for (MCString::const_iterator it2 = msCTCPReplies.begin(); it2 != msCTCPReplies.end(); it2++) { sCTCPReplies += it2->first + " " + it2->second + "\r\n"; } } sPageRet += "
\r\n"; sPageRet += "\r\n" "
Authentication
\r\n" "
Username:
\r\n"; if (pUser) { sPageRet += "\r\n" "
\r\n"; } else { sPageRet += "
\r\n"; } sPageRet += "Password:

\r\n" "Confirm Password:
\r\n" "
Allowed IPs:
\r\n" "


\r\n" "
IRC Information
\r\n" "
Nick:
\r\n" "
\r\n" "
AltNick:
\r\n" "
\r\n" "
AwaySuffix:
\r\n" "
\r\n" "
StatusPrefix:
\r\n" "

\r\n" "
Ident:
\r\n" "
\r\n" "
RealName:
\r\n" "

\r\n"; const VCString& vsVHosts = m_pModule->GetZNC()->GetVHosts(); if (vsVHosts.size()) { sPageRet += "VHost:
\r\n" "

\r\n"; } sPageRet += "QuitMsg:
\r\n" "

\r\n" "
Servers:
\r\n" "
\r\n" "


\r\n" "
Modules
\r\n"; set ssUserMods; m_pModule->GetZNC()->GetModules().GetAvailableMods(ssUserMods, m_pModule->GetZNC()); for (set::iterator it = ssUserMods.begin(); it != ssUserMods.end(); it++) { const CModInfo& Info = *it; sPageRet += "" " (" + Info.GetDescription().Escape_n(CString::EHTML) + ")
"; } sPageRet += "


\r\n" "
Channels
\r\n" "Default Modes:
\r\n" "

\r\n" "
Channels:
\r\n" "
\r\n" "


\r\n" "
ZNC Behavior
\r\n" "Playback Buffer Size:
\r\n" "

\r\n" "Options:
\r\n" "  \r\n" "  \r\n" "  \r\n" "  \r\n" "  \r\n"; if (IsAdmin()) { sPageRet += "  \r\n"; } sPageRet += "

" "
CTCP Replies:
" "
\r\n" "


\r\n" "\r\n" "
\r\n"; sPageRet += Footer(); return true; } CString sUsername = GetParam("user"); if (!pUser && m_pModule->GetZNC()->FindUser(sUsername)) { GetErrorPage(sPageRet, "Invalid Submission [User " + sUsername + " already exists]"); return true; } CUser* pNewUser = GetNewUser(sPageRet, pUser); if (!pNewUser) { return true; } CString sErr; if (!pUser) { // Add User Submission if (!pNewUser->IsValid(sErr)) { delete pNewUser; GetErrorPage(sPageRet, "Invalid submission [" + sErr + "]"); return true; } m_pModule->GetZNC()->AddUser(pNewUser); if (!m_pModule->GetZNC()->WriteConfig()) { GetErrorPage(sPageRet, "User added, but config was not written"); return true; } } else { // Edit User Submission if (!pUser->Clone(*pNewUser, sErr)) { delete pNewUser; GetErrorPage(sPageRet, "Invalid Submission [" + sErr + "]"); return true; } delete pNewUser; if (!m_pModule->GetZNC()->WriteConfig()) { GetErrorPage(sPageRet, "User edited, but config was not written"); return true; } } if (m_pUser) { Redirect("/edituser"); } else { Redirect("/listusers"); } return false; } CUser* CWebAdminSock::GetNewUser(CString& sPageRet, CUser* pUser) { CString sUsername = GetParam("newuser"); if (sUsername.empty()) { sUsername = GetParam("user"); } if (sUsername.empty()) { GetErrorPage(sPageRet, "Invalid Submission [Username is required]"); return NULL; } CString sArg = GetParam("password"); if (sArg != GetParam("password2")) { GetErrorPage(sPageRet, "Invalid Submission [Passwords do not match]"); return NULL; } CUser* pNewUser = new CUser(sUsername, m_pModule->GetZNC()); if (!sArg.empty()) { pNewUser->SetPass(sArg.MD5(), true); } VCString vsArgs; GetParam("servers").Split("\n", vsArgs); unsigned int a = 0; for (a = 0; a < vsArgs.size(); a++) { pNewUser->AddServer(vsArgs[a].Trim_n()); } GetParam("allowedips").Split("\n", vsArgs); for (a = 0; a < vsArgs.size(); a++) { pNewUser->AddAllowedHost(vsArgs[a].Trim_n()); } GetParam("ctcpreplies").Split("\n", vsArgs); for (a = 0; a < vsArgs.size(); a++) { CString sReply = vsArgs[a].TrimRight_n("\r"); pNewUser->AddCTCPReply(sReply.Token(0).Trim_n(), sReply.Token(1, true).Trim_n()); } if (IsAdmin() || (pUser && !pUser->DenyLoadMod())) { GetParamValues("loadmod", vsArgs); for (a = 0; a < vsArgs.size(); a++) { CString sModRet; CString sArg = vsArgs[a].TrimRight_n("\r"); if (!sArg.empty()) { try { if (!pNewUser->GetModules().LoadModule(sArg, "", pNewUser, sModRet)) { DEBUG_ONLY(cerr << "Unable to load module [" << sArg << "] [" << sModRet << "]" << endl); } } catch (...) { DEBUG_ONLY(cerr << "Unable to load module [" << sArg << "]" << endl); } } } } else if (pUser) { CModules& Modules = pUser->GetModules(); for (a = 0; a < Modules.size(); a++) { CString sModName = Modules[a]->GetModName(); CString sModRet; try { if (!pNewUser->GetModules().LoadModule(sModName, "", pNewUser, sModRet)) { DEBUG_ONLY(cerr << "Unable to load module [" << sModName << "] [" << sModRet << "]" << endl); } } catch (...) { DEBUG_ONLY(cerr << "Unable to load module [" << sModName << "]" << endl); } } } sArg = GetParam("nick"); if (!sArg.empty()) { pNewUser->SetNick(sArg); } sArg = GetParam("altnick"); if (!sArg.empty()) { pNewUser->SetAltNick(sArg); } sArg = GetParam("awaysuffix"); if (!sArg.empty()) { pNewUser->SetAwaySuffix(sArg); } sArg = GetParam("statusprefix"); if (!sArg.empty()) { pNewUser->SetStatusPrefix(sArg); } sArg = GetParam("ident"); if (!sArg.empty()) { pNewUser->SetIdent(sArg); } sArg = GetParam("realname"); if (!sArg.empty()) { pNewUser->SetRealName(sArg); } 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); } pNewUser->SetBufferCount(GetParam("bufsize").ToUInt()); pNewUser->SetKeepBuffer(GetParam("keepbuffer").ToBool()); pNewUser->SetBounceDCCs(GetParam("bouncedccs").ToBool()); pNewUser->SetAutoCycle(GetParam("autocycle").ToBool()); pNewUser->SetKeepNick(GetParam("keepnick").ToBool()); pNewUser->SetUseClientIP(GetParam("useclientip").ToBool()); if (IsAdmin()) { pNewUser->SetDenyLoadMod(GetParam("denyloadmod").ToBool()); } else if (pUser) { pNewUser->SetDenyLoadMod(pUser->DenyLoadMod()); } GetParam("channels").Split("\n", vsArgs); for (a = 0; a < vsArgs.size(); a++) { pNewUser->AddChan(vsArgs[a].TrimRight_n("\r"), true); } return pNewUser; } GLOBALMODULEDEFS(CWebAdminMod, "Dynamic configuration of users/settings through a web browser")