mirror of
https://github.com/znc/znc.git
synced 2026-07-04 00:41:38 +02:00
Import SilverLeo's modtcl from znc-extra
The configure and Makefile stuff was taken from there, too. By default modtcl is disabled, use --enable-extra --enable-tcl to get it. git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@1609 726aef4b-f618-498e-8847-2d620e286838
This commit is contained in:
@@ -556,6 +556,7 @@ LIBOBJS
|
||||
DATADIR
|
||||
MODDIR
|
||||
SASL
|
||||
TCL_FLAGS
|
||||
EXTRA
|
||||
NOSSL
|
||||
MODTARGET
|
||||
@@ -636,6 +637,9 @@ enable_optimization
|
||||
enable_c_ares
|
||||
with_module_prefix
|
||||
with_module_data_prefix
|
||||
enable_tcl
|
||||
with_tcl_flags
|
||||
with_tcl
|
||||
'
|
||||
ac_precious_vars='build_alias
|
||||
host_alias
|
||||
@@ -1274,6 +1278,7 @@ Optional Features:
|
||||
--disable-optimization Disable some compiler optimizations to decrease
|
||||
memory usage while compiling
|
||||
--disable-c-ares disable c-ares usage
|
||||
--enable-tcl enable modtcl
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
@@ -1282,6 +1287,8 @@ Optional Packages:
|
||||
--with-module-prefix module object code [LIBDIR/znc]
|
||||
--with-module-data-prefix=DIR
|
||||
static module data (webadmin skins) [DATADIR/znc]
|
||||
--with-tcl-flags=FLAGS The flags needed for compiling and linking modtcl
|
||||
--with-tcl=DIR directory containing tclConfig.sh
|
||||
|
||||
Some influential environment variables:
|
||||
CXX C++ compiler command
|
||||
@@ -3408,6 +3415,79 @@ fi
|
||||
|
||||
fi
|
||||
|
||||
# Check if we want modtcl
|
||||
# Check whether --enable-tcl was given.
|
||||
if test "${enable_tcl+set}" = set; then :
|
||||
enableval=$enable_tcl; TCL="yes"
|
||||
else
|
||||
TCL="no"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check whether --with-tcl-flags was given.
|
||||
if test "${with_tcl_flags+set}" = set; then :
|
||||
withval=$with_tcl_flags; TCL_FLAGS="$withval"
|
||||
fi
|
||||
|
||||
|
||||
if test x"$TCL" = "xyes"
|
||||
then
|
||||
|
||||
# Check whether --with-tcl was given.
|
||||
if test "${with_tcl+set}" = set; then :
|
||||
withval=$with_tcl; TCL_DIR="${withval}"
|
||||
fi
|
||||
|
||||
|
||||
# This need most likely most be extended in the future, but I dont think
|
||||
# it's a good idea to stuff a shitload of random stuff in here right now
|
||||
for path in $TCL_DIR /usr/lib /usr/lib/tcl8.4 /usr/lib/tcl8.5
|
||||
do
|
||||
file="${path}/tclConfig.sh"
|
||||
as_ac_File=`$as_echo "ac_cv_file_${file}" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${file}" >&5
|
||||
$as_echo_n "checking for ${file}... " >&6; }
|
||||
if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
test "$cross_compiling" = yes &&
|
||||
as_fn_error "cannot check for file existence when cross compiling" "$LINENO" 5
|
||||
if test -r "${file}"; then
|
||||
eval "$as_ac_File=yes"
|
||||
else
|
||||
eval "$as_ac_File=no"
|
||||
fi
|
||||
fi
|
||||
eval ac_res=\$$as_ac_File
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
eval as_val=\$$as_ac_File
|
||||
if test "x$as_val" = x""yes; then :
|
||||
TCL_CONF="$file" ; break
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
if test x"${TCL_CONF}" = x
|
||||
then
|
||||
# They --enable-tcl'd, so give them some sane default
|
||||
TCL_FLAGS="-I/usr/include/tcl -ltcl"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find tclConfig.sh, using some sane defaults." >&5
|
||||
$as_echo "$as_me: WARNING: Could not find tclConfig.sh, using some sane defaults." >&2;}
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking modtcl flags" >&5
|
||||
$as_echo_n "checking modtcl flags... " >&6; }
|
||||
. ${TCL_CONF}
|
||||
# eval because those vars depend on other vars in there
|
||||
eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
|
||||
eval "TCL_INCLUDE_SPEC=\"${TCL_INCLUDE_SPEC}\""
|
||||
TCL_FLAGS="$TCL_INCLUDE_SPEC $TCL_LIB_SPEC"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $TCL_FLAGS" >&5
|
||||
$as_echo "$TCL_FLAGS" >&6; }
|
||||
fi
|
||||
fi
|
||||
|
||||
appendCXX "-D_MODDIR_=\\\"${MODDIR}\\\""
|
||||
appendCXX "-D_DATADIR_=\\\"${DATADIR}\\\""
|
||||
fi
|
||||
@@ -3427,6 +3507,7 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile"
|
||||
|
||||
ac_config_files="$ac_config_files znc-config"
|
||||
@@ -4606,3 +4687,8 @@ else
|
||||
echo "sasl: yes"
|
||||
fi
|
||||
echo "extra: $EXTRA"
|
||||
if test x"$TCL_FLAGS" = "x" ; then
|
||||
echo "tcl: no"
|
||||
else
|
||||
echo "tcl: yes"
|
||||
fi
|
||||
|
||||
@@ -234,6 +234,48 @@ if test "$MODULES" = "yes"; then
|
||||
AC_MSG_ERROR([could not find libsasl2. Try --disable-sasl.]))
|
||||
fi
|
||||
|
||||
# Check if we want modtcl
|
||||
AC_ARG_ENABLE( [tcl],
|
||||
AS_HELP_STRING([--enable-tcl], [enable modtcl]),
|
||||
[TCL="yes"],
|
||||
[TCL="no"])
|
||||
|
||||
AC_ARG_WITH( [tcl-flags],
|
||||
AS_HELP_STRING([--with-tcl-flags=FLAGS],
|
||||
[The flags needed for compiling and linking modtcl]),
|
||||
[TCL_FLAGS="$withval"],)
|
||||
|
||||
if test x"$TCL" = "xyes"
|
||||
then
|
||||
AC_ARG_WITH( [tcl],
|
||||
AS_HELP_STRING([--with-tcl=DIR],
|
||||
[directory containing tclConfig.sh]),
|
||||
TCL_DIR="${withval}")
|
||||
|
||||
# This need most likely most be extended in the future, but I dont think
|
||||
# it's a good idea to stuff a shitload of random stuff in here right now
|
||||
for path in $TCL_DIR /usr/lib /usr/lib/tcl8.4 /usr/lib/tcl8.5
|
||||
do
|
||||
file="${path}/tclConfig.sh"
|
||||
AC_CHECK_FILE(${file}, [TCL_CONF="$file" ; break])
|
||||
done
|
||||
|
||||
if test x"${TCL_CONF}" = x
|
||||
then
|
||||
# They --enable-tcl'd, so give them some sane default
|
||||
TCL_FLAGS="-I/usr/include/tcl -ltcl"
|
||||
AC_MSG_WARN([Could not find tclConfig.sh, using some sane defaults.])
|
||||
else
|
||||
AC_MSG_CHECKING([modtcl flags])
|
||||
. ${TCL_CONF}
|
||||
# eval because those vars depend on other vars in there
|
||||
eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
|
||||
eval "TCL_INCLUDE_SPEC=\"${TCL_INCLUDE_SPEC}\""
|
||||
TCL_FLAGS="$TCL_INCLUDE_SPEC $TCL_LIB_SPEC"
|
||||
AC_MSG_RESULT([$TCL_FLAGS])
|
||||
fi
|
||||
fi
|
||||
|
||||
appendCXX "-D_MODDIR_=\\\"${MODDIR}\\\""
|
||||
appendCXX "-D_DATADIR_=\\\"${DATADIR}\\\""
|
||||
fi
|
||||
@@ -250,6 +292,7 @@ AC_SUBST([MODTARGET])
|
||||
AC_SUBST([NOSSL])
|
||||
AC_SUBST([EXTRA])
|
||||
AC_SUBST([PERL])
|
||||
AC_SUBST([TCL_FLAGS])
|
||||
AC_SUBST([SASL])
|
||||
AC_SUBST([MODDIR])
|
||||
AC_SUBST([DATADIR])
|
||||
@@ -287,3 +330,8 @@ else
|
||||
echo "sasl: yes"
|
||||
fi
|
||||
echo "extra: $EXTRA"
|
||||
if test x"$TCL_FLAGS" = "x" ; then
|
||||
echo "tcl: no"
|
||||
else
|
||||
echo "tcl: yes"
|
||||
fi
|
||||
|
||||
+16
-2
@@ -25,6 +25,8 @@ DATADIR := @DATADIR@
|
||||
LIBZNC := @LIBZNC@
|
||||
LIBZNCDIR:= @LIBZNCDIR@
|
||||
|
||||
TCL_FLAGS:= @TCL_FLAGS@
|
||||
|
||||
ifneq "$(LIBZNC)" ""
|
||||
LDFLAGS += -L.. -lznc -Wl,-rpath,$(LIBZNCDIR)
|
||||
endif
|
||||
@@ -62,6 +64,14 @@ FILES := $(shell echo $(FILES) | sed -e "s/saslauth//")
|
||||
endif
|
||||
saslauthFLAGS := -lsasl2
|
||||
|
||||
ifeq "$(TCL_FLAGS)" ""
|
||||
FILES := $(shell echo $(FILES) | sed -e "s:extra/modtcl::g")
|
||||
else
|
||||
TCLHOOK := modtcl_install
|
||||
endif
|
||||
modtclFLAGS := $(TCL_FLAGS)
|
||||
|
||||
|
||||
TARGETS := $(addsuffix .so, $(FILES))
|
||||
|
||||
CLEAN := *.so extra/*.so
|
||||
@@ -70,7 +80,7 @@ CLEAN := *.so extra/*.so
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
install: all create_install_dir install_metadirs $(PERLHOOK)
|
||||
install: all create_install_dir install_metadirs $(PERLHOOK) $(TCLHOOK)
|
||||
install -m 0755 $(TARGETS) $(DESTDIR)$(MODDIR)
|
||||
|
||||
create_install_dir:
|
||||
@@ -93,13 +103,17 @@ clean:
|
||||
@mkdir -p .depend
|
||||
@mkdir -p .depend/extra
|
||||
@mkdir -p extra
|
||||
$(CXX) $(MODFLAGS) $(LDFLAGS) $(MODLINK) -o $@ $< $($(basename $@)FLAGS) -MMD -MF .depend/$@.dep
|
||||
$(CXX) $(MODFLAGS) $(LDFLAGS) $(MODLINK) -o $@ $< $($(shell echo '$(basename $@)' | sed -e 's:extra/::')FLAGS) -MMD -MF .depend/$@.dep
|
||||
|
||||
modperl_install: create_install_dir
|
||||
for i in $(srcdir)/*.pm; do \
|
||||
install -m 0644 $$i $(DESTDIR)$(MODDIR); \
|
||||
done
|
||||
|
||||
modtcl_install:
|
||||
mkdir -p $(DESTDIR)$(DATADIR)/modtcl/
|
||||
install -m 0644 $(srcdir)/extra/modtcl.tcl $(srcdir)/extra/binds.tcl $(DESTDIR)$(DATADIR)/modtcl/
|
||||
|
||||
uninstall:
|
||||
# Yes, we are lazy, just remove everything in there
|
||||
rm -rf $(DESTDIR)$(MODDIR)/*
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
# 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.
|
||||
#
|
||||
# Binds module to process incoming messages with ZNC modtcl
|
||||
#
|
||||
# Supported bind types: pubm/pub, time, evnt, nick, bot, dcc, kick
|
||||
# evnt: prerehash,rehash,init-server,disconnect-server
|
||||
#
|
||||
|
||||
namespace eval Binds {
|
||||
|
||||
# vars
|
||||
if {![info exists Binds::List]} {
|
||||
variable List [list]
|
||||
}
|
||||
|
||||
# procs
|
||||
proc Add {type flags cmd {procname ""}} {
|
||||
if {![regexp {^(pub|pubm|nick|mode|raw|bot|time|evnt|dcc|kick)$} $type]} {
|
||||
PutModule "Tcl error: Bind type: $type not supported"
|
||||
return
|
||||
}
|
||||
# ToDo: Flags check from user info (IsAdmin, etc)
|
||||
if {$procname == ""} {
|
||||
PutModule "Tcl error: Without a proc binds are useless"
|
||||
return
|
||||
}
|
||||
# ToDo: bind hit counter
|
||||
if {[lsearch $Binds::List "$type $flags [list $cmd] 0 [list $procname]"] == -1} {
|
||||
lappend Binds::List "$type $flags [list $cmd] 0 [list $procname]"
|
||||
}
|
||||
return $cmd
|
||||
}
|
||||
|
||||
proc Del {type flags cmd procname} {
|
||||
if {[set match [lsearch [binds] "$type $flags $cmd 0 $procname"]] != -1 || [set match [lsearch [binds] "$type $flags {${cmd}} 0 $procname"]] != -1} {
|
||||
set Binds::List [lreplace $Binds::List $match $match]
|
||||
return $cmd
|
||||
} else {
|
||||
error "Tcl error: no such binding"
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessPubm {nick user handle channel text} {
|
||||
# Loop bind list and execute
|
||||
foreach n [binds pub] {
|
||||
if {[string match [lindex $n 2] [lindex [split $text] 0]]} {
|
||||
[lindex $n 4] $nick $user $handle $channel [lrange [join $text] 1 end]
|
||||
}
|
||||
}
|
||||
foreach {type flags mask hits proc} [join [binds pubm]] {
|
||||
regsub {^%} $mask {*} mask
|
||||
if {[ModuleLoaded crypt]} {regsub {^¤} $nick {} nick}
|
||||
if {[string match -nocase $mask "$channel $text"]} {
|
||||
$proc $nick $user $handle $channel $text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessTime {} {
|
||||
if {[clock format [clock seconds] -format "%S"] != 0} {return}
|
||||
set time [clock format [clock seconds] -format "%M %H %d %m %Y"]
|
||||
foreach {mi ho da mo ye} $time {}
|
||||
# Loop bind list and execute
|
||||
foreach n [binds time] {
|
||||
if {[string match [lindex $n 2] $time]} {
|
||||
[lindex $n 4] $mi $ho $da $mo $ye
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessEvnt {event} {
|
||||
foreach n [binds evnt] {
|
||||
if {[string match [lindex $n 2] $event]} {
|
||||
[lindex $n 4] $event
|
||||
}
|
||||
}
|
||||
switch $event {
|
||||
init-server {
|
||||
set ::botnick [GetCurNick]
|
||||
set ::server [GetServer]
|
||||
set ::server-online [expr [GetServerOnline] / 1000]
|
||||
}
|
||||
disconnect-server {
|
||||
set ::botnick ""
|
||||
set ::server ""
|
||||
set ::server-online 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessBot {from-bot cmd text} {
|
||||
foreach n [binds bot] {
|
||||
if {[string match [lindex $n 2] $cmd]} {
|
||||
[lindex $n 4] ${from-bot} $cmd $text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessNick {nick user handle channel newnick} {
|
||||
if {$nick == $::botnick} {set ::botnick $newnick}
|
||||
foreach n [binds nick] {
|
||||
if {[string match [lindex $n 2] $newnick]} {
|
||||
[lindex $n 4] $nick $user $handle $channel $newnick
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessKick {nick user handle channel target reason} {
|
||||
foreach n [binds kick] {
|
||||
if {[string match [lindex $n 2 0] $channel] && [string match [lindex $n 2 1] $target]} {
|
||||
[lindex $n 4] $nick $user $handle $channel $target $reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc ProcessDcc {handle idx text} {
|
||||
set match 0
|
||||
foreach n [binds dcc] {
|
||||
if {[string match -nocase [lindex $n 2] [string range [lindex $text 0] 1 end]]} {
|
||||
[lindex $n 4] $handle $idx [lrange $text 1 end]
|
||||
set match 1
|
||||
}
|
||||
}
|
||||
if {!$match} {
|
||||
PutModule "Error, dcc trigger '[string range [lindex $text 0] 1 end]' doesnt exist"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Provide aliases according to eggdrop specs
|
||||
proc ::bind {type flags cmd {procname ""}} {Binds::Add $type $flags $cmd $procname}
|
||||
proc ::unbind {type flags cmd procname} {Binds::Del $type $flags $cmd $procname}
|
||||
proc ::binds {{type ""}} {if {$type != ""} {set type "$type "};return [lsearch -all -inline $Binds::List "$type*"]}
|
||||
proc ::bindlist {{type ""}} {foreach bind $Binds::List {PutModule "$bind"}}
|
||||
|
||||
PutModule "modtcl script loaded: Binds v0.1"
|
||||
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "Chan.h"
|
||||
#include "IRCSock.h"
|
||||
#include "Modules.h"
|
||||
#include "Server.h"
|
||||
#include "User.h"
|
||||
#include "znc.h"
|
||||
|
||||
#include <tcl.h>
|
||||
|
||||
#define STDVAR (ClientData cd, Tcl_Interp *irp, int argc, const char *argv[])
|
||||
|
||||
#define BADARGS(nl, nh, example) do { \
|
||||
if ((argc < (nl)) || (argc > (nh))) { \
|
||||
Tcl_AppendResult(irp, "wrong # args: should be \"", \
|
||||
argv[0], (example), "\"", NULL); \
|
||||
return TCL_ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
class CModTcl;
|
||||
|
||||
class CModTclTimer : public CTimer {
|
||||
public:
|
||||
|
||||
CModTclTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
|
||||
virtual ~CModTclTimer() {}
|
||||
protected:
|
||||
virtual void RunJob();
|
||||
CModTcl* m_pParent;
|
||||
};
|
||||
|
||||
class CModTclStartTimer : public CTimer {
|
||||
public:
|
||||
|
||||
CModTclStartTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
|
||||
virtual ~CModTclStartTimer() {}
|
||||
protected:
|
||||
virtual void RunJob();
|
||||
CModTcl* m_pParent;
|
||||
};
|
||||
|
||||
|
||||
class CModTcl : public CModule {
|
||||
public:
|
||||
MODCONSTRUCTOR(CModTcl) {}
|
||||
|
||||
virtual ~CModTcl() {
|
||||
if (interp) {
|
||||
Tcl_DeleteInterp(interp);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool OnLoad(const CString& sArgs, CString& sErrorMsg) {
|
||||
interp = NULL;
|
||||
AddTimer(new CModTclStartTimer(this, 1, 1, "ModTclStarter", "Timer for modtcl to load the interpreter."));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Start() {
|
||||
CString sMyArgs = GetArgs();
|
||||
|
||||
interp = Tcl_CreateInterp();
|
||||
Tcl_Init(interp);
|
||||
Tcl_CreateCommand(interp, "Binds::ProcessPubm", tcl_Bind, this, NULL);
|
||||
Tcl_CreateCommand(interp, "Binds::ProcessTime", tcl_Bind, this, NULL);
|
||||
Tcl_CreateCommand(interp, "Binds::ProcessEvnt", tcl_Bind, this, NULL);
|
||||
Tcl_CreateCommand(interp, "Binds::ProcessNick", tcl_Bind, this, NULL);
|
||||
Tcl_CreateCommand(interp, "Binds::ProcessKick", tcl_Bind, this, NULL);
|
||||
Tcl_CreateCommand(interp, "PutIRC", tcl_PutIRC, this, NULL);
|
||||
Tcl_CreateCommand(interp, "PutIRCAs", tcl_PutIRCAs, this, NULL);
|
||||
Tcl_CreateCommand(interp, "PutModule", tcl_PutModule, this, NULL);
|
||||
Tcl_CreateCommand(interp, "PutStatus", tcl_PutStatus, this, NULL);
|
||||
Tcl_CreateCommand(interp, "PutStatusNotice", tcl_PutStatusNotice, this, NULL);
|
||||
Tcl_CreateCommand(interp, "PutUser", tcl_PutUser, this, NULL);
|
||||
|
||||
Tcl_CreateCommand(interp, "GetLocalIP", tcl_GetLocalIP, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetCurNick", tcl_GetCurNick, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetUsername", tcl_GetUsername, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetRealName", tcl_GetRealName, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetVHost", tcl_GetVHost, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetChans", tcl_GetChans, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetChannelUsers", tcl_GetChannelUsers, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetChannelModes", tcl_GetChannelModes, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetServer", tcl_GetServer, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetServerOnline", tcl_GetServerOnline, this, NULL);
|
||||
Tcl_CreateCommand(interp, "GetModules", tcl_GetModules, this, NULL);
|
||||
|
||||
Tcl_CreateCommand(interp, "exit", tcl_exit, this, NULL);
|
||||
|
||||
if (!sMyArgs.empty()) {
|
||||
i = Tcl_EvalFile(interp, sMyArgs.c_str());
|
||||
if (i != TCL_OK) {
|
||||
result = interp->result;
|
||||
PutModule(CString(result));
|
||||
}
|
||||
}
|
||||
|
||||
AddTimer(new CModTclTimer(this, 1, 0, "ModTclUpdate", "Timer for modtcl to process pending events and idle callbacks."));
|
||||
}
|
||||
|
||||
virtual void OnModCommand(const CString& sCommand) {
|
||||
CString sResult;
|
||||
VCString vsResult;
|
||||
CString sCmd = sCommand;
|
||||
|
||||
if (sCmd.Token(0).CaseCmp(".tcl") == 0)
|
||||
sCmd = sCmd.Token(1,true);
|
||||
|
||||
if (sCmd.Left(1).CaseCmp(".") == 0)
|
||||
sCmd = "Binds::ProcessDcc - - {" + sCmd + "}";
|
||||
|
||||
Tcl_Eval(interp, sCmd.c_str());
|
||||
|
||||
sResult = CString(Tcl_GetStringResult(interp));
|
||||
if (!sResult.empty()) {
|
||||
sResult.Split("\n", vsResult);
|
||||
unsigned int a = 0;
|
||||
for (a = 0; a < vsResult.size(); a++)
|
||||
PutModule(vsResult[a].TrimRight_n());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TclUpdate() {
|
||||
while (Tcl_DoOneEvent(TCL_DONT_WAIT)) {}
|
||||
i = Tcl_Eval(interp,"Binds::ProcessTime");
|
||||
if (i != TCL_OK) {
|
||||
result = interp->result;
|
||||
PutModule(CString(result));
|
||||
}
|
||||
}
|
||||
|
||||
CString TclEscape(CString sLine) {
|
||||
sLine.Replace("\\","\\\\");
|
||||
sLine.Replace("{","\\{");
|
||||
sLine.Replace("}","\\}");
|
||||
return sLine;
|
||||
}
|
||||
|
||||
virtual void OnPreRehash() {
|
||||
if (interp)
|
||||
Tcl_Eval(interp,"Binds::ProcessEvnt prerehash");
|
||||
}
|
||||
|
||||
virtual void OnPostRehash() {
|
||||
if (interp) {
|
||||
Tcl_Eval(interp,"rehash");
|
||||
Tcl_Eval(interp,"Binds::ProcessEvnt rehash");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnIRCConnected() {
|
||||
if (interp)
|
||||
Tcl_Eval(interp, "Binds::ProcessEvnt init-server");
|
||||
}
|
||||
|
||||
virtual void OnIRCDisconnected() {
|
||||
if (interp)
|
||||
Tcl_Eval(interp, "Binds::ProcessEvnt disconnect-server");
|
||||
}
|
||||
|
||||
virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
|
||||
CString sMes = TclEscape(sMessage);
|
||||
CString sNick = TclEscape(CString(Nick.GetNick()));
|
||||
CString sHost = TclEscape(CString(Nick.GetIdent() + "@" + Nick.GetHost()));
|
||||
CString sChannel = TclEscape(CString(Channel.GetName()));
|
||||
|
||||
CString sCommand = "Binds::ProcessPubm {" + sNick + "} {" + sHost + "} - {" + sChannel + "} {" + sMes + "}";
|
||||
i = Tcl_Eval(interp, sCommand.c_str());
|
||||
if (i != TCL_OK) {
|
||||
result = interp->result;
|
||||
PutModule(CString(result));
|
||||
}
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) {
|
||||
CString sOldNick = TclEscape(CString(OldNick.GetNick()));
|
||||
CString sNewNickTmp = TclEscape(sNewNick);
|
||||
CString sHost = TclEscape(CString(OldNick.GetIdent() + "@" + OldNick.GetHost()));
|
||||
|
||||
CString sCommand;
|
||||
// Nick change is triggered for each common chan so that binds can be chan specific
|
||||
unsigned int nLength = vChans.size();
|
||||
for (unsigned int n = 0; n < nLength; n++) {
|
||||
sCommand = "Binds::ProcessNick {" + sOldNick + "} {" + sHost + "} - {" + vChans[n]->GetName() + "} {" + sNewNickTmp + "}";
|
||||
i = Tcl_Eval(interp, sCommand.c_str());
|
||||
if (i != TCL_OK) {
|
||||
result = interp->result;
|
||||
PutModule(CString(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {
|
||||
CString sOpNick = TclEscape(CString(OpNick.GetNick()));
|
||||
CString sNick = TclEscape(sKickedNick);
|
||||
CString sOpHost = TclEscape(CString(OpNick.GetIdent() + "@" + OpNick.GetHost()));
|
||||
|
||||
CString sCommand = "Binds::ProcessKick {" + sOpNick + "} {" + sOpHost + "} - {" + Channel.GetName() + "} {" + sNick + "} {" + sMessage + "}";
|
||||
i = Tcl_Eval(interp, sCommand.c_str());
|
||||
if (i != TCL_OK) {
|
||||
result = interp->result;
|
||||
PutModule(CString(result));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Tcl_Interp *interp;
|
||||
int i;
|
||||
char *result;
|
||||
|
||||
static CString argvit(const char *argv[], unsigned int end, unsigned int begin, CString delim) {
|
||||
CString sRet;
|
||||
unsigned int i;
|
||||
if (begin < end)
|
||||
sRet = CString(argv[begin]);
|
||||
|
||||
for (i = begin + 1; i < end; i++) {
|
||||
sRet = sRet + delim + CString(argv[i]);
|
||||
}
|
||||
|
||||
return sRet;
|
||||
}
|
||||
|
||||
// Placeholder for binds incase binds.tcl isn't used
|
||||
static int tcl_Bind STDVAR {return TCL_OK;}
|
||||
|
||||
static int tcl_GetLocalIP STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
Tcl_SetResult(irp, (char *)mod->m_pUser->GetLocalIP().c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetCurNick STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
Tcl_SetResult(irp, (char *)mod->m_pUser->GetCurNick().c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetUsername STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
Tcl_SetResult(irp, (char *)mod->m_pUser->GetUserName().c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetRealName STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
Tcl_SetResult(irp, (char *)mod->m_pUser->GetRealName().c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetVHost STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
Tcl_SetResult(irp, (char *)mod->m_pUser->GetVHost().c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetChans STDVAR {
|
||||
char *p;
|
||||
const char *l[1];
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(1, 1, "");
|
||||
|
||||
const vector<CChan*>& Channels = mod->m_pUser->GetChans();
|
||||
for (unsigned int c = 0; c < Channels.size(); c++) {
|
||||
CChan* pChan = Channels[c];
|
||||
l[0] = pChan->GetName().c_str();
|
||||
p = Tcl_Merge(1, l);
|
||||
Tcl_AppendElement(irp, p);
|
||||
Tcl_Free((char *)p);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetChannelUsers STDVAR {
|
||||
char *p;
|
||||
const char *l[4];
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " channel");
|
||||
|
||||
CString sChannel = argvit(argv, argc, 1, " ");
|
||||
CChan *pChannel = mod->m_pUser->FindChan(sChannel);
|
||||
|
||||
if (!pChannel) {
|
||||
CString sMsg = "invalid channel: " + sChannel;
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
const map<CString,CNick*>& msNicks = pChannel->GetNicks();
|
||||
for (map<CString,CNick*>::const_iterator it = msNicks.begin(); it != msNicks.end(); it++) {
|
||||
const CNick& Nick = *it->second;
|
||||
l[0] = (Nick.GetNick()).c_str();
|
||||
l[1] = (Nick.GetIdent()).c_str();
|
||||
l[2] = (Nick.GetHost()).c_str();
|
||||
l[3] = (Nick.GetPermStr()).c_str();
|
||||
p = Tcl_Merge(4, l);
|
||||
Tcl_AppendElement(irp, p);
|
||||
Tcl_Free((char *)p);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetChannelModes STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " channel");
|
||||
|
||||
CString sChannel = argvit(argv, argc, 1, " ");
|
||||
CChan *pChannel = mod->m_pUser->FindChan(sChannel);
|
||||
CString sMsg;
|
||||
|
||||
if (!pChannel) {
|
||||
sMsg = "invalid channel: " + sChannel;
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
sMsg = pChannel->GetModeString();
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetServer STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
CServer* pServer = mod->m_pUser->GetCurrentServer();
|
||||
CString sMsg;
|
||||
if (pServer)
|
||||
sMsg = pServer->GetName() + ":" + CString(pServer->GetPort());
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetServerOnline STDVAR {
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
CIRCSock* pIRCSock = mod->m_pUser->GetIRCSock();
|
||||
CString sMsg = "0";
|
||||
if (pIRCSock)
|
||||
sMsg = CString(pIRCSock->GetStartTime());
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_GetModules STDVAR {
|
||||
char *p;
|
||||
const char *l[3];
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(1, 1, "");
|
||||
|
||||
CModules& GModules = CZNC::Get().GetModules();
|
||||
CModules& Modules = mod->m_pUser->GetModules();
|
||||
|
||||
for (unsigned int b = 0; b < GModules.size(); b++) {
|
||||
l[0] = GModules[b]->GetModName().c_str();
|
||||
l[1] = GModules[b]->GetArgs().c_str();
|
||||
l[2] = "1"; // IsGlobal
|
||||
p = Tcl_Merge(3, l);
|
||||
Tcl_AppendElement(irp, p);
|
||||
Tcl_Free((char *)p);
|
||||
}
|
||||
for (unsigned int b = 0; b < Modules.size(); b++) {
|
||||
l[0] = Modules[b]->GetModName().c_str();
|
||||
l[1] = Modules[b]->GetArgs().c_str();
|
||||
l[2] = "0"; // IsGlobal
|
||||
p = Tcl_Merge(3, l);
|
||||
Tcl_AppendElement(irp, p);
|
||||
Tcl_Free((char *)p);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_PutIRC STDVAR {
|
||||
CString sMsg;
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " string");
|
||||
sMsg = argvit(argv, argc, 1, " ");
|
||||
mod->m_pUser->PutIRC(sMsg);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_PutIRCAs STDVAR {
|
||||
CString sMsg;
|
||||
|
||||
BADARGS(3, 999, " user string");
|
||||
|
||||
CUser *pUser = CZNC::Get().FindUser(argv[1]);
|
||||
if (!pUser) {
|
||||
sMsg = "invalid user " + CString(argv[1]);
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
sMsg = argvit(argv, argc, 2, " ");
|
||||
pUser->PutIRC(sMsg);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_PutModule STDVAR {
|
||||
CString sMsg;
|
||||
VCString vsMsg;
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " string");
|
||||
sMsg = argvit(argv, argc, 1, " ");
|
||||
//mod->PutModule(sMsg);
|
||||
sMsg.Split("\n", vsMsg);
|
||||
unsigned int a = 0;
|
||||
for (a = 0; a < vsMsg.size(); a++)
|
||||
mod->PutModule(vsMsg[a].TrimRight_n());
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_PutStatus STDVAR {
|
||||
CString sMsg;
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " string");
|
||||
sMsg = argvit(argv, argc, 1, " ");
|
||||
mod->PutStatus(sMsg);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_PutStatusNotice STDVAR {
|
||||
CString sMsg;
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " string");
|
||||
sMsg = argvit(argv, argc, 1, " ");
|
||||
mod->m_pUser->PutStatusNotice(sMsg);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_PutUser STDVAR {
|
||||
CString sMsg;
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(2, 999, " string");
|
||||
sMsg = argvit(argv, argc, 1, " ");
|
||||
mod->m_pUser->PutUser(sMsg);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_exit STDVAR {
|
||||
CString sMsg;
|
||||
CModTcl *mod = static_cast<CModTcl *>(cd);
|
||||
|
||||
BADARGS(1, 2, " ?reason?");
|
||||
|
||||
if (! mod->m_pUser->IsAdmin()) {
|
||||
sMsg = "You need to be administrator to shutdown the bnc.";
|
||||
Tcl_SetResult(irp, (char*)sMsg.c_str(), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if (argc > 1) {
|
||||
sMsg = argvit(argv, argc, 1, " ");
|
||||
CZNC::Get().Broadcast(sMsg);
|
||||
usleep(100000); // Sleep for 10ms to attempt to allow the previous Broadcast() to go through to all users
|
||||
}
|
||||
|
||||
throw CException(CException::EX_Shutdown);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void CModTclTimer::RunJob() {
|
||||
CModTcl *p = (CModTcl *)m_pModule;
|
||||
if (p)
|
||||
p->TclUpdate();
|
||||
}
|
||||
|
||||
void CModTclStartTimer::RunJob() {
|
||||
CModTcl *p = (CModTcl *)m_pModule;
|
||||
if (p)
|
||||
p->Start();
|
||||
}
|
||||
|
||||
MODULEDEFS(CModTcl, "Loads Tcl scripts as ZNC modules");
|
||||
@@ -0,0 +1,122 @@
|
||||
# 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.
|
||||
#
|
||||
# TCL master file for ZNC modtcl
|
||||
#
|
||||
# Usage: LoadModule = modtcl /path/to/this/file
|
||||
#
|
||||
|
||||
# rehash simply reloads this file and shows info for any errors
|
||||
set ::MasterFile [info script]
|
||||
proc rehash {} {
|
||||
if {[catch {source $::MasterFile}]} {
|
||||
PutModule "Error rehashing tcl master file: $::errorInfo"
|
||||
}
|
||||
}
|
||||
proc bgerror {message} {
|
||||
PutModule "Background error: $::errorInfo"
|
||||
}
|
||||
|
||||
# set basic eggdrop style variables
|
||||
set ::botnet-nick ZNC_[GetUsername]
|
||||
set ::botnick [GetCurNick]
|
||||
set ::server [GetServer]
|
||||
set ::server-online [expr [GetServerOnline] / 1000]
|
||||
|
||||
# add some eggdrop style procs
|
||||
proc putlog message {PutModule $message}
|
||||
proc puthelp {text {option ""}} {
|
||||
if {[regexp -nocase {^(?:privmsg|notice) (\S+) :(.*)} $text . target line]} {
|
||||
if {$target == "*modtcl"} {PutModule $line; return}
|
||||
if {$target == "*status"} {PutStatus $line; return}
|
||||
if {[botonchan $target]} {PutUser ":$::botnick![getchanhost $::botnick] $text"}
|
||||
}
|
||||
PutIRC $text
|
||||
}
|
||||
proc putserv {text {option ""}} {puthelp $text}
|
||||
proc putquick {text {option ""}} {puthelp $text}
|
||||
proc stripcodes {flags args} {return [regsub -all {\xF|\x2|\x16|\x1F|\x7|\x3([0-9]{1,2})?(,[0-9]{1,2})?} [join $args] {}]}
|
||||
proc unixtime {} {return [clock seconds]}
|
||||
proc duration {s} {
|
||||
set ret ""
|
||||
foreach {n m} "year 31536000 week 604800 day 86400 hour 3600 minute 60 second 1" {
|
||||
if {$s >= $m} {
|
||||
set tmp [expr $s / $m]
|
||||
if {$tmp == 1} {set i ""} else {set i "s"}
|
||||
set ret $ret[format "%lu %s%s " $tmp $n $i]
|
||||
incr s -[expr $tmp * $m]
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
proc encrypt {key string} {return [bencrypt $key $string]}
|
||||
proc decrypt {key string} {return [bdecrypt $key $string]}
|
||||
proc channels {} {return [GetChans]}
|
||||
proc chanlist {channel {flags ""}} {
|
||||
set ret ""
|
||||
foreach u [GetChannelUsers [string trim $channel "{}"]] {lappend ret [lindex $u 0]}
|
||||
return $ret
|
||||
}
|
||||
proc getchanmode channel {return [GetChannelModes [string trim $channel "{}"]]}
|
||||
proc getchanhost {nick {channel ""}} {
|
||||
# TODO: to have full info here we need to use /who #chan when we join
|
||||
if {$channel == ""} {set channel [join [channels]]}
|
||||
foreach c $channel {
|
||||
foreach u [GetChannelUsers $c] {
|
||||
if {[string match $nick [lindex $u 0]]} {
|
||||
return "[lindex $u 1]@[lindex $u 2]"
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
proc onchan {nick chan} {return [expr [lsearch -exact [string tolower [chanlist $chan]] [string tolower $nick]] != -1]}
|
||||
proc botonchan channel {return [onchan $::botnick $channel]}
|
||||
|
||||
proc isop {nick {channel ""}} {return [PermCheck $nick "@" $channel]}
|
||||
proc isvoice {nick {channel ""}} {return [PermCheck $nick "+" $channel]}
|
||||
proc botisop {{channel ""}} {return [isop $::botnick $channel]}
|
||||
proc botisvoice {{channel ""}} {return [isvoice $::botnick $channel]}
|
||||
|
||||
proc PermCheck {nick perm channel} {
|
||||
if {$channel == ""} {set channel [channels]}
|
||||
if {[ModuleLoaded crypt]} {regsub {^¤} $nick {} nick}
|
||||
foreach c $channel {
|
||||
foreach u [GetChannelUsers $c] {
|
||||
if {[string match -nocase $nick [lindex $u 0]] && [string match *$perm* [lindex $u 3]]} {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
proc ModuleLoaded modname {
|
||||
foreach {mod args glob} [join [GetModules]] {
|
||||
if {[string match -nocase $modname $mod]} {return 1}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
# rewrite all timers to use the after command
|
||||
proc utimer {seconds tcl-command} {after [expr $seconds * 1000] ${tcl-command}}
|
||||
proc timer {minutes tcl-command} {after [expr $minutes * 60 * 1000] ${tcl-command}}
|
||||
proc utimers {} {set t {}; foreach a [after info] {lappend t "0 [lindex [after info $a] 0] $a"}; return $t}
|
||||
proc timers {} {set t {}; foreach a [after info] {lappend t "0 [lindex [after info $a] 0] $a"}; return $t}
|
||||
proc killtimer id {return [after cancel $id]}
|
||||
proc killutimer id {return [after cancel $id]}
|
||||
|
||||
# pseudo procs to satisfy script loading, no functionality
|
||||
proc setudef {type name} {return}
|
||||
proc modules {} {return "encryption"}
|
||||
proc channel {command {options ""}} {return ""}
|
||||
proc queuesize {{queue ""}} {return 0}
|
||||
proc matchattr {handle flags {channel ""}} {return 0}
|
||||
|
||||
# load other script files
|
||||
source [file dirname $::MasterFile]/binds.tcl
|
||||
|
||||
|
||||
PutModule "Succesfully loaded modtcl with master file: [info script]"
|
||||
Reference in New Issue
Block a user