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:
prozacx
2010-02-22 07:40:22 +00:00
parent 7331fe3fd2
commit c09dca3b8e
41 changed files with 2845 additions and 1109 deletions
+22 -1
View File
@@ -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
View File
@@ -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)
+8
View File
@@ -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;
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+4
View File
@@ -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
+28
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
+1
View File
@@ -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 \
+194
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+110
View File
@@ -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

+42
View File
@@ -0,0 +1,42 @@
<? INC Header.tmpl ?>
<form method="POST" action="/mods/notes/addnote">
<table>
<tr>
<td>Key:</td>
<td>Note:</td>
<td>&nbsp;</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;">&nbsp;</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>&nbsp;&nbsp;&nbsp;</td>
<td><? VAR Key ?></td>
<td><? VAR Note ?></td>
</tr>
<? ENDLOOP ?>
</tbody>
</table>
<? ENDIF ?>
<? INC Footer.tmpl ?>
+53
View File
@@ -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 ?>
+258
View File
@@ -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 ?>&amp;name=<? VAR Name ESC=URL ?>">Edit</a>] [<a href="delchan?user=<? VAR Username ESC=URL ?>&amp;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 ?>
+9
View File
@@ -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 ?>
+35
View File
@@ -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 ?>
+149
View File
@@ -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 ?>
+31
View File
@@ -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" ?>
&lt;<? VAR Nick ?>&gt; <? 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 ?>
+5
View File
@@ -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

+221
View File
@@ -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;
}
+55
View File
@@ -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>&raquo;</b> <? VAR ModName ?><? ENDIF ?>
<? REM ?><? IF PageName ?><b>&raquo;</b> <? VAR PageName ?><? ENDIF ?><? ENDREM ?>
<? IF Title ?><b>&raquo;</b> <? VAR Title ?><? ENDIF ?>
</div>
<div id="subpage">
<? REM ?>Called from subpage then finished in Footer.tmpl<? ENDREM ?>
+3
View File
@@ -0,0 +1,3 @@
<? INC Header.tmpl ?>
<h3><? VAR Error ?></h3>
<? INC Footer.tmpl ?>
+16
View File
@@ -0,0 +1,16 @@
</div>
<!-- !SubPage -->
</div>
<!-- !Content -->
</div>
<!-- !Main -->
<!-- FooterBar -->
<div id="footerbar">
<? INC FooterTag.tmpl ?>
</div>
<!-- !FooterBar -->
</div>
<!-- !Wrapper -->
</body>
</html>
+1
View File
@@ -0,0 +1 @@
<i>ZNC Web Skin "dark-clouds" by <a href="mailto:davidp@preshweb.co.uk">David Precious</a></i>
+18
View File
@@ -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 ?>
+22
View File
@@ -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 ?>&raquo; <? ENDIF ?><? VAR Title ?><? IF Active ?> &laquo;<? 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 ?>&raquo; <? ENDIF ?><? VAR Title ?></a></div>
<? ENDLOOP ?>
<? ENDLOOP ?>
<? ENDIF ?>
+5
View File
@@ -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 ?>
+3
View File
@@ -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

+21
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
<i>ZNC "forest" Web Skin - based on "dark-clouds" by <a href="mailto:davidp@preshweb.co.uk">David Precious</a></i>
+2
View File
@@ -0,0 +1,2 @@
<? ADDROW CSSLoop HREF=/pub/forest.css ?>
<? INC BaseHeader.tmpl ?>
+29
View File
@@ -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);
+6 -1
View File
@@ -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