mirror of
https://github.com/znc/znc.git
synced 2026-07-04 17:01:23 +02:00
Initial commit of webmods - still lots of work to be done
git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@1784 726aef4b-f618-498e-8847-2d620e286838
This commit is contained in:
+22
-1
@@ -12,6 +12,7 @@
|
||||
#include "IRCSock.h"
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
#include "WebModules.h"
|
||||
|
||||
#define CALLMOD(MOD, CLIENT, USER, FUNC) { \
|
||||
CModule* pModule = CZNC::Get().GetModules().FindModule(MOD); \
|
||||
@@ -67,8 +68,28 @@ void CClient::ReadLine(const CString& sData) {
|
||||
if (IsAttached()) {
|
||||
MODULECALL(OnUserRaw(sLine), m_pUser, this, return);
|
||||
} else {
|
||||
if (CZNC::Get().GetModules().OnUnknownUserRaw(this, sLine))
|
||||
// If it's an HTTP Request - Check the webmods
|
||||
if (sLine.WildCmp("GET * HTTP/1.?") || sLine.WildCmp("POST * HTTP/1.?")) {
|
||||
CModule* pMod = new CModule(NULL, "<webmod>", "");
|
||||
pMod->SetFake(true);
|
||||
|
||||
CWebSock* pSock = new CWebSock(pMod);
|
||||
CZNC::Get().GetManager().SwapSockByAddr(pSock, this);
|
||||
|
||||
// And don't forget to give it some sane name / timeout
|
||||
pSock->SetSockName("WebMod::Client");
|
||||
pSock->SetTimeout(120);
|
||||
|
||||
// TODO can we somehow get rid of this?
|
||||
pSock->ReadLine(sLine);
|
||||
pSock->PushBuff("", 0, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (CZNC::Get().GetModules().OnUnknownUserRaw(this, sLine)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+3
-1
@@ -22,7 +22,7 @@ PKGCONFIGDIR := $(libdir)/pkgconfig
|
||||
|
||||
LIB_SRCS := ZNCString.cpp Csocket.cpp znc.cpp User.cpp IRCSock.cpp Client.cpp DCCBounce.cpp \
|
||||
DCCSock.cpp Chan.cpp Nick.cpp Server.cpp Modules.cpp MD5.cpp Buffer.cpp Utils.cpp \
|
||||
FileUtils.cpp HTTPSock.cpp Template.cpp ClientCommand.cpp Socket.cpp SHA256.cpp
|
||||
FileUtils.cpp HTTPSock.cpp Template.cpp ClientCommand.cpp Socket.cpp SHA256.cpp WebModules.cpp
|
||||
BIN_SRCS := main.cpp
|
||||
LIB_OBJS := $(patsubst %cpp,%o,$(LIB_SRCS))
|
||||
BIN_OBJS := $(patsubst %cpp,%o,$(BIN_SRCS))
|
||||
@@ -75,6 +75,8 @@ install: znc $(LIBZNC)
|
||||
mkdir -p $(DESTDIR)$(bindir)
|
||||
mkdir -p $(DESTDIR)$(includedir)/znc
|
||||
mkdir -p $(DESTDIR)$(PKGCONFIGDIR)
|
||||
mkdir -p $(DESTDIR)$(libdir)/znc
|
||||
cp -Rp webskins $(DESTDIR)$(libdir)/znc
|
||||
install -m 0755 znc $(DESTDIR)$(bindir)
|
||||
install -m 0755 znc-config $(DESTDIR)$(bindir)
|
||||
install -m 0755 znc-buildmod $(DESTDIR)$(bindir)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
#include <dlfcn.h>
|
||||
#include "WebModules.h"
|
||||
#include "Template.h"
|
||||
|
||||
#ifndef RTLD_LOCAL
|
||||
# define RTLD_LOCAL 0
|
||||
@@ -387,6 +389,10 @@ void CModule::ListSockets() {
|
||||
|
||||
CString CModule::GetModNick() const { return ((m_pUser) ? m_pUser->GetStatusPrefix() : "*") + m_sModName; }
|
||||
|
||||
// Webmods
|
||||
bool CModule::OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) { return false; }
|
||||
// !Webmods
|
||||
|
||||
bool CModule::OnLoad(const CString& sArgs, CString& sMessage) { sMessage = ""; return true; }
|
||||
bool CModule::OnBoot() { return true; }
|
||||
void CModule::OnPreRehash() {}
|
||||
@@ -701,6 +707,8 @@ bool CModules::LoadModule(const CString& sModule, const CString& sArgs, CUser* p
|
||||
pModule->SetDescription(sDesc);
|
||||
pModule->SetGlobal(bIsGlobal);
|
||||
pModule->SetArgs(sArgs);
|
||||
DEBUG("********************************* [" + CZNC::Get().GetCurPath() + "] [" + sModPath + "] [" + CDir::ChangeDir(CZNC::Get().GetCurPath(), sModPath) + "]");
|
||||
pModule->SetModPath(CDir::ChangeDir(CZNC::Get().GetCurPath(), sModPath));
|
||||
push_back(pModule);
|
||||
|
||||
bool bLoaded;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#ifndef _MODULES_H
|
||||
#define _MODULES_H
|
||||
|
||||
#include "WebModules.h"
|
||||
#include "FileUtils.h"
|
||||
#include "Utils.h"
|
||||
#include <set>
|
||||
@@ -22,6 +23,8 @@ using std::set;
|
||||
class CAuthBase;
|
||||
class CChan;
|
||||
class CClient;
|
||||
class CWebSock;
|
||||
class CTemplate;
|
||||
class CIRCSock;
|
||||
// !Forward Declarations
|
||||
|
||||
@@ -268,6 +271,17 @@ public:
|
||||
* @return false to abort ZNC startup.
|
||||
*/
|
||||
virtual bool OnBoot();
|
||||
|
||||
// For handling web traffic
|
||||
virtual bool WebRequiresLogin() { return true; }
|
||||
virtual bool WebRequiresAdmin() { return false; }
|
||||
virtual CString GetWebNavTitle() { return ""; }
|
||||
virtual bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl);
|
||||
virtual void AddSubPage(TWebSubPage spSubPage) { m_vSubPages.push_back(spSubPage); }
|
||||
virtual void ClearSubPages() { m_vSubPages.clear(); }
|
||||
virtual VWebSubPages& GetSubPages() { return m_vSubPages; }
|
||||
// !Web
|
||||
|
||||
/** Called just before znc.conf is rehashed */
|
||||
virtual void OnPreRehash();
|
||||
/** This module hook is called after a <em>successful</em> rehash. */
|
||||
@@ -680,6 +694,7 @@ public:
|
||||
void SetFake(bool b) { m_bFake = b; }
|
||||
void SetGlobal(bool b) { m_bGlobal = b; }
|
||||
void SetDescription(const CString& s) { m_sDescription = s; }
|
||||
void SetModPath(const CString& s) { m_sModPath = s; }
|
||||
void SetArgs(const CString& s) { m_sArgs = s; }
|
||||
// !Setters
|
||||
|
||||
@@ -688,6 +703,7 @@ public:
|
||||
bool IsGlobal() const { return m_bGlobal; }
|
||||
const CString& GetDescription() const { return m_sDescription; }
|
||||
const CString& GetArgs() const { return m_sArgs; }
|
||||
const CString& GetModPath() const { return m_sModPath; }
|
||||
|
||||
/** @returns For user modules this returns the user for which this
|
||||
* module was loaded. For global modules this returns NULL,
|
||||
@@ -716,8 +732,10 @@ protected:
|
||||
CString m_sDataDir;
|
||||
CString m_sSavePath;
|
||||
CString m_sArgs;
|
||||
CString m_sModPath;
|
||||
private:
|
||||
MCString m_mssRegistry; //!< way to save name/value pairs. Note there is no encryption involved in this
|
||||
VWebSubPages m_vSubPages;
|
||||
};
|
||||
|
||||
class CModules : public vector<CModule*> {
|
||||
|
||||
+65
-26
@@ -90,26 +90,34 @@ void CTemplate::Init() {
|
||||
}
|
||||
*/
|
||||
|
||||
ClearPath();
|
||||
ClearPaths();
|
||||
m_pParent = NULL;
|
||||
}
|
||||
|
||||
CString CTemplate::ExpandFile(const CString& sFilename) {
|
||||
if (sFilename.Left(1) == "/" || sFilename.Left(2) == "./") {
|
||||
CString CTemplate::ExpandFile(const CString& sFilename, bool bFromInc) {
|
||||
/*if (sFilename.Left(1) == "/" || sFilename.Left(2) == "./") {
|
||||
return sFilename;
|
||||
}
|
||||
}*/
|
||||
|
||||
CString sFile(ResolveLiteral(sFilename));
|
||||
CString sFile(ResolveLiteral(sFilename).TrimLeft_n("/"));
|
||||
|
||||
for (LCString::iterator it = m_lsPaths.begin(); it != m_lsPaths.end(); ++it) {
|
||||
CString sRoot = *it;
|
||||
for (list<pair<CString, bool> >::iterator it = m_lsbPaths.begin(); it != m_lsbPaths.end(); ++it) {
|
||||
CString& sRoot = it->first;
|
||||
CString sFilePath(CDir::ChangeDir(sRoot, sFile));
|
||||
|
||||
// Make sure path ends with a slash because "/foo/pub*" matches "/foo/public_keep_out/" but "/foo/pub/*" doesn't
|
||||
if (!sRoot.empty() && sRoot.Right(1) != "/") {
|
||||
sRoot += "/";
|
||||
}
|
||||
|
||||
if (it->second && !bFromInc) {
|
||||
DEBUG("\t\tSkipping path (not from INC) [" + sFilePath + "]");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CFile::Exists(sFilePath)) {
|
||||
// This only works if sRoot got a trailing slash! The
|
||||
// code which adds paths makes sure this is true.
|
||||
if (sRoot.empty() || sFilePath.Left(sRoot.length()) == sRoot) {
|
||||
//DEBUG("\t\tFound [" + sFilePath + "]\n");
|
||||
DEBUG("\t\tFound [" + sFilePath + "]\n");
|
||||
return sFilePath;
|
||||
} else {
|
||||
DEBUG("\t\tOutside of root [" + sFilePath + "] !~ [" + sRoot + "]");
|
||||
@@ -119,15 +127,15 @@ CString CTemplate::ExpandFile(const CString& sFilename) {
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_lsPaths.size()) {
|
||||
switch (m_lsbPaths.size()) {
|
||||
case 0:
|
||||
DEBUG("Unable to find [" + sFile + "] using the current directory");
|
||||
break;
|
||||
case 1:
|
||||
DEBUG("Unable to find [" + sFile + "] in the defined path [" + *m_lsPaths.begin());
|
||||
DEBUG("Unable to find [" + sFile + "] in the defined path [" + m_lsbPaths.begin()->first + "]");
|
||||
break;
|
||||
default:
|
||||
DEBUG("Unable to find [" + sFile + "] in any of the " + CString(m_lsPaths.size()) + " defined paths");
|
||||
DEBUG("Unable to find [" + sFile + "] in any of the " + CString(m_lsbPaths.size()) + " defined paths");
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -138,31 +146,48 @@ void CTemplate::SetPath(const CString& sPaths) {
|
||||
sPaths.Split(":", vsDirs, false);
|
||||
|
||||
for (size_t a = 0; a < vsDirs.size(); a++) {
|
||||
AppendPath(vsDirs[a]);
|
||||
AppendPath(vsDirs[a], false);
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplate::PrependPath(const CString& sPath) {
|
||||
DEBUG("CTemplate::PrependPath(" + sPath + ") == [" + CDir::ChangeDir("./", sPath + "/") + "]");
|
||||
m_lsPaths.push_front(CDir::ChangeDir("./", sPath + "/"));
|
||||
CString CTemplate::MakePath(const CString& sPath) const {
|
||||
CString sRet(CDir::ChangeDir("./", sPath + "/"));
|
||||
|
||||
if (!sRet.empty() && sRet.Right(1) != "/") {
|
||||
sRet += "/";
|
||||
}
|
||||
|
||||
return sRet;
|
||||
}
|
||||
|
||||
void CTemplate::AppendPath(const CString& sPath) {
|
||||
DEBUG("CTemplate::AppendPath(" + sPath + ") == [" + CDir::ChangeDir("./", sPath + "/") + "]");
|
||||
m_lsPaths.push_back(CDir::ChangeDir("./", sPath + "/"));
|
||||
void CTemplate::PrependPath(const CString& sPath, bool bIncludesOnly) {
|
||||
DEBUG("CTemplate::PrependPath(" + sPath + ") == [" + MakePath(sPath) + "]");
|
||||
m_lsbPaths.push_front(make_pair(MakePath(sPath), bIncludesOnly));
|
||||
}
|
||||
|
||||
void CTemplate::AppendPath(const CString& sPath, bool bIncludesOnly) {
|
||||
DEBUG("CTemplate::AppendPath(" + sPath + ") == [" + MakePath(sPath) + "]");
|
||||
m_lsbPaths.push_back(make_pair(MakePath(sPath), bIncludesOnly));
|
||||
}
|
||||
|
||||
void CTemplate::RemovePath(const CString& sPath) {
|
||||
DEBUG("CTemplate::RemovePath(" + sPath + ") == [" + CDir::ChangeDir("./", sPath + "/") + "]");
|
||||
m_lsPaths.remove(CDir::ChangeDir("./", sPath + "/"));
|
||||
|
||||
for (list<pair<CString, bool> >::iterator it = m_lsbPaths.begin(); it != m_lsbPaths.end(); ++it) {
|
||||
if (it->first == sPath) {
|
||||
m_lsbPaths.remove(*it);
|
||||
RemovePath(sPath); // @todo probably shouldn't use recursion, being lazy
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplate::ClearPath() {
|
||||
m_lsPaths.clear();
|
||||
void CTemplate::ClearPaths() {
|
||||
m_lsbPaths.clear();
|
||||
}
|
||||
|
||||
bool CTemplate::SetFile(const CString& sFileName) {
|
||||
m_sFileName = ExpandFile(sFileName);
|
||||
m_sFileName = ExpandFile(sFileName, false);
|
||||
PrependPath(sFileName + "/..");
|
||||
|
||||
if (sFileName.empty()) {
|
||||
@@ -230,6 +255,7 @@ bool CTemplate::PrintString(CString& sRet) {
|
||||
}
|
||||
|
||||
bool CTemplate::Print(ostream& oOut) {
|
||||
DEBUG("== Print(o) m_sFileName = [" + m_sFileName + "]");
|
||||
return Print(m_sFileName, oOut);
|
||||
}
|
||||
|
||||
@@ -311,7 +337,8 @@ bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
|
||||
|
||||
if (!uSkip) {
|
||||
if (sAction.Equals("INC")) {
|
||||
if (!Print(ExpandFile(sArgs), oOut)) {
|
||||
if (!Print(ExpandFile(sArgs, true), oOut)) {
|
||||
DEBUG("Unable to print INC'd file [" + sArgs + "]");
|
||||
return false;
|
||||
}
|
||||
} else if (sAction.Equals("SETOPTION")) {
|
||||
@@ -365,7 +392,7 @@ bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
|
||||
sSetBlockVar = sArgs;
|
||||
bInSetBlock = true;
|
||||
} else if (sAction.Equals("EXPAND")) {
|
||||
sOutput += ExpandFile(sArgs);
|
||||
sOutput += ExpandFile(sArgs, true);
|
||||
} else if (sAction.Equals("VAR")) {
|
||||
sOutput += GetValue(sArgs);
|
||||
} else if (sAction.Equals("LT")) {
|
||||
@@ -643,8 +670,20 @@ bool CTemplate::ValidExpr(const CString& sExpression) {
|
||||
}
|
||||
|
||||
if (sExpr.find("!=") != CString::npos) {
|
||||
// GOOD:
|
||||
// >>>>>>>>>>>>>>>>>>> [PageName] [index] -> [PageName != "index"]
|
||||
// >>>>>>>>>>>>>>>>>>> [PageName] [help] -> [PageName != "help"]
|
||||
//
|
||||
// BAD:
|
||||
// >>>>>>>>>>>>>>>>>>> [PageName] ["index"] -> [PageName != "index"]
|
||||
// >>>>>>>>>>>>>>>>>>> [PageName] ["help"] -> [PageName != "help"]
|
||||
//
|
||||
|
||||
//CString CTemplate::Token(const CString& sStr, unsigned int uPos, bool bRest, const CString& sSep, bool bAllowEmpty,
|
||||
// const CString& sLeft, const CString& sRight, bool bTrimQuotes) {
|
||||
sName = sExpr.Token(0, false, "!=").Trim_n();
|
||||
sValue = sExpr.Token(1, true, "!=", false, "\"", "\"", true).Trim_n();
|
||||
DEBUG(">>>>>>>>>>>>>>>>>>> [" + sName + "] [" + sValue + "] -> [" + sExpr + "]");
|
||||
bNegate = !bNegate;
|
||||
} else if (sExpr.find("==") != CString::npos) {
|
||||
sName = sExpr.Token(0, false, "==").Trim_n();
|
||||
|
||||
+6
-5
@@ -145,14 +145,15 @@ public:
|
||||
void Init();
|
||||
|
||||
CTemplate* GetParent(bool bRoot);
|
||||
CString ExpandFile(const CString& sFilename);
|
||||
CString ExpandFile(const CString& sFilename, bool bFromInc = false);
|
||||
bool SetFile(const CString& sFileName);
|
||||
|
||||
void SetPath(const CString& sPath); // Sets the dir:dir:dir type path to look at for templates, as of right now no ../../.. protection
|
||||
void PrependPath(const CString& sPath);
|
||||
void AppendPath(const CString& sPath);
|
||||
CString MakePath(const CString& sPath) const;
|
||||
void PrependPath(const CString& sPath, bool bIncludesOnly = false);
|
||||
void AppendPath(const CString& sPath, bool bIncludesOnly = false);
|
||||
void RemovePath(const CString& sPath);
|
||||
void ClearPath();
|
||||
void ClearPaths();
|
||||
CString ResolvePath(const CString& sPath, const CString& sFilename);
|
||||
bool PrintString(CString& sRet);
|
||||
bool Print(ostream& oOut = cout);
|
||||
@@ -175,7 +176,7 @@ public:
|
||||
private:
|
||||
CTemplate* m_pParent;
|
||||
CString m_sFileName;
|
||||
LCString m_lsPaths;
|
||||
list<pair<CString, bool> > m_lsbPaths;
|
||||
map<CString, vector<CTemplate*> > m_mvLoops;
|
||||
vector<CTemplateLoopContext*> m_vLoopContexts;
|
||||
CSmartPtr<CTemplateOptions> m_spOptions;
|
||||
|
||||
@@ -358,6 +358,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneChans) {
|
||||
SetVHost(User.GetVHost());
|
||||
SetDCCVHost(User.GetDCCVHost());
|
||||
SetQuitMsg(User.GetQuitMsg());
|
||||
SetSkinName(User.GetSkinName());
|
||||
SetDefaultChanModes(User.GetDefaultChanModes());
|
||||
SetBufferCount(User.GetBufferCount());
|
||||
SetJoinTries(User.JoinTries());
|
||||
@@ -645,6 +646,7 @@ bool CUser::WriteConfig(CFile& File) {
|
||||
PrintLine(File, "QuitMsg", GetQuitMsg());
|
||||
if (CZNC::Get().GetStatusPrefix() != GetStatusPrefix())
|
||||
PrintLine(File, "StatusPrefix", GetStatusPrefix());
|
||||
PrintLine(File, "Skin", GetSkinName());
|
||||
PrintLine(File, "ChanModes", GetDefaultChanModes());
|
||||
PrintLine(File, "Buffer", CString(GetBufferCount()));
|
||||
PrintLine(File, "KeepBuffer", CString(KeepBuffer()));
|
||||
@@ -1247,4 +1249,6 @@ CString CUser::GetQuitMsg() const { return (!m_sQuitMsg.Trim_n().empty()) ? m_sQ
|
||||
const MCString& CUser::GetCTCPReplies() const { return m_mssCTCPReplies; }
|
||||
unsigned int CUser::GetBufferCount() const { return m_uBufferCount; }
|
||||
bool CUser::KeepBuffer() const { return m_bKeepBuffer; }
|
||||
//CString CUser::GetSkinName() const { return (!m_sSkinName.empty()) ? m_sSkinName : CZNC::Get().GetSkinName(); }
|
||||
CString CUser::GetSkinName() const { return m_sSkinName; }
|
||||
// !Getters
|
||||
|
||||
@@ -138,6 +138,29 @@ public:
|
||||
void AddBytesRead(unsigned long long u) { m_uBytesRead += u; }
|
||||
void AddBytesWritten(unsigned long long u) { m_uBytesWritten += u; }
|
||||
|
||||
// Counter for logging out of the web interface
|
||||
unsigned int GetWebLogoutCounter(const CString& sToken) {
|
||||
map<CString, unsigned int>::iterator it = m_suWebLogoutCounters.find(sToken);
|
||||
|
||||
if (it == m_suWebLogoutCounters.end()) {
|
||||
m_suWebLogoutCounters[sToken] = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
unsigned int IncWebLogoutCounter(const CString& sToken) {
|
||||
map<CString, unsigned int>::iterator it = m_suWebLogoutCounters.find(sToken);
|
||||
|
||||
if (it == m_suWebLogoutCounters.end()) {
|
||||
m_suWebLogoutCounters[sToken] = 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
return ++it->second;
|
||||
}
|
||||
|
||||
// Setters
|
||||
void SetUserName(const CString& s);
|
||||
void SetNick(const CString& s);
|
||||
@@ -169,6 +192,7 @@ public:
|
||||
void SetTimezoneOffset(float b) { m_fTimezoneOffset = b; }
|
||||
void SetJoinTries(unsigned int i) { m_uMaxJoinTries = i; }
|
||||
void SetMaxJoins(unsigned int i) { m_uMaxJoins = i; }
|
||||
void SetSkinName(const CString& s) { m_sSkinName = s; }
|
||||
void SetIRCConnectEnabled(bool b) { m_bIRCConnectEnabled = b; }
|
||||
void SetIRCAway(bool b) { m_bIRCAway = b; }
|
||||
// !Setters
|
||||
@@ -223,6 +247,7 @@ public:
|
||||
unsigned long long BytesWritten() const { return m_uBytesWritten; }
|
||||
unsigned int JoinTries() const { return m_uMaxJoinTries; }
|
||||
unsigned int MaxJoins() const { return m_uMaxJoins; }
|
||||
CString GetSkinName() const;
|
||||
// !Getters
|
||||
private:
|
||||
protected:
|
||||
@@ -284,6 +309,9 @@ protected:
|
||||
unsigned long long m_uBytesWritten;
|
||||
unsigned int m_uMaxJoinTries;
|
||||
unsigned int m_uMaxJoins;
|
||||
CString m_sSkinName;
|
||||
|
||||
map<CString, unsigned int> m_suWebLogoutCounters;
|
||||
|
||||
#ifdef _MODULES
|
||||
CModules* m_pModules;
|
||||
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
#include "WebModules.h"
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
CZNCTagHandler::CZNCTagHandler(CWebSock& WebSock) : CTemplateTagHandler(), m_WebSock(WebSock) {
|
||||
}
|
||||
|
||||
bool CZNCTagHandler::HandleTag(CTemplate& Tmpl, const CString& sName, const CString& sArgs, CString& sOutput) {
|
||||
if (sName.Equals("URLPARAM")) {
|
||||
//sOutput = CZNC::Get()
|
||||
std::cerr << "========================= URLPARAM !!!!!!!!!!" << std::endl;
|
||||
sOutput = m_WebSock.GetParam(sArgs.Token(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CWebAuth::CWebAuth(CWebSock* pWebSock, const CString& sUsername, const CString& sPassword)
|
||||
: CAuthBase(sUsername, sPassword, pWebSock) {
|
||||
m_pWebSock = pWebSock;
|
||||
}
|
||||
|
||||
void CWebAuth::AcceptedLogin(CUser& User) {
|
||||
if (m_pWebSock) {
|
||||
m_pWebSock->SetSessionUser(&User);
|
||||
m_pWebSock->SetLoggedIn(true);
|
||||
m_pWebSock->UnPauseRead();
|
||||
}
|
||||
}
|
||||
|
||||
void CWebAuth::RefusedLogin(const CString& sReason) {
|
||||
if (m_pWebSock) {
|
||||
m_pWebSock->SetSessionUser(NULL);
|
||||
m_pWebSock->SetLoggedIn(false);
|
||||
m_pWebSock->UnPauseRead();
|
||||
}
|
||||
}
|
||||
|
||||
CWebSock::CWebSock(CModule* pModule) : CHTTPSock(pModule) {
|
||||
m_pModule = pModule;
|
||||
m_pSessionUser = NULL;
|
||||
m_bPathsSet = false;
|
||||
|
||||
m_Template.AddTagHandler(new CZNCTagHandler(*this));
|
||||
}
|
||||
|
||||
CWebSock::CWebSock(CModule* pModule, const CString& sHostname, unsigned short uPort, int iTimeout)
|
||||
: CHTTPSock(pModule, sHostname, uPort, iTimeout) {
|
||||
m_pModule = pModule;
|
||||
m_pSessionUser = NULL;
|
||||
m_bPathsSet = false;
|
||||
|
||||
m_Template.AddTagHandler(new CZNCTagHandler(*this));
|
||||
}
|
||||
|
||||
CWebSock::~CWebSock() {
|
||||
if (!m_spAuth.IsNull()) {
|
||||
CWebAuth* pAuth = (CWebAuth*) &(*m_spAuth);
|
||||
pAuth->SetWebSock(NULL);
|
||||
}
|
||||
|
||||
// If the module IsFake() then it was created as a dummy and needs to be deleted
|
||||
CModule* pMod = GetModule();
|
||||
if (pMod && pMod->IsFake()) {
|
||||
pMod->UnlinkSocket(this);
|
||||
delete pMod;
|
||||
}
|
||||
}
|
||||
|
||||
void CWebSock::ParsePath() {
|
||||
// The URI looks like:
|
||||
// /[user:][module][/page][?arg1=val1&arg2=val2...]
|
||||
|
||||
m_sForceUser.clear();
|
||||
|
||||
m_sPath = GetPath().TrimLeft_n("/");
|
||||
|
||||
m_sPath.TrimPrefix("mods/");
|
||||
m_sPath.TrimPrefix("modfiles/");
|
||||
|
||||
m_sModName = m_sPath.Token(0, false, "/");
|
||||
m_sPage = m_sPath.Token(1, true, "/");
|
||||
|
||||
if (m_sModName.find(":") != CString::npos) {
|
||||
m_sForceUser = m_sModName.Token(0, false, ":");
|
||||
m_sModName = m_sModName.Token(1, false, ":");
|
||||
}
|
||||
|
||||
if (m_sPage.empty()) {
|
||||
m_sPage = "index";
|
||||
}
|
||||
|
||||
DEBUG("Path [" + m_sPath + "]");
|
||||
DEBUG("User [" + m_sForceUser + "]");
|
||||
DEBUG("Module [" + m_sModName + "]");
|
||||
DEBUG("Page [" + m_sPage + "]");
|
||||
}
|
||||
|
||||
CModule* CWebSock::ResolveModule() {
|
||||
ParsePath();
|
||||
CModule* pModRet = NULL;
|
||||
|
||||
// Dot means static file, not module
|
||||
if (m_sModName.find(".") != CString::npos) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// First look for forced user-mods
|
||||
if (!m_sForceUser.empty()) {
|
||||
CUser* pUser = CZNC::Get().FindUser(m_sForceUser);
|
||||
|
||||
if (pUser) {
|
||||
pModRet = pUser->GetModules().FindModule(m_sModName);
|
||||
} else {
|
||||
DEBUG("User not found while trying to handle web requrest for [" + m_sPage + "]");
|
||||
}
|
||||
} else {
|
||||
// This could be user level or global level, check both
|
||||
pModRet = CZNC::Get().GetModules().FindModule(m_sModName);
|
||||
|
||||
if (!pModRet) {
|
||||
// It's not a loaded global module and it has no forced username so we
|
||||
// have to force a login to try a module loaded by the current user
|
||||
if (!ForceLogin()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pModRet = m_pSessionUser->GetModules().FindModule(m_sModName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pModRet) {
|
||||
DEBUG("Module not found");
|
||||
} else if (pModRet->IsFake()) {
|
||||
DEBUG("Fake module found, ignoring");
|
||||
pModRet = NULL;
|
||||
}
|
||||
|
||||
return pModRet;
|
||||
}
|
||||
|
||||
size_t CWebSock::GetAvailSkins(vector<CFile>& vRet) {
|
||||
vRet.clear();
|
||||
|
||||
CString sRoot(GetSkinPath("_default_"));
|
||||
|
||||
sRoot.TrimRight("/");
|
||||
sRoot.TrimRight("_default_");
|
||||
sRoot.TrimRight("/");
|
||||
|
||||
if (!sRoot.empty()) {
|
||||
sRoot += "/";
|
||||
}
|
||||
|
||||
if (!sRoot.empty() && CFile::IsDir(sRoot)) {
|
||||
CDir Dir(sRoot);
|
||||
|
||||
for (unsigned int d = 0; d < Dir.size(); d++) {
|
||||
const CFile& SubDir = *Dir[d];
|
||||
|
||||
if (SubDir.IsDir() && SubDir.GetShortName() == "_default_") {
|
||||
vRet.push_back(SubDir);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int e = 0; e < Dir.size(); e++) {
|
||||
const CFile& SubDir = *Dir[e];
|
||||
|
||||
if (SubDir.IsDir() && SubDir.GetShortName() != "_default_" && SubDir.GetShortName() != ".svn") {
|
||||
vRet.push_back(SubDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vRet.size();
|
||||
}
|
||||
|
||||
void CWebSock::SetPaths(CModule* pModule, bool bIsTemplate) {
|
||||
m_Template.ClearPaths();
|
||||
|
||||
CString sHomeSkinsDir(CZNC::Get().GetZNCPath() + "/webskins/");
|
||||
CString sSkinName(GetSkinName());
|
||||
|
||||
DEBUG("---- sHomeSkinsDir=[" + sHomeSkinsDir + "] sSkinName=[" + sSkinName + "]");
|
||||
|
||||
// Module specific paths
|
||||
|
||||
if (pModule) {
|
||||
const CString& sModName(pModule->GetModName());
|
||||
|
||||
// 1. ~/.znc/webskins/<user_skin_setting>/mods/<mod_name>/
|
||||
//
|
||||
if (!sSkinName.empty()) {
|
||||
m_Template.AppendPath(GetSkinPath(sSkinName) + "/mods/" + sModName + "/");
|
||||
}
|
||||
|
||||
// 2. ~/.znc/webskins/_default_/mods/<mod_name>/
|
||||
//
|
||||
m_Template.AppendPath(GetSkinPath("_default_") + "/mods/" + sModName + "/");
|
||||
|
||||
// 3. ./modules/<mod_name>/
|
||||
//
|
||||
m_Template.AppendPath(GetModWebPath(sModName));
|
||||
|
||||
// 4. ~/.znc/webskins/<user_skin_setting>/mods/<mod_name>/
|
||||
//
|
||||
if (!sSkinName.empty()) {
|
||||
m_Template.AppendPath(GetSkinPath(sSkinName) + "/mods/" + sModName + "/");
|
||||
}
|
||||
|
||||
// 5. ~/.znc/webskins/_default_/mods/<mod_name>/
|
||||
//
|
||||
m_Template.AppendPath(GetSkinPath("_default_") + "/mods/" + sModName + "/");
|
||||
}
|
||||
|
||||
// 6. ~/.znc/webskins/<user_skin_setting>/
|
||||
//
|
||||
if (!sSkinName.empty()) {
|
||||
m_Template.AppendPath(GetSkinPath(sSkinName) + CString(bIsTemplate ? "/tmpl/" : "/"), (0 && pModule != NULL));
|
||||
}
|
||||
|
||||
// 7. ~/.znc/webskins/_default_/
|
||||
//
|
||||
m_Template.AppendPath(GetSkinPath("_default_") + CString(bIsTemplate ? "/tmpl/" : "/"), (0 && pModule != NULL));
|
||||
|
||||
m_bPathsSet = true;
|
||||
}
|
||||
|
||||
void CWebSock::SetVars() {
|
||||
m_Template["SessionUser"] = GetUser();
|
||||
m_Template["SessionIP"] = GetRemoteIP();
|
||||
m_Template["Tag"] = CZNC::GetTag();
|
||||
m_Template["SkinName"] = GetSkinName();
|
||||
|
||||
if (IsAdmin()) {
|
||||
m_Template["IsAdmin"] = "true";
|
||||
}
|
||||
|
||||
// Global Mods
|
||||
CGlobalModules& vgMods = CZNC::Get().GetModules();
|
||||
for (unsigned int a = 0; a < vgMods.size(); a++) {
|
||||
AddModLoop("GlobalModLoop", *vgMods[a]);
|
||||
}
|
||||
|
||||
// User Mods
|
||||
if (IsLoggedIn()) {
|
||||
CModules& vMods = m_pSessionUser->GetModules();
|
||||
|
||||
for (unsigned int a = 0; a < vMods.size(); a++) {
|
||||
AddModLoop("UserModLoop", *vMods[a]);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsLoggedIn()) {
|
||||
m_Template["LoggedIn"] = "true";
|
||||
}
|
||||
}
|
||||
|
||||
bool CWebSock::AddModLoop(const CString& sLoopName, CModule& Module) {
|
||||
CString sTitle(Module.GetWebNavTitle());
|
||||
|
||||
DEBUG("=== === === === === [" + Module.GetModName() + "] [" + CString(IsLoggedIn()) + "]");
|
||||
|
||||
if (!sTitle.empty() && (IsLoggedIn() || (!Module.WebRequiresLogin() && !Module.WebRequiresAdmin())) && (IsAdmin() || !Module.WebRequiresAdmin())) {
|
||||
CTemplate& Row = m_Template.AddRow(sLoopName);
|
||||
|
||||
Row["ModName"] = Module.GetModName();
|
||||
Row["Title"] = sTitle;
|
||||
|
||||
if (m_sModName == Module.GetModName()) {
|
||||
Row["Active"] = "true";
|
||||
}
|
||||
|
||||
if (Module.GetUser()) {
|
||||
Row["Username"] = Module.GetUser()->GetUserName();
|
||||
}
|
||||
|
||||
VWebSubPages& vSubPages = Module.GetSubPages();
|
||||
|
||||
for (unsigned int a = 0; a < vSubPages.size(); a++) {
|
||||
CTemplate& SubRow = Row.AddRow("SubPageLoop");
|
||||
TWebSubPage& SubPage = vSubPages[a];
|
||||
|
||||
SubRow["ModName"] = Module.GetModName();
|
||||
SubRow["PageName"] = SubPage->GetName();
|
||||
SubRow["Title"] = SubPage->GetTitle().empty() ? SubPage->GetName() : SubPage->GetTitle();
|
||||
|
||||
CString& sParams = SubRow["Params"];
|
||||
|
||||
// bActive is whether or not the current url matches this subpage (including the params below)
|
||||
bool bActive = (m_sModName == Module.GetModName() && m_sPage == SubPage->GetName());
|
||||
|
||||
const VPair& vParams = SubPage->GetParams();
|
||||
for (size_t b = 0; b < vParams.size(); b++) {
|
||||
pair<CString, CString> ssNV = vParams[b];
|
||||
|
||||
if (!sParams.empty()) {
|
||||
sParams += "&";
|
||||
}
|
||||
|
||||
if (!ssNV.first.empty()) {
|
||||
if (!ssNV.second.empty()) {
|
||||
sParams += ssNV.first.Escape_n(CString::EURL);
|
||||
sParams += "=";
|
||||
sParams += ssNV.second.Escape_n(CString::EURL);
|
||||
}
|
||||
|
||||
if (bActive && GetParam(ssNV.first) != ssNV.second) {
|
||||
bActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bActive) {
|
||||
SubRow["Active"] = "true";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWebSock::PrintStaticFile(const CString& sPath, CString& sPageRet, CModule* pModule) {
|
||||
SetPaths(pModule);
|
||||
DEBUG("About to print [" + m_Template.ExpandFile(sPath) + "]");
|
||||
return PrintFile(m_Template.ExpandFile(sPath.TrimLeft_n("/")));
|
||||
}
|
||||
|
||||
bool CWebSock::PrintTemplate(const CString& sPageName, CString& sPageRet, CModule* pModule) {
|
||||
SetVars();
|
||||
m_Template["PageName"] = sPageName;
|
||||
|
||||
if (pModule) {
|
||||
CUser* pUser = pModule->GetUser();
|
||||
m_Template["ModUser"] = pUser ? pUser->GetUserName() : "";
|
||||
m_Template["ModName"] = pModule->GetModName();
|
||||
|
||||
if (m_Template.find("Title") == m_Template.end()) {
|
||||
m_Template["Title"] = pModule->GetWebNavTitle();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bPathsSet) {
|
||||
SetPaths(pModule, true);
|
||||
}
|
||||
|
||||
if (m_Template.GetFileName().empty() && !m_Template.SetFile(sPageName + ".tmpl")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_Template.PrintString(sPageRet);
|
||||
}
|
||||
|
||||
CString CWebSock::GetModWebPath(const CString& sModName) const {
|
||||
CString sRet = CZNC::Get().GetCurPath() + "/modules/www/" + sModName;
|
||||
|
||||
if (!CFile::IsDir(sRet)) {
|
||||
sRet = CString(_MODDIR_) + "/www/" + sModName;
|
||||
}
|
||||
|
||||
return sRet + "/";
|
||||
}
|
||||
|
||||
CString CWebSock::GetSkinPath(const CString& sSkinName) const {
|
||||
CString sRet = CZNC::Get().GetZNCPath() + "/webskins/" + sSkinName;
|
||||
|
||||
if (!CFile::IsDir(sRet)) {
|
||||
sRet = CZNC::Get().GetCurPath() + "/webskins/" + sSkinName;
|
||||
|
||||
if (!CFile::IsDir(sRet)) {
|
||||
sRet = CString(_SKINDIR_) + "/" + sSkinName;
|
||||
}
|
||||
}
|
||||
|
||||
return sRet + "/";
|
||||
}
|
||||
|
||||
bool CWebSock::OnPageRequest(const CString& sURI, CString& sPageRet) {
|
||||
DEBUG("CWebSock::OnPageRequest(" + sURI + ")");
|
||||
|
||||
// Handle the static pages that don't require a login
|
||||
if (sURI == "/") {
|
||||
return PrintTemplate("index", sPageRet);
|
||||
} else if (sURI == "/favicon.ico") {
|
||||
return PrintStaticFile("/pub/favicon.ico", sPageRet);
|
||||
} else if (sURI == "/logout") {
|
||||
if (!IsLoggedIn()) {
|
||||
Redirect("/");
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int uCurCnt = GetParam("cnt").ToUInt();
|
||||
unsigned int uCounter = m_pSessionUser->GetWebLogoutCounter(GetRemoteIP());
|
||||
|
||||
if (!uCurCnt) {
|
||||
Redirect("/logout?cnt=" + CString(uCounter));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uCurCnt >= uCounter) {
|
||||
m_bLoggedIn = false;
|
||||
m_pSessionUser->IncWebLogoutCounter(GetRemoteIP());
|
||||
ForceLogin();
|
||||
} else {
|
||||
Redirect("/");
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (sURI.Left(6) == "/login") {
|
||||
if (ForceLogin()) {
|
||||
Redirect("/");
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (sURI.Left(5) == "/pub/") {
|
||||
return PrintStaticFile(sURI, sPageRet);
|
||||
} else if (sURI.Left(6) == "/mods/" || sURI.Left(10) == "/modfiles/") {
|
||||
ParsePath();
|
||||
// Make sure modules are treated as directories
|
||||
if (sURI.Right(1) != "/" && sURI.find(".") == CString::npos && sURI.TrimLeft_n("/mods/").TrimLeft_n("/").find("/") == CString::npos) {
|
||||
Redirect(sURI + "/");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_sModName.empty()) {
|
||||
return PrintTemplate("modlist", sPageRet);
|
||||
}
|
||||
|
||||
DEBUG("FindModule(" + m_sModName + ", " + m_sForceUser + ")");
|
||||
CModule* pModule = CZNC::Get().FindModule(m_sModName, m_sForceUser);
|
||||
|
||||
if (!pModule && m_sForceUser.empty()) {
|
||||
if (!ForceLogin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pModule = CZNC::Get().FindModule(m_sModName, m_pSessionUser);
|
||||
}
|
||||
|
||||
if (!pModule) {
|
||||
return false;
|
||||
} else if (pModule->WebRequiresLogin() && !ForceLogin()) {
|
||||
return true;
|
||||
} else if (pModule->WebRequiresAdmin() && !IsAdmin()) {
|
||||
sPageRet = GetErrorPage(403, "Forbidden", "You need to be an admin to access this module");
|
||||
return true;
|
||||
} else if (pModule && !pModule->IsGlobal() && pModule->GetUser() != m_pSessionUser) {
|
||||
sPageRet = GetErrorPage(403, "Forbidden", "You must login as " + pModule->GetUser()->GetUserName() + " in order to view this page");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pModule && !pModule->IsGlobal() && (!IsLoggedIn() || pModule->GetUser() != GetSessionUser())) {
|
||||
AddModLoop("UserModLoop", *pModule);
|
||||
}
|
||||
|
||||
if (sURI.Left(10) == "/modfiles/") {
|
||||
m_Template.AppendPath(GetSkinPath(GetSkinName()) + "/mods/" + m_sModName + "/files/");
|
||||
m_Template.AppendPath(GetModWebPath(m_sModName) + "/files/");
|
||||
|
||||
std::cerr << "===================== fffffffffffffffffffff [" << m_sModName << "] [" << m_sPage << "]" << std::endl;
|
||||
return PrintFile(m_Template.ExpandFile(m_sPage.TrimLeft_n("/")));
|
||||
} else {
|
||||
SetPaths(pModule, true);
|
||||
|
||||
if (pModule->OnWebRequest(*this, m_sPage, m_Template)) {
|
||||
return PrintTemplate(m_sPage, sPageRet, pModule);
|
||||
}
|
||||
|
||||
sPageRet = GetErrorPage(404, "Not Implemented", "The requested module does not acknowledge web requests");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
CString sPage(sURI.Trim_n("/"));
|
||||
if (sPage.length() < 32) {
|
||||
for (unsigned int a = 0; a < sPage.length(); a++) {
|
||||
unsigned char c = sPage[a];
|
||||
|
||||
if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return PrintTemplate(sPage, sPageRet);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CWebSock::PrintErrorPage(const CString& sMessage) {
|
||||
m_Template.SetFile("Error.tmpl");
|
||||
|
||||
m_Template["Action"] = "error";
|
||||
m_Template["Title"] = "Error";
|
||||
m_Template["Error"] = sMessage;
|
||||
}
|
||||
|
||||
size_t CWebSock::AddError(const CString& sMessage) {
|
||||
CTemplate& Row = m_Template.AddRow("ErrorLoop");
|
||||
|
||||
Row["Message"] = sMessage;
|
||||
return m_Template.GetLoop("ErrorLoop")->size();
|
||||
}
|
||||
|
||||
size_t CWebSock::AddSuccess(const CString& sMessage) {
|
||||
CTemplate& Row = m_Template.AddRow("SuccessLoop");
|
||||
|
||||
Row["Message"] = sMessage;
|
||||
return m_Template.GetLoop("SuccessLoop")->size();
|
||||
}
|
||||
|
||||
bool CWebSock::OnLogin(const CString& sUser, const CString& sPass) {
|
||||
DEBUG("=================== CWebSock::OnLogin()");
|
||||
m_spAuth = new CWebAuth(this, sUser, sPass);
|
||||
|
||||
// Some authentication module could need some time, block this socket
|
||||
// until then. CWebAuth will UnPauseRead().
|
||||
PauseRead();
|
||||
CZNC::Get().AuthUser(m_spAuth);
|
||||
|
||||
// If CWebAuth already set this, don't change it.
|
||||
return IsLoggedIn();
|
||||
}
|
||||
|
||||
Csock* CWebSock::GetSockObj(const CString& sHost, unsigned short uPort) {
|
||||
CWebSock* pSock = new CWebSock(GetModule(), sHost, uPort);
|
||||
pSock->SetSockName("Web::Client");
|
||||
pSock->SetTimeout(120);
|
||||
|
||||
return pSock;
|
||||
}
|
||||
|
||||
bool CWebSock::IsAdmin() const { return m_pSessionUser && m_pSessionUser->IsAdmin(); }
|
||||
CUser* CWebSock::GetSessionUser() const { return m_pSessionUser; }
|
||||
CString CWebSock::GetSkinName() const {
|
||||
if (m_pSessionUser && IsLoggedIn() && !m_pSessionUser->GetSkinName().empty()) {
|
||||
return m_pSessionUser->GetSkinName();
|
||||
}
|
||||
|
||||
return CZNC::Get().GetSkinName();
|
||||
}
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 See the AUTHORS file for details.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _WEBMODULES_H
|
||||
#define _WEBMODULES_H
|
||||
|
||||
#include "Client.h"
|
||||
#include "Template.h"
|
||||
#include "HTTPSock.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
class CWebSock;
|
||||
class CModule;
|
||||
class CWebSubPage;
|
||||
|
||||
typedef CSmartPtr<CWebSubPage> TWebSubPage;
|
||||
typedef vector<TWebSubPage> VWebSubPages;
|
||||
|
||||
class CZNCTagHandler : public CTemplateTagHandler {
|
||||
public:
|
||||
CZNCTagHandler(CWebSock& pWebSock);
|
||||
virtual ~CZNCTagHandler() {}
|
||||
|
||||
virtual bool HandleTag(CTemplate& Tmpl, const CString& sName, const CString& sArgs, CString& sOutput);
|
||||
private:
|
||||
CWebSock& m_WebSock;
|
||||
};
|
||||
|
||||
|
||||
class CWebSubPage {
|
||||
public:
|
||||
CWebSubPage(const CString& sName, const CString& sTitle = "") : m_sName(sName), m_sTitle(sTitle) {
|
||||
}
|
||||
|
||||
CWebSubPage(const CString& sName, const CString& sTitle, const VPair& vParams) : m_sName(sName), m_sTitle(sTitle), m_vParams(vParams) {}
|
||||
virtual ~CWebSubPage() {}
|
||||
|
||||
void SetName(const CString& s) { m_sName = s; }
|
||||
void SetTitle(const CString& s) { m_sTitle = s; }
|
||||
void AddParam(const CString& sName, const CString& sValue) { m_vParams.push_back(make_pair(sName, sValue)); }
|
||||
|
||||
const CString& GetName() const { return m_sName; }
|
||||
const CString& GetTitle() const { return m_sTitle; }
|
||||
const VPair& GetParams() const { return m_vParams; }
|
||||
|
||||
private:
|
||||
CString m_sName;
|
||||
CString m_sTitle;
|
||||
VPair m_vParams;
|
||||
};
|
||||
|
||||
class CWebAuth : public CAuthBase {
|
||||
public:
|
||||
CWebAuth(CWebSock* pWebSock, const CString& sUsername, const CString& sPassword);
|
||||
virtual ~CWebAuth() {}
|
||||
|
||||
void SetWebSock(CWebSock* pWebSock) { m_pWebSock = pWebSock; }
|
||||
void AcceptedLogin(CUser& User);
|
||||
void RefusedLogin(const CString& sReason);
|
||||
private:
|
||||
protected:
|
||||
CWebSock* m_pWebSock;
|
||||
};
|
||||
|
||||
|
||||
class CWebSock : public CHTTPSock {
|
||||
public:
|
||||
CWebSock(CModule* pModule);
|
||||
CWebSock(CModule* pModule, const CString& sHostname, unsigned short uPort, int iTimeout = 60);
|
||||
virtual ~CWebSock();
|
||||
|
||||
virtual bool OnPageRequest(const CString& sURI, CString& sPageRet);
|
||||
|
||||
virtual bool OnLogin(const CString& sUser, const CString& sPass);
|
||||
|
||||
void ParsePath(); // This parses the path portion of the url into some member vars
|
||||
CModule* ResolveModule();
|
||||
|
||||
//virtual bool PrintFile(const CString& sFileName, CString sContentType = "");
|
||||
bool PrintTemplate(const CString& sPageName, CString& sPageRet, CModule* pModule = NULL);
|
||||
bool PrintStaticFile(const CString& sPath, CString& sPageRet, CModule* pModule = NULL);
|
||||
|
||||
bool AddModLoop(const CString& sLoopName, CModule& Module);
|
||||
void SetPaths(CModule* pModule, bool bIsTemplate = false);
|
||||
void SetVars();
|
||||
|
||||
void FillErrorPage(CString& sPageRet, const CString& sError) {
|
||||
m_Template["Action"] = "error";
|
||||
m_Template["Title"] = "Error";
|
||||
m_Template["Error"] = sError;
|
||||
|
||||
PrintTemplate("error", sPageRet);
|
||||
}
|
||||
|
||||
void PrintErrorPage(const CString& sMessage);
|
||||
size_t AddError(const CString& sMessage);
|
||||
size_t AddSuccess(const CString& sMessage);
|
||||
|
||||
// Setters
|
||||
void SetSessionUser(CUser* p) {
|
||||
m_pSessionUser = p;
|
||||
}
|
||||
// !Setters
|
||||
|
||||
virtual Csock* GetSockObj(const CString& sHost, unsigned short uPort);
|
||||
bool IsAdmin() const;
|
||||
CUser* GetSessionUser() const;
|
||||
CString GetModWebPath(const CString& sModName) const;
|
||||
CString GetSkinPath(const CString& sSkinName) const;
|
||||
CModule* GetModule() const { return (CModule*) m_pModule; }
|
||||
size_t GetAvailSkins(vector<CFile>& vRet);
|
||||
CString GetSkinName() const;
|
||||
|
||||
private:
|
||||
CUser* m_pSessionUser;
|
||||
bool m_bPathsSet;
|
||||
CTemplate m_Template;
|
||||
CSmartPtr<CAuthBase> m_spAuth;
|
||||
CString m_sForceUser; // Gets filled by ResolveModule()
|
||||
CString m_sModName; // Gets filled by ResolveModule()
|
||||
CString m_sPath; // Gets filled by ResolveModule()
|
||||
CString m_sPage; // Gets filled by ResolveModule()
|
||||
};
|
||||
|
||||
#endif // !_WEBMODULES_H
|
||||
@@ -24,6 +24,10 @@
|
||||
#define _MODDIR_ "/usr/lib/znc"
|
||||
#endif
|
||||
|
||||
#ifndef _SKINDIR_
|
||||
#define _SKINDIR_ _MODDIR_ "/webskins"
|
||||
#endif
|
||||
|
||||
#ifndef _DATADIR_
|
||||
#define _DATADIR_ "/usr/share/znc"
|
||||
#endif
|
||||
|
||||
@@ -87,6 +87,7 @@ create_install_dir:
|
||||
rm -rf $(DESTDIR)$(MODDIR)/*.so
|
||||
|
||||
install_metadirs: create_install_dir
|
||||
cp -Rp $(srcdir)/www $(DESTDIR)$(MODDIR)
|
||||
for a in $(srcdir)/*; do \
|
||||
d=$$(echo $$a | sed -e "s:$(srcdir)/::g"); \
|
||||
if [ -d $$a ] && [ -f $${d}.so ]; then \
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2010 See the AUTHORS file for details.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "Chan.h"
|
||||
#include "HTTPSock.h"
|
||||
#include "Server.h"
|
||||
#include "Template.h"
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
#include "WebModules.h"
|
||||
#include <sstream>
|
||||
|
||||
using std::stringstream;
|
||||
|
||||
class CNotesMod : public CModule {
|
||||
public:
|
||||
MODCONSTRUCTOR(CNotesMod) {
|
||||
}
|
||||
|
||||
virtual ~CNotesMod() {
|
||||
}
|
||||
|
||||
virtual bool OnLoad(const CString& sArgStr, CString& sMessage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool WebRequiresLogin() { return true; }
|
||||
virtual bool WebRequiresAdmin() { return false; }
|
||||
virtual CString GetWebNavTitle() { return "Notes"; }
|
||||
|
||||
virtual void OnClientLogin() {
|
||||
ListNotes(true);
|
||||
}
|
||||
|
||||
virtual EModRet OnUserRaw(CString& sLine) {
|
||||
if (sLine.Left(1) != "#") {
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
CString sKey;
|
||||
bool bOverwrite = false;
|
||||
|
||||
if (sLine == "#?") {
|
||||
ListNotes(true);
|
||||
} else if (sLine.Left(2) == "#-") {
|
||||
sKey = sLine.Token(0).LeftChomp_n(2);
|
||||
if (DelNote(sKey)) {
|
||||
PutModNotice("Deleted note [" + sKey + "]");
|
||||
} else {
|
||||
PutModNotice("Unable to delete note [" + sKey + "]");
|
||||
}
|
||||
} else if (sLine.Left(2) == "#+") {
|
||||
sKey = sLine.Token(0).LeftChomp_n(2);
|
||||
bOverwrite = true;
|
||||
} else if (sLine.Left(1) == "#") {
|
||||
sKey = sLine.Token(0).LeftChomp_n(1);
|
||||
}
|
||||
|
||||
CString sValue(sLine.Token(1, true));
|
||||
|
||||
if (!sKey.empty()) {
|
||||
if (!bOverwrite && !GetNV(sKey).empty()) {
|
||||
PutModNotice("That note already exists. Use /#+<key> <note> to overwrite.");
|
||||
} else if (AddNote(sKey, sValue)) {
|
||||
if (!bOverwrite) {
|
||||
PutModNotice("Added note [" + sKey + "]");
|
||||
} else {
|
||||
PutModNotice("Set note for [" + sKey + "]");
|
||||
}
|
||||
} else {
|
||||
PutModNotice("Unable to add note [" + sKey + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return HALT;
|
||||
}
|
||||
|
||||
bool DelNote(const CString& sKey) {
|
||||
return DelNV(sKey);
|
||||
}
|
||||
|
||||
bool AddNote(const CString& sKey, const CString& sNote) {
|
||||
if (sKey.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetNV(sKey, sNote);
|
||||
}
|
||||
|
||||
void ListNotes(bool bNotice = false) {
|
||||
CClient* pClient = GetClient();
|
||||
|
||||
if (pClient) {
|
||||
CTable Table;
|
||||
Table.AddColumn("Key");
|
||||
Table.AddColumn("Note");
|
||||
|
||||
for (MCString::iterator it = BeginNV(); it != EndNV(); ++it) {
|
||||
Table.AddRow();
|
||||
Table.SetCell("Key", it->first);
|
||||
Table.SetCell("Note", it->second);
|
||||
}
|
||||
|
||||
if (Table.size()) {
|
||||
unsigned int idx = 0;
|
||||
CString sLine;
|
||||
while (Table.GetLine(idx++, sLine)) {
|
||||
if (bNotice) {
|
||||
pClient->PutModNotice(GetModName(), sLine);
|
||||
} else {
|
||||
pClient->PutModule(GetModName(), sLine);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (bNotice) {
|
||||
PutModNotice("You have no entries.");
|
||||
} else {
|
||||
PutModule("You have no entries.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnModCommand(const CString& sLine) {
|
||||
CString sCmd(sLine.Token(0));
|
||||
|
||||
if (sLine.Equals("LIST")) {
|
||||
ListNotes();
|
||||
} else if (sCmd.Equals("ADD")) {
|
||||
CString sKey(sLine.Token(1));
|
||||
CString sValue(sLine.Token(2, true));
|
||||
|
||||
if (!GetNV(sKey).empty()) {
|
||||
PutModule("That note already exists. Use MOD <key> <note> to overwrite.");
|
||||
} else if (AddNote(sKey, sValue)) {
|
||||
PutModule("Added note [" + sKey + "]");
|
||||
} else {
|
||||
PutModule("Unable to add note [" + sKey + "]");
|
||||
}
|
||||
} else if (sCmd.Equals("MOD")) {
|
||||
CString sKey(sLine.Token(1));
|
||||
CString sValue(sLine.Token(2, true));
|
||||
|
||||
if (AddNote(sKey, sValue)) {
|
||||
PutModule("Set note for [" + sKey + "]");
|
||||
} else {
|
||||
PutModule("Unable to add note [" + sKey + "]");
|
||||
}
|
||||
} else if (sCmd.Equals("DEL")) {
|
||||
CString sKey(sLine.Token(1));
|
||||
|
||||
if (DelNote(sKey)) {
|
||||
PutModule("Deleted note [" + sKey + "]");
|
||||
} else {
|
||||
PutModule("Unable to delete note [" + sKey + "]");
|
||||
}
|
||||
} else {
|
||||
PutModule("Commands are: Help, List, Add <key> <note>, Del <key>, Mod <key> <note>");
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) {
|
||||
if (sPageName.empty() || sPageName == "index") {
|
||||
for (MCString::iterator it = BeginNV(); it != EndNV(); ++it) {
|
||||
CTemplate& Row = Tmpl.AddRow("NotesLoop");
|
||||
|
||||
Row["Key"] = it->first;
|
||||
Row["Note"] = it->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (sPageName == "delnote") {
|
||||
DelNote(WebSock.GetParam("key"));
|
||||
WebSock.Redirect("/mods/notes/");
|
||||
return true;
|
||||
} else if (sPageName == "addnote") {
|
||||
AddNote(WebSock.GetParam("key"), WebSock.GetParam("note"));
|
||||
WebSock.Redirect("/mods/notes/");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
map<CString, unsigned int> m_suSwitchCounters;
|
||||
};
|
||||
|
||||
MODULEDEFS(CNotesMod, "Keep and replay notes")
|
||||
+720
-1075
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2010 See the AUTHORS file for details.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "Chan.h"
|
||||
#include "HTTPSock.h"
|
||||
#include "Server.h"
|
||||
#include "Template.h"
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
#include "WebModules.h"
|
||||
#include <sstream>
|
||||
|
||||
using std::stringstream;
|
||||
|
||||
class CWebChatMod : public CModule {
|
||||
public:
|
||||
MODCONSTRUCTOR(CWebChatMod) {
|
||||
}
|
||||
|
||||
virtual ~CWebChatMod() {
|
||||
}
|
||||
|
||||
virtual bool OnLoad(const CString& sArgStr, CString& sMessage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool WebRequiresLogin() { return true; }
|
||||
virtual bool WebRequiresAdmin() { return false; }
|
||||
virtual CString GetWebNavTitle() { return "webchat"; }
|
||||
|
||||
virtual VWebSubPages& GetSubPages() {
|
||||
ClearSubPages();
|
||||
|
||||
// @todo Note: I don't actually suggest we use "sub pages" for the channel nav bar
|
||||
// The channel tabs should be in the main window and updated via jscript
|
||||
// Examples of good sub pages would be like Status, Chat, Settings, etc.
|
||||
// Under the Chat subpage we'd have the jscript client with its own chan tabs
|
||||
const vector<CChan*>& vChans = m_pUser->GetChans();
|
||||
|
||||
for (size_t a = 0; a < vChans.size(); a++) {
|
||||
CString sName(vChans[a]->GetName());
|
||||
VPair vParams;
|
||||
|
||||
vParams.push_back(make_pair("c", sName));
|
||||
AddSubPage(new CWebSubPage("chan", sName, vParams));
|
||||
}
|
||||
|
||||
return CModule::GetSubPages();
|
||||
}
|
||||
|
||||
virtual bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) {
|
||||
std::cerr << "=============================== webchat sPageName=[" << sPageName << "]" << std::endl;
|
||||
|
||||
if (sPageName.empty() || sPageName == "index") {
|
||||
return true;
|
||||
} else if (sPageName == "chan") {
|
||||
return ChannelPage(WebSock, Tmpl);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChannelPage(CWebSock& WebSock, CTemplate& Tmpl) {
|
||||
CChan* pChan = m_pUser->FindChan(WebSock.GetParam("c"));
|
||||
|
||||
if (pChan) {
|
||||
Tmpl["Title"] = pChan->GetName();
|
||||
|
||||
const VCString& vLines = pChan->GetBuffer();
|
||||
|
||||
for (size_t a = 0; a < vLines.size(); a++) {
|
||||
const CString& sLine(vLines[a]);
|
||||
CNick Nick(sLine.Token(0).LeftChomp_n());
|
||||
CTemplate& Row = Tmpl.AddRow("BufferLoop");
|
||||
|
||||
if (sLine.Token(1).Equals("PRIVMSG")) {
|
||||
Row["Type"] = "PRIVMSG";
|
||||
Row["Nick"] = Nick.GetNick();
|
||||
Row["Message"] = sLine.Token(3, true).TrimLeft_n(":");
|
||||
}
|
||||
}
|
||||
|
||||
const map<CString,CNick*>& msNicks = pChan->GetNicks();
|
||||
|
||||
for (map<CString,CNick*>::const_iterator it = msNicks.begin(); it != msNicks.end(); it++) {
|
||||
CTemplate& Row = Tmpl.AddRow("NickLoop");
|
||||
CNick& Nick = *it->second;
|
||||
|
||||
Row["Nick"] = Nick.GetNick();
|
||||
Row["Ident"] = Nick.GetIdent();
|
||||
Row["Host"] = Nick.GetHost();
|
||||
Row["ModePrefix"] = CString(Nick.GetPermChar());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
map<CString, unsigned int> m_suSwitchCounters;
|
||||
};
|
||||
|
||||
MODULEDEFS(CWebChatMod, "Web based chat")
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 98 B |
@@ -0,0 +1,42 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<form method="POST" action="/mods/notes/addnote">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Key:</td>
|
||||
<td>Note:</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="text" name="key" size="8"></td>
|
||||
<td><input type="text" name="note" size="32"></td>
|
||||
<td><input type="submit" name="add" value="Add Note"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<? IF !NotesLoop ?>
|
||||
You have no notes to display
|
||||
<? ELSE ?>
|
||||
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<td style="width: 10px;"> </td>
|
||||
<td>Key</td>
|
||||
<td>Note</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<? LOOP NotesLoop ?>
|
||||
<tr class="<? IF __EVEN__ ?>altrow<? ENDIF ?>">
|
||||
<td><a href="/mods/notes/delnote?key=<? VAR Key ESC=URL,HTML ?>"><img src="/modfiles/<? VAR ModName TOP ?>/trash.gif" alt="[del]"></a> </td>
|
||||
<td><? VAR Key ?></td>
|
||||
<td><? VAR Note ?></td>
|
||||
</tr>
|
||||
<? ENDLOOP ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<? ENDIF ?>
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,53 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<form action="<? IF Edit ?>editchan<? ELSE ?>addchan<? ENDIF ?>" method="post">
|
||||
<input type="hidden" name="submitted" value="1">
|
||||
<input type="hidden" name="user" value="<? VAR User ?>">
|
||||
|
||||
<table class="section">
|
||||
<thead><tr><td colspan="2">Channel Info</td></tr>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Channel Name:</td>
|
||||
|
||||
<td>
|
||||
<? IF Edit ?>
|
||||
<input type="hidden" name="name" value="<? VAR ChanName ?>">
|
||||
<? VAR ChanName ?>
|
||||
<? ELSE ?>
|
||||
<div><input type="text" name="name" value="" size="32"></div>
|
||||
<? ENDIF ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Buffer Count:</td>
|
||||
<td><input type="text" name="buffercount" value="<? VAR BufferCount ?>" size="8" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Default Modes:</td>
|
||||
<td><input type="text" name="defmodes" value="<? VAR DefModes ?>" size="16" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Save:</td>
|
||||
<td><input type="checkbox" name="save" id="save" value="true"<? IF InConfig ?> checked="checked"<? ENDIF ?> /><label for="save"> Save to config</label></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Options:</td>
|
||||
<td>
|
||||
<? LOOP OptionLoop ?>
|
||||
<input type="checkbox" name="<? VAR Name ?>" id="opt_<? VAR Name ?>" value="true"<? IF Checked ?> checked="checked"<? ENDIF ?><? IF Disabled ?> disabled="disabled"<? ENDIF ?> /><label for="opt_<? VAR Name ?>"> <? VAR DisplayName ?></label><br>
|
||||
<? ENDLOOP ?>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input type="submit" value="<? IF Edit ?>Save<? ELSE ?>Add Channel<? ENDIF ?>" />
|
||||
</form>
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,258 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<form action="<? IF Edit ?>edituser<? ELSE ?>adduser<? ENDIF ?>" method="post">
|
||||
<input type="hidden" name="submitted" value="1">
|
||||
<table class="section">
|
||||
<thead><tr><td colspan="2">Authentication</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Username:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<? IF Edit ?>
|
||||
<input type="hidden" name="user" value="<? VAR Username ?>">
|
||||
<input type="text" name="newuser" value="<? VAR Username ?>" size="32" maxlength="128" disabled="disabled">
|
||||
<? ELSE ?>
|
||||
<input type="text" name="user" value="<? VAR Username ?>" size="32" maxlength="128">
|
||||
<? ENDIF ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Password:</td>
|
||||
<td><input type="password" name="password" size="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Confirm password:</td>
|
||||
<td><input type="password" name="password2" size="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Allowed IPs:</td>
|
||||
<td><textarea name="allowedips" cols="40" rows="5"><? LOOP AllowedHostLoop ?><? VAR Host ?>
|
||||
<? ENDLOOP ?>
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="section">
|
||||
<thead><tr><td colspan="2">IRC Information</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Nickname:</td>
|
||||
<td><input type="text" name="nick" value="<? VAR Nick ?>" size="22" maxlength="128"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Alt. Nickname:</td>
|
||||
<td><input type="text" name="altnick" value="<? VAR AltNick ?>" size="22" maxlength="128"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Status Prefix:</td>
|
||||
<td><input type="text" name="statusprefix" value="<? VAR StatusPrefix ?>" size="16" maxlength="5"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Ident:</td>
|
||||
<td><input type="text" name="ident" value="<? VAR Ident ?>" size="22" maxlength="128"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Realname:</td>
|
||||
<td><input type="text" name="realname" value="<? VAR RealName ?>" size="68" maxlength="256"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>VHost:</td>
|
||||
<td><select name="vhost">
|
||||
<option value="">- Default -</option>
|
||||
<? LOOP VHostLoop ?><option value="<? VAR VHost ?>"<? IF Checked ?> selected="selected"<? ENDIF ?>><? VAR VHost ?></option><? ENDLOOP ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Quit-MSG:</td>
|
||||
<td><input type="text" name="quitmsg" value="<? VAR QuitMsg ?>" size="68" maxlength="256"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Servers:</td>
|
||||
<td><textarea name="servers" cols="40" rows="5"><? LOOP ServerLoop ?><? VAR Server ?>
|
||||
<? ENDLOOP ?>
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="section">
|
||||
<thead><tr><td>Module(s)</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Arguments</td>
|
||||
<td>Description</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<? LOOP ModuleLoop ?>
|
||||
<tbody>
|
||||
<tr class="<? IF __EVEN__ ?>altrow<? ENDIF ?>">
|
||||
<td>
|
||||
<span class="nowrap"><input type="checkbox" name="loadmod" id="lm_<? VAR Name ?>" value="<? VAR Name ?>"<? IF Checked ?> checked="checked"<? ENDIF ?><? IF Disabled ?> disabled="disabled"<? ENDIF ?> /><label for="lm_<? VAR Name ?>"> <? VAR Name ?></label></span>
|
||||
</td>
|
||||
<td>
|
||||
<? IF Disabled ?><? VAR Args ?><? ELSE ?><input type="text" name="modargs_<? VAR Name ?>" value="<? VAR Args ?>" /><? ENDIF ?>
|
||||
</td>
|
||||
<td><? VAR Description ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<? ENDLOOP ?>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="section">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="2">Channel(s)</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Default Modes:</td>
|
||||
<td><input type="text" name="chanmodes" value="<? VAR DefaultChanModes ?>" size="32" maxlength="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<? IF !ChannelLoop ?>
|
||||
<center>- There are no channels defined -</center>
|
||||
<? ELSE ?>
|
||||
<? IF Edit ?>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>[<a href="addchan?user=<? VAR Username ESC=URL ?>">Add</a>]</td>
|
||||
<? IF ChannelLoop ?>
|
||||
<td>Save</td>
|
||||
<td>Name</td>
|
||||
<td>CurModes</td>
|
||||
<td>DefModes</td>
|
||||
<td>BufferCount</td>
|
||||
<td>Options</td>
|
||||
<? ELSE ?>
|
||||
<td>- Add a channel (opens in same page)</td>
|
||||
<? ENDIF ?>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<? LOOP ChannelLoop ?>
|
||||
<tr class="<? IF __EVEN__ ?>altrow<? ENDIF ?>">
|
||||
<td>
|
||||
<input type="hidden" name="channel" value="<? VAR Name ?>" />
|
||||
[<a href="editchan?user=<? VAR Username ESC=URL ?>&name=<? VAR Name ESC=URL ?>">Edit</a>] [<a href="delchan?user=<? VAR Username ESC=URL ?>&name=<? VAR Name ESC=URL ?>">Del</a>]
|
||||
</td>
|
||||
<td><input type="checkbox" name="save_<? VAR Name ?>"<? IF InConfig ?> checked="checked"<? ENDIF ?> /></td>
|
||||
<td><? VAR Name ?></td>
|
||||
<td><? VAR CurModes ?></td>
|
||||
<td><? VAR DefModes ?></td>
|
||||
<td><? VAR BufferCount ?></td>
|
||||
<td><? VAR Options ?></td>
|
||||
</tr>
|
||||
<? ENDLOOP ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<? ENDIF ?>
|
||||
<? ENDIF ?>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="section">
|
||||
<thead><tr><td colspan="2">ZNC Behavior</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Skin:</td>
|
||||
<td>
|
||||
<? IF SkinLoop ROWS > 1 ?>
|
||||
<select name="skin">
|
||||
<option value="">- Global -</option>
|
||||
<? LOOP SkinLoop ?>
|
||||
<option value="<? VAR Name ?>"<? IF Checked ?> selected="selected"<? ENDIF ?>><? IF Name == "_default_" ?>- Default -<? ELSE ?><? VAR Name ?><? ENDIF ?></option>
|
||||
<? ENDLOOP ?>
|
||||
</select>
|
||||
<? ELSE ?>
|
||||
No other skins found
|
||||
<? ENDIF ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Playback Buffer Size:</td>
|
||||
<td><input type="text" name="bufsize" value="<? VAR BufferCount ?>" size="32" maxlength="9"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Timestamp Format:</td>
|
||||
<td><input type="text" name="timestampformat" value="<? VAR TimestampFormat ?>" size="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Timezone offset:</td>
|
||||
<td><input type="text" name="timezoneoffset" value="<? VAR TimezoneOffset ?>" size="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Join Tries:</td>
|
||||
<td><input type="text" name="jointries" value="<? VAR JoinTries ?>" size="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Max Joins:</td>
|
||||
<td><input type="text" name="maxjoins" value="<? VAR MaxJoins ?>" size="32"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Options:</td>
|
||||
<td>
|
||||
<? LOOP OptionLoop ?>
|
||||
<span class="nowrap"><input type="checkbox" name="<? VAR Name ?>" id="opt_<? VAR Name ?>" value="1"<? IF Checked ?> checked="checked"<? ENDIF ?><? IF Disabled ?> disabled="disabled"<? ENDIF ?> /><label for="opt_<? VAR Name ?>"><? VAR DisplayName ?></label><br></span>
|
||||
<? ENDLOOP ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>CTCP Replies:</td>
|
||||
<td><textarea name="ctcpreplies" cols="40" rows="5"><? LOOP CTCPLoop ?><? VAR CTCP ?>
|
||||
<? ENDLOOP ?>
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input type="submit" value="<? IF Edit ?>Save User<? ELSE ?>Add User<? ENDIF ?>">
|
||||
</form>
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,9 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<a href="/mods/webadmin/settings">Settings</a>
|
||||
<br>
|
||||
<a href="/mods/webadmin/listusers">List Users</a>
|
||||
<br>
|
||||
<a href="/mods/webadmin/adduser">Add User</a>
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,35 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<? IF !UserLoop ?>
|
||||
There are no users defined.<br>
|
||||
Click <a href="adduser">here</a>, if you would like to add one.
|
||||
<? ELSE ?>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Action</td>
|
||||
<td>Username</td>
|
||||
<td>Clients</td>
|
||||
<td>Current Server</td>
|
||||
<td>IRC Nick</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<? LOOP UserLoop ?>
|
||||
<tr class="<? IF __EVEN__ ?>altrow<? ENDIF ?>">
|
||||
<td>
|
||||
<span class="nowrap">
|
||||
[<a href="edituser?user=<? VAR Username ESC=URL ?>">Edit</a>]
|
||||
<? IF !IsSelf ?>[<a href="deluser?user=<? VAR Username ESC=URL ?>" onClick="return confirm('Do you really wish to remove this user?');">Delete</a>]<? ENDIF ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><b><? VAR Username DEFAULT="-N/A-" ?></b></td>
|
||||
<td><? VAR Clients DEFAULT="-N/A-" ?></td>
|
||||
<td><? VAR Server DEFAULT="-N/A-" ?></td>
|
||||
<td><? VAR IRCNick DEFAULT="-N/A-" ?></td>
|
||||
</tr>
|
||||
<? ENDLOOP ?>
|
||||
</table>
|
||||
<? ENDIF ?>
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,149 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<form action="settings" method="POST">
|
||||
<input type="hidden" name="submitted" value="1">
|
||||
<table class="section">
|
||||
<thead><tr><td>Listen Port(s)</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Port</td>
|
||||
<td>BindHost</td>
|
||||
<td>SSL</td>
|
||||
<td>IPv6</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<? LOOP ListenLoop ?>
|
||||
<tr class="<? IF __EVEN__ ?>altrow<? ENDIF ?>">
|
||||
<td><? VAR Port ?></td>
|
||||
<td><? VAR BindHost DEFAULT=** ?></td>
|
||||
<td><? IF IsSSL ?>True<? ELSE ?>False<? ENDIF ?></td>
|
||||
<td><? IF IsIPV6 ?>True<? ELSE ?>False<? ENDIF ?></td>
|
||||
</tr>
|
||||
<? ENDLOOP ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="section">
|
||||
<thead><tr><td colspan="2">Settings</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Skin:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<? IF SkinLoop ROWS > 1 ?>
|
||||
<select name="skin">
|
||||
<? LOOP SkinLoop ?>
|
||||
<option value="<? VAR Name ?>"<? IF Checked ?> selected="selected"<? ENDIF ?>><? VAR Name ?></option>
|
||||
<? ENDLOOP ?>
|
||||
</select>
|
||||
<? ELSE ?>
|
||||
No other skins found
|
||||
<? ENDIF ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
Status prefix:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="text" name="statusprefix" value="<? VAR StatusPrefix ?>" size="32" maxlength="128">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
ISpoofFile:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="text" name="ispooffile" value="<? VAR ISpoofFile ?>" size="32" maxlength="128">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
ISpoofFormat:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="text" name="ispoofformat" value="<? VAR ISpoofFormat ?>" size="32" maxlength="128">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
MOTD:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<textarea name="motd" cols="10" rows="5"><? LOOP MOTDLoop ?><? VAR Line ?>
|
||||
<? ENDLOOP ?>
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
VHosts:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<textarea name="vhosts" cols="10" rows="5"><? LOOP VHostLoop ?><? VAR VHost ?>
|
||||
<? ENDLOOP ?>
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="section">
|
||||
<thead><tr><td>Global Module(s)</td></tr></thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="data">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Arguments</td>
|
||||
<td>Description</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<? LOOP ModuleLoop ?>
|
||||
<tr class="<? IF __EVEN__ ?>altrow<? ENDIF ?>">
|
||||
<td><span class="nowrap"><input type="checkbox" name="loadmod" id="lm_<? VAR Name ?>" value="<? VAR Name ?>"<? IF Checked ?> checked="checked"<? ENDIF ?><? IF Disabled ?> disabled="disabled"<? ENDIF ?> /><label for="lm_<? VAR Name ?>"> <? VAR Name ?></label></span></td>
|
||||
<td><input type="text" name="modargs_<? VAR Name ?>" value="<? VAR Args ?>" /></td>
|
||||
<td><? VAR Description ?></td>
|
||||
</tr>
|
||||
<? ENDLOOP ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<br><br>
|
||||
<input type="submit" value="Save settings" />
|
||||
</form>
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
<script type="javascript" src="jisirc.js"></script>
|
||||
<? VAR ChannelName ?>
|
||||
|
||||
<textarea readonly=on style="width: 75%; height: 350px; display: inline; margin: 0">
|
||||
<? LOOP BufferLoop ?>
|
||||
<? IF Type == "PRIVMSG" ?>
|
||||
<<? VAR Nick ?>> <? VAR Message ?>
|
||||
<? ENDIF ?>
|
||||
<? ENDLOOP ?>
|
||||
</textarea><select multiple style="width: 20%; height: 350px; vertical-align: top; margin: 0">
|
||||
<? LOOP NickLoop ?>
|
||||
<? IF ModePrefix == "@" ?>
|
||||
<option value="<? VAR Nick ?>"><? VAR ModePrefix ?><? VAR Nick ?></option>
|
||||
<? ENDIF ?>
|
||||
<? ENDLOOP ?>
|
||||
<? LOOP NickLoop ?>
|
||||
<? IF ModePrefix == "+" ?>
|
||||
<option value="<? VAR Nick ?>"><? VAR ModePrefix ?><? VAR Nick ?></option>
|
||||
<? ENDIF ?>
|
||||
<? ENDLOOP ?>
|
||||
<? LOOP NickLoop ?>
|
||||
<? IF ModePrefix != "@" && ModePrefix != "+" ?>
|
||||
<option value="<? VAR Nick ?>"><? VAR ModePrefix ?><? VAR Nick ?></option>
|
||||
<? ENDIF ?>
|
||||
<? ENDLOOP ?>
|
||||
</select>
|
||||
<input style="width: 95%" type="text" value="This box does nothing so far, and you'll have to refresh to get updates!">
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
Welcome to the webchat module. It's currently more like a buffer reply module since there isn't any real-time back and forth communication (yet).
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,221 @@
|
||||
html,
|
||||
body {
|
||||
background: #555;
|
||||
padding: 0;
|
||||
margin: 10px 0;
|
||||
background: #444;
|
||||
font-family: verdana;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* TABLES */
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
table.section,
|
||||
table.data {
|
||||
width: 100%;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #f00;
|
||||
}
|
||||
|
||||
table.section td,
|
||||
table.data td {
|
||||
height: 20px;
|
||||
border: 1px solid #000;
|
||||
padding: 2px 3px;
|
||||
}
|
||||
|
||||
table.section thead td,
|
||||
table.data thead td {
|
||||
background-color: #D49712;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.data thead td {
|
||||
background-color: #EC8E00;
|
||||
}
|
||||
|
||||
table.data tbody .altrow td {
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
table.section table.data {
|
||||
width: 95%;
|
||||
margin: 10 auto;
|
||||
}
|
||||
|
||||
/* !TABLES */
|
||||
|
||||
/* FORMS */
|
||||
|
||||
input, select, textarea {
|
||||
font-family: verdana;
|
||||
font-size: 12px;
|
||||
color: #000000;
|
||||
border: 1px solid #000000;
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
table.section textarea,
|
||||
table.section select,
|
||||
table.section input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* !FORMS */
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* LINKS */
|
||||
|
||||
a:link,
|
||||
a:active,
|
||||
a:visited,
|
||||
a:hover {
|
||||
font-family: verdana;
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* !LINKS */
|
||||
|
||||
#wrapper {
|
||||
width: 800px;
|
||||
border: 1px solid #000;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#banner {
|
||||
background-image: url('clouds-header.jpg');
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #000000;
|
||||
height: 100px;
|
||||
width: 800px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#banner p {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
#infobar {
|
||||
width: 800px;
|
||||
height: 20px;
|
||||
border-bottom: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
}
|
||||
|
||||
#infobar span {
|
||||
float: left;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
#infobar span.switchuser {
|
||||
text-align: center;
|
||||
border-left: 1px solid #000000;
|
||||
height: 100%;
|
||||
width: 150px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#subpage {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#content {
|
||||
float: right;
|
||||
width: 640px;
|
||||
padding: 0;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
/* MENU */
|
||||
|
||||
#menu {
|
||||
float: left;
|
||||
background-color: #333;
|
||||
width: 160px;
|
||||
margin: 0;
|
||||
margin-bottom: 20px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#menu .title,
|
||||
#menu .item,
|
||||
#menu .subitem {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
padding: 8px 5px;
|
||||
background-color: #777;
|
||||
border-bottom: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
}
|
||||
|
||||
#menu .title {
|
||||
text-align: left;
|
||||
padding-left: 3px;
|
||||
background-color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#menu .item.active {
|
||||
background-color: #D49712;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#menu .subitem {
|
||||
text-align: left;
|
||||
padding: 3px 5px 3px 10px;
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
#menu .subitem.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#menu .subitem.active a:hover {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* !MENU */
|
||||
|
||||
#footerbar {
|
||||
clear: both;
|
||||
background-color: #D49712;
|
||||
border-top: 1px solid #000000;
|
||||
height: 20px;
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#breadcrumb {
|
||||
padding: 5px 10px;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<? SETOPTION ESC=HTML ?>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
|
||||
<title>ZNC - <? VAR Title DEFAULT="Web Frontend" ?></title>
|
||||
<link rel="stylesheet" type="text/css" href="/pub/main.css" />
|
||||
<? LOOP CSSLoop ?>
|
||||
<?IF HREF?><link rel="stylesheet" type="text/css" href="<? VAR HREF ?>" /><?ENDIF?>
|
||||
<?ENDLOOP?>
|
||||
|
||||
<!--[if lt IE 8]><script type="text/javascript">var MSIE = 1;</script><![endif]-->
|
||||
<? LOOP JSLoop ?>
|
||||
<?IF HREF?><script type="text/javascript" src="<? VAR HREF ?>"></script><?ENDIF?>
|
||||
<?ENDLOOP?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Wrapper -->
|
||||
<div id="wrapper">
|
||||
<!-- Banner -->
|
||||
<div id="banner"><p><? VAR Tag ?></p></div>
|
||||
<!-- !Banner -->
|
||||
|
||||
<!-- Loginbar -->
|
||||
<div id="infobar">
|
||||
<span>Logged in as: <em><? VAR SessionUser DEFAULT="-Guest-" ?></em> (from: <? VAR SessionIP ?>)</span>
|
||||
<? IF LoggedIn ?>
|
||||
<span class="switchuser"><a href="/logout">Logout</a></span>
|
||||
<? ELSE ?>
|
||||
<span class="switchuser"><a href="/login">Login</a></span>
|
||||
<? ENDIF ?>
|
||||
</div>
|
||||
<!-- / Loginbar -->
|
||||
|
||||
<!-- Main -->
|
||||
<div id="main">
|
||||
<!-- Menu -->
|
||||
<div id="menu">
|
||||
<? INC Menu.tmpl ?>
|
||||
</div>
|
||||
<!-- / Menu -->
|
||||
|
||||
<!-- Content -->
|
||||
<div id="content">
|
||||
|
||||
<div id="breadcrumb">ZNC
|
||||
<? IF ModName ?><b>»</b> <? VAR ModName ?><? ENDIF ?>
|
||||
<? REM ?><? IF PageName ?><b>»</b> <? VAR PageName ?><? ENDIF ?><? ENDREM ?>
|
||||
<? IF Title ?><b>»</b> <? VAR Title ?><? ENDIF ?>
|
||||
</div>
|
||||
|
||||
<div id="subpage">
|
||||
|
||||
<? REM ?>Called from subpage then finished in Footer.tmpl<? ENDREM ?>
|
||||
@@ -0,0 +1,3 @@
|
||||
<? INC Header.tmpl ?>
|
||||
<h3><? VAR Error ?></h3>
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,16 @@
|
||||
</div>
|
||||
<!-- !SubPage -->
|
||||
</div>
|
||||
<!-- !Content -->
|
||||
</div>
|
||||
<!-- !Main -->
|
||||
|
||||
<!-- FooterBar -->
|
||||
<div id="footerbar">
|
||||
<? INC FooterTag.tmpl ?>
|
||||
</div>
|
||||
<!-- !FooterBar -->
|
||||
</div>
|
||||
<!-- !Wrapper -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
<i>ZNC Web Skin "dark-clouds" by <a href="mailto:davidp@preshweb.co.uk">David Precious</a></i>
|
||||
@@ -0,0 +1,18 @@
|
||||
<? INC BaseHeader.tmpl ?>
|
||||
<? REM ?>
|
||||
|
||||
This is a wrapper file which simply includes BaseHeader.tmpl so that new skins can make
|
||||
a Header.tmpl similar to...
|
||||
|
||||
<? ADDROW CSSLoop HREF=/pub/myskin.css ?>
|
||||
<? INC BaseHeader.tmpl ?>
|
||||
|
||||
...this way a skin can base itself off of the same html as the default skin but still add
|
||||
custom css/js
|
||||
|
||||
@todo In the future I'd like to support something like <? INC File.tmpl SUPER ?> or even
|
||||
just do a current file vs inc'd file comparison to make sure they aren't the same.
|
||||
This way we can <? INC Header.tmpl ?> from the "derived" Header.tmpl and not cause
|
||||
an recursive loop.
|
||||
|
||||
<? ENDREM ?>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="item <? IF !ModName && PageName == "index" ?>active<? ENDIF ?>"><a href="/">Home</a></div>
|
||||
<div class="item <? IF !ModName && PageName == "help" ?>active<? ENDIF ?>"><a href="/help">Help</a></div>
|
||||
|
||||
<? IF GlobalModLoop ?>
|
||||
<div class="title">Global Modules:</div>
|
||||
<? LOOP GlobalModLoop ?>
|
||||
<div class="item <? IF Active ?>active<? ENDIF ?>"><a href="/mods/<? VAR ModName ?>"><? VAR Title ?></a></div>
|
||||
<? LOOP SubPageLoop ?>
|
||||
<div class="subitem <? IF Active ?>active<? ENDIF ?>"><a href="/mods/<? VAR ModName ?>/<? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? IF Active ?>» <? ENDIF ?><? VAR Title ?><? IF Active ?> «<? ENDIF ?></a></div>
|
||||
<? ENDLOOP ?>
|
||||
<? ENDLOOP ?>
|
||||
<? ENDIF ?>
|
||||
|
||||
<? IF UserModLoop ?>
|
||||
<div class="title">User Modules:</div>
|
||||
<? LOOP UserModLoop ?>
|
||||
<div class="item <? IF Active ?>active<? ENDIF ?>"><a href="/mods/<? IF ModUser ?><? VAR ModUser ?>:<? ENDIF ?><? VAR ModName ?>"><? VAR Title ?></a></div>
|
||||
<? LOOP SubPageLoop ?>
|
||||
<div class="subitem <? IF Active ?>active<? ENDIF ?>"><a href="/mods/<? VAR ModName ?>/<? VAR PageName ?><? IF Params ?>?<? VAR Params ?><? ENDIF ?>"><? IF Active ?>» <? ENDIF ?><? VAR Title ?></a></div>
|
||||
<? ENDLOOP ?>
|
||||
<? ENDLOOP ?>
|
||||
<? ENDIF ?>
|
||||
@@ -0,0 +1,5 @@
|
||||
<? INC Header.tmpl ?>
|
||||
|
||||
This is the help section. A quick tutorial with links to the <a href="http://znc.in/">wiki</a> should go here before we release.
|
||||
|
||||
<? INC Footer.tmpl ?>
|
||||
@@ -0,0 +1,3 @@
|
||||
<? INC Header.tmpl ?>
|
||||
Welcome to ZNC's web interface!
|
||||
<? INC Footer.tmpl ?>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 215 KiB |
@@ -0,0 +1,21 @@
|
||||
table.section thead td,
|
||||
table.data thead td {
|
||||
background-color: #049712;
|
||||
}
|
||||
|
||||
table.data thead td {
|
||||
background-color: #007700;
|
||||
}
|
||||
|
||||
#banner {
|
||||
background-image: url('forest-header.png');
|
||||
}
|
||||
|
||||
#menu .item.active {
|
||||
background-color: #049712;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#footerbar {
|
||||
background-color: #049712;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<i>ZNC "forest" Web Skin - based on "dark-clouds" by <a href="mailto:davidp@preshweb.co.uk">David Precious</a></i>
|
||||
@@ -0,0 +1,2 @@
|
||||
<? ADDROW CSSLoop HREF=/pub/forest.css ?>
|
||||
<? INC BaseHeader.tmpl ?>
|
||||
@@ -575,6 +575,11 @@ bool CZNC::WriteConfig() {
|
||||
if (!m_sPidFile.empty()) {
|
||||
m_LockFile.Write("PidFile = " + m_sPidFile.FirstLine() + "\n");
|
||||
}
|
||||
|
||||
if (!m_sSkinName.empty()) {
|
||||
m_LockFile.Write("Skin = " + m_sSkinName.FirstLine() + "\n");
|
||||
}
|
||||
|
||||
if (!m_sStatusPrefix.empty()) {
|
||||
m_LockFile.Write("StatusPrefix = " + m_sStatusPrefix.FirstLine() + "\n");
|
||||
}
|
||||
@@ -1418,6 +1423,9 @@ bool CZNC::DoRehash(CString& sError)
|
||||
} else if (sName.Equals("MaxJoins")) {
|
||||
pUser->SetMaxJoins(sValue.ToUInt());
|
||||
continue;
|
||||
} else if (sName.Equals("Skin")) {
|
||||
pUser->SetSkinName(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("LoadModule")) {
|
||||
CString sModName = sValue.Token(0);
|
||||
CUtils::PrintAction("Loading Module [" + sModName + "]");
|
||||
@@ -1570,6 +1578,9 @@ bool CZNC::DoRehash(CString& sError)
|
||||
} else if (sName.Equals("PidFile")) {
|
||||
m_sPidFile = sValue;
|
||||
continue;
|
||||
} else if (sName.Equals("Skin")) {
|
||||
SetSkinName(sValue);
|
||||
continue;
|
||||
} else if (sName.Equals("StatusPrefix")) {
|
||||
m_sStatusPrefix = sValue;
|
||||
continue;
|
||||
@@ -1751,6 +1762,24 @@ void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly,
|
||||
}
|
||||
}
|
||||
|
||||
CModule* CZNC::FindModule(const CString& sModName, const CString& sUsername) {
|
||||
if (sUsername.empty()) {
|
||||
return CZNC::Get().GetModules().FindModule(sModName);
|
||||
}
|
||||
|
||||
CUser* pUser = FindUser(sUsername);
|
||||
|
||||
return (!pUser) ? NULL : pUser->GetModules().FindModule(sModName);
|
||||
}
|
||||
|
||||
CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) {
|
||||
if (pUser) {
|
||||
return pUser->GetModules().FindModule(sModName);
|
||||
}
|
||||
|
||||
return CZNC::Get().GetModules().FindModule(sModName);
|
||||
}
|
||||
|
||||
CUser* CZNC::FindUser(const CString& sUsername) {
|
||||
map<CString,CUser*>::iterator it = m_msUsers.find(sUsername);
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ public:
|
||||
|
||||
// Setters
|
||||
void SetConfigState(enum ConfigState e) { m_eConfigState = e; }
|
||||
void SetSkinName(const CString& s) { m_sSkinName = s; }
|
||||
void SetStatusPrefix(const CString& s) { m_sStatusPrefix = (s.empty()) ? "*" : s; }
|
||||
void SetISpoofFile(const CString& s) { m_sISpoofFile = s; }
|
||||
void SetISpoofFormat(const CString& s) { m_sISpoofFormat = (s.empty()) ? "global { reply \"%\" }" : s; }
|
||||
@@ -95,6 +96,7 @@ public:
|
||||
CGlobalModules& GetModules() { return *m_pModules; }
|
||||
size_t FilterUncommonModules(set<CModInfo>& ssModules);
|
||||
#endif
|
||||
CString GetSkinName() const { return m_sSkinName; }
|
||||
const CString& GetStatusPrefix() const { return m_sStatusPrefix; }
|
||||
const CString& GetCurPath() const { if (!CFile::Exists(m_sCurPath)) { CDir::MakeDir(m_sCurPath); } return m_sCurPath; }
|
||||
const CString& GetHomePath() const { if (!CFile::Exists(m_sHomePath)) { CDir::MakeDir(m_sHomePath); } return m_sHomePath; }
|
||||
@@ -115,6 +117,8 @@ public:
|
||||
// Static allocator
|
||||
static CZNC& Get();
|
||||
CUser* FindUser(const CString& sUsername);
|
||||
CModule* FindModule(const CString& sModName, const CString& sUsername);
|
||||
CModule* FindModule(const CString& sModName, CUser* pUser);
|
||||
bool DeleteUser(const CString& sUsername);
|
||||
bool AddUser(CUser* pUser, CString& sErrorRet);
|
||||
const map<CString,CUser*> & GetUserMap() const { return(m_msUsers); }
|
||||
@@ -154,6 +158,7 @@ protected:
|
||||
CString m_sZNCPath;
|
||||
|
||||
CString m_sConfigFile;
|
||||
CString m_sSkinName;
|
||||
CString m_sStatusPrefix;
|
||||
CString m_sISpoofFile;
|
||||
CString m_sOrigISpoof;
|
||||
@@ -263,7 +268,7 @@ protected:
|
||||
bool m_bIPV6;
|
||||
unsigned short m_uPort;
|
||||
CString m_sBindHost;
|
||||
CRealListener* m_pListener;
|
||||
CRealListener* m_pListener;
|
||||
};
|
||||
|
||||
#endif // !_ZNC_H
|
||||
|
||||
Reference in New Issue
Block a user