diff --git a/configure b/configure index 8eaae55b..138ea216 100755 --- a/configure +++ b/configure @@ -556,6 +556,7 @@ PACKAGE_URL='' ac_unique_file="znc.cpp" ac_subst_vars='LTLIBOBJS LIBOBJS +PERL DATADIR MODDIR LIBICONV @@ -568,8 +569,9 @@ MODLINK LIBZNCDIR LIBZNC MODFLAGS -SWIG_BINARY +PYTHONCFG_BINARY PERL_BINARY +SWIG_BINARY openssl_LIBS openssl_CFLAGS c_ares_LIBS @@ -643,6 +645,7 @@ enable_debug enable_ipv6 enable_openssl enable_perl +enable_python enable_swig enable_sasl enable_optimization @@ -1292,12 +1295,18 @@ Optional Features: --disable-ipv6 disable ipv6 support --disable-openssl disable openssl --enable-perl enable perl + --enable-python[=python3-config] + enable python. By default python3-config is used, + but you can use another name if your python3-config + has another name. --disable-swig Disable automatic generation of source files needed - for modperl. This value is ignored if perl is - disabled or not found. If you disable swig, but - still want to use modperl, please download the - necessary files to modules/modperl. Check - http://en.znc.in/wiki/Modperl for details. + for modperl/modpython. This value is ignored if perl + and python are disabled. If you disable swig, but + still want to use these modules, please download the + necessary files to modules/modperl and/or to + modules/modpython. Check + http://en.znc.in/wiki/Modperl and + http://en.znc.in/wiki/Modpython for details. --enable-sasl enable sasl --disable-optimization Disable some compiler optimizations to decrease memory usage while compiling @@ -2926,6 +2935,13 @@ else PERL="no" fi +# Check whether --enable-python was given. +if test "${enable_python+set}" = set; then : + enableval=$enable_python; PYTHON="$enableval" +else + PYTHON="no" +fi + # Check whether --enable-swig was given. if test "${enable_swig+set}" = set; then : enableval=$enable_swig; SWIG="$enableval" @@ -3675,9 +3691,76 @@ else # (dynamic_lookup might only work on 10.4 and later) fi +if test "x$PERL" != xno -o "x$PYTHON" != xno; then + old_SWIG="$SWIG" + if test "x$SWIG" != "xno"; then + # Extract the first word of "swig", so it can be a program name with args. +set dummy swig; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_SWIG_BINARY+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + case $SWIG_BINARY in + [\\/]* | ?:[\\/]*) + ac_cv_path_SWIG_BINARY="$SWIG_BINARY" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_SWIG_BINARY="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +SWIG_BINARY=$ac_cv_path_SWIG_BINARY +if test -n "$SWIG_BINARY"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_BINARY" >&5 +$as_echo "$SWIG_BINARY" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -z "$SWIG_BINARY" && SWIG=no + fi + if test "x$SWIG" = xno; then + SWIG_BINARY="" + if test "x$old_SWIG" = xno; then + SWIG="no, need manual download for perl/python bindings" + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: swig was disabled, so you should put needed sources to modules/ +Please check http://en.znc.in/wiki/Modperl or http://en.znc.in/wiki/Modpython for details." >&5 +$as_echo "$as_me: WARNING: swig was disabled, so you should put needed sources to modules/ +Please check http://en.znc.in/wiki/Modperl or http://en.znc.in/wiki/Modpython for details." >&2;} + else + as_fn_error $? "swig was not found. Try --disable-perl --disable-python, or download the necessary sources and use --disable-swig. +Please check http://en.znc.in/wiki/Modperl or http://en.znc.in/wiki/Modpython for details." "$LINENO" 5 + fi + else + SWIG=yes + fi +else + if test "x$SWIG" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: swig is needed only for perl and python, but both are disabled. Disabling swig." >&5 +$as_echo "$as_me: WARNING: swig is needed only for perl and python, but both are disabled. Disabling swig." >&2;} + fi + SWIG=no +fi + if test "x$PERL" != "xno"; then old_PERL="$PERL" - old_SWIG="$SWIG" # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -3763,53 +3846,6 @@ else PERL="no" fi - if test "x$PERL" != "xno"; then - if test "x$SWIG" != "xno"; then - # Extract the first word of "swig", so it can be a program name with args. -set dummy swig; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_path_SWIG_BINARY+set}" = set; then : - $as_echo_n "(cached) " >&6 -else - case $SWIG_BINARY in - [\\/]* | ?:[\\/]*) - ac_cv_path_SWIG_BINARY="$SWIG_BINARY" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_path_SWIG_BINARY="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -SWIG_BINARY=$ac_cv_path_SWIG_BINARY -if test -n "$SWIG_BINARY"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG_BINARY" >&5 -$as_echo "$SWIG_BINARY" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - if test -z "$SWIG_BINARY"; then - SWIG="no" - fi - fi - fi LDFLAGS="$my_saved_LDFLAGS" else PERL="no" @@ -3827,22 +3863,131 @@ $as_echo "$as_me: WARNING: perl was not found and thus disabled" >&2;} PERL_BINARY="" else - if test "x$SWIG" = "xno"; then - SWIG_BINARY="" - if test "x$old_SWIG" = "xno"; then - PERL="yes, but with manual download" - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: swig was disabled, so you should put needed sources for modperl to modules/modperl. -Please check http://en.znc.in/wiki/Modperl for details." >&5 -$as_echo "$as_me: WARNING: swig was disabled, so you should put needed sources for modperl to modules/modperl. -Please check http://en.znc.in/wiki/Modperl for details." >&2;} - else - as_fn_error $? "swig was not found. Try --disable-perl, or download the necessary sources and use --disable-swig. -Please check http://en.znc.in/wiki/Modperl for details." "$LINENO" 5 + PERL="yes" + fi +fi + +if test "x$PYTHON" != "xno"; then + # Default value for just --enable-python + if test "x$PYTHON" = "xyes"; then + PYTHON="python3-config" + fi + old_PYTHON="$PYTHON" + # Extract the first word of ""$PYTHON"", so it can be a program name with args. +set dummy "$PYTHON"; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_PYTHONCFG_BINARY+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHONCFG_BINARY in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHONCFG_BINARY="$PYTHONCFG_BINARY" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PYTHONCFG_BINARY="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PYTHONCFG_BINARY=$ac_cv_path_PYTHONCFG_BINARY +if test -n "$PYTHONCFG_BINARY"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHONCFG_BINARY" >&5 +$as_echo "$PYTHONCFG_BINARY" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -n "$PYTHONCFG_BINARY"; then + my_saved_LDFLAGS="$LDFLAGS" + appendLD `$PYTHONCFG_BINARY --includes` + appendLD `$PYTHONCFG_BINARY --ldflags` + ac_fn_cxx_check_func "$LINENO" "Py_Initialize" "ac_cv_func_Py_Initialize" +if test "x$ac_cv_func_Py_Initialize" = x""yes; then : + +else + PYTHON="no" +fi + + if test "x$PYTHON" != "xno"; then + # Yes, modpython depends on perl. + # Extract the first word of "perl", so it can be a program name with args. +set dummy perl; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_PERL_BINARY+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + case $PERL_BINARY in + [\\/]* | ?:[\\/]*) + ac_cv_path_PERL_BINARY="$PERL_BINARY" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PERL_BINARY="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PERL_BINARY=$ac_cv_path_PERL_BINARY +if test -n "$PERL_BINARY"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL_BINARY" >&5 +$as_echo "$PERL_BINARY" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -z "$PERL_BINARY"; then + as_fn_error $? "To compile modpython you need to be able to execute perl scripts. Try --disable-python or install perl." "$LINENO" 5 fi - else - PERL="yes" - SWIG="yes" fi + LDFLAGS="$my_saved_LDFLAGS" + else + PYTHON="no" + fi + if test "x$PYTHON" = "xno"; then + + # This looks better in the summary at the end + PYTHON="not found" + if test "x$old_PYTHON" != "xauto" ; then + as_fn_error $? "python not found. Try --disable-python." "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: python was not found and thus disabled" >&5 +$as_echo "$as_me: WARNING: python was not found and thus disabled" >&2;} + fi + + PYTHONCFG_BINARY="" + else + PYTHON="yes" fi fi @@ -4085,6 +4230,7 @@ appendCXX "-D_DATADIR_=\\\"${DATADIR}\\\"" + ac_config_files="$ac_config_files Makefile" ac_config_files="$ac_config_files znc-config" @@ -5273,6 +5419,8 @@ echo "ipv6: $IPV6" echo "openssl: $SSL" echo "c-ares: $ARES" echo "perl: $PERL" +echo "python: $PYTHON" +echo "swig: $SWIG" if test x"$SASL" = "x" ; then echo "sasl: no" else diff --git a/configure.in b/configure.in index 27c28897..65f0221b 100644 --- a/configure.in +++ b/configure.in @@ -96,11 +96,17 @@ AC_ARG_ENABLE( [perl], AS_HELP_STRING([--enable-perl], [enable perl]), [PERL="$enableval"], [PERL="no"]) +AC_ARG_ENABLE( [python], + AS_HELP_STRING([--enable-python[[[=python3-config]]]], [enable python. + By default python3-config is used, but you can use + another name if your python3-config has another name.]), + [PYTHON="$enableval"], + [PYTHON="no"]) AC_ARG_ENABLE( [swig], - AS_HELP_STRING([--disable-swig], [Disable automatic generation of source files needed for modperl. - This value is ignored if perl is disabled or not found. If you disable swig, - but still want to use modperl, please download the necessary files to modules/modperl. - Check http://en.znc.in/wiki/Modperl for details.]), + AS_HELP_STRING([--disable-swig], [Disable automatic generation of source files needed for modperl/modpython. + This value is ignored if perl and python are disabled. If you disable swig, + but still want to use these modules, please download the necessary files to modules/modperl and/or to modules/modpython. + Check http://en.znc.in/wiki/Modperl and http://en.znc.in/wiki/Modpython for details.]), [SWIG="$enableval"], [SWIG="auto"]) AC_ARG_ENABLE( [sasl], @@ -270,9 +276,34 @@ else # (dynamic_lookup might only work on 10.4 and later) fi +if test "x$PERL" != xno -o "x$PYTHON" != xno; then + old_SWIG="$SWIG" + if test "x$SWIG" != "xno"; then + AC_PATH_PROG([SWIG_BINARY], [swig]) + test -z "$SWIG_BINARY" && SWIG=no + fi + if test "x$SWIG" = xno; then + SWIG_BINARY="" + if test "x$old_SWIG" = xno; then + SWIG="no, need manual download for perl/python bindings" + AC_MSG_WARN([swig was disabled, so you should put needed sources to modules/ +Please check http://en.znc.in/wiki/Modperl or http://en.znc.in/wiki/Modpython for details.]) + else + AC_MSG_ERROR([swig was not found. Try --disable-perl --disable-python, or download the necessary sources and use --disable-swig. +Please check http://en.znc.in/wiki/Modperl or http://en.znc.in/wiki/Modpython for details.]) + fi + else + SWIG=yes + fi +else + if test "x$SWIG" = "xyes"; then + AC_MSG_WARN([swig is needed only for perl and python, but both are disabled. Disabling swig.]) + fi + SWIG=no +fi + if test "x$PERL" != "xno"; then old_PERL="$PERL" - old_SWIG="$SWIG" AC_PATH_PROG([PERL_BINARY], [perl], []) if test -n "$PERL_BINARY" && eval "$PERL_BINARY -e'use 5.010'"; then my_saved_LDFLAGS="$LDFLAGS" @@ -280,14 +311,6 @@ if test "x$PERL" != "xno"; then AC_CHECK_LIB(perl, perl_alloc, [: No, we do not want autoconf to do sth automatically], PERL="no") - if test "x$PERL" != "xno"; then - if test "x$SWIG" != "xno"; then - AC_PATH_PROG([SWIG_BINARY], [swig], []) - if test -z "$SWIG_BINARY"; then - SWIG="no" - fi - fi - fi LDFLAGS="$my_saved_LDFLAGS" else PERL="no" @@ -298,20 +321,40 @@ if test "x$PERL" != "xno"; then [perl was not found and thus disabled]) PERL_BINARY="" else - if test "x$SWIG" = "xno"; then - SWIG_BINARY="" - if test "x$old_SWIG" = "xno"; then - PERL="yes, but with manual download" - AC_MSG_WARN([swig was disabled, so you should put needed sources for modperl to modules/modperl. -Please check http://en.znc.in/wiki/Modperl for details.]) - else - AC_MSG_ERROR([swig was not found. Try --disable-perl, or download the necessary sources and use --disable-swig. -Please check http://en.znc.in/wiki/Modperl for details.]) + PERL="yes" + fi +fi + +if test "x$PYTHON" != "xno"; then + # Default value for just --enable-python + if test "x$PYTHON" = "xyes"; then + PYTHON="python3-config" + fi + old_PYTHON="$PYTHON" + AC_PATH_PROG([PYTHONCFG_BINARY], ["$PYTHON"]) + if test -n "$PYTHONCFG_BINARY"; then + my_saved_LDFLAGS="$LDFLAGS" + appendLD `$PYTHONCFG_BINARY --includes` + appendLD `$PYTHONCFG_BINARY --ldflags` + AC_CHECK_FUNC([Py_Initialize], [], [PYTHON="no"]) + if test "x$PYTHON" != "xno"; then + # Yes, modpython depends on perl. + AC_PATH_PROG([PERL_BINARY], [perl]) + if test -z "$PERL_BINARY"; then + AC_MSG_ERROR([To compile modpython you need to be able to execute perl scripts. Try --disable-python or install perl.]) fi - else - PERL="yes" - SWIG="yes" fi + LDFLAGS="$my_saved_LDFLAGS" + else + PYTHON="no" + fi + if test "x$PYTHON" = "xno"; then + ZNC_AUTO_FAIL([PYTHON], + [python not found. Try --disable-python.], + [python was not found and thus disabled]) + PYTHONCFG_BINARY="" + else + PYTHON="yes" fi fi @@ -435,6 +478,8 @@ echo "ipv6: $IPV6" echo "openssl: $SSL" echo "c-ares: $ARES" echo "perl: $PERL" +echo "python: $PYTHON" +echo "swig: $SWIG" if test x"$SASL" = "x" ; then echo "sasl: no" else diff --git a/modules/Makefile.in b/modules/Makefile.in index c43305be..0d9344ad 100644 --- a/modules/Makefile.in +++ b/modules/Makefile.in @@ -23,6 +23,7 @@ LDFLAGS := @LDFLAGS@ # LIBS := @LIBS@ PERL_ON := @PERL@ PERL := @PERL_BINARY@ +PYCFG := @PYTHONCFG_BINARY@ SWIG := @SWIG_BINARY@ MODDIR := @MODDIR@ DATADIR := @DATADIR@ @@ -81,6 +82,16 @@ else FILES := $(shell echo $(FILES) | sed -e "s/modperl//") endif +ifneq "$(PYCFG)" "" +PYTHONCOMMON := $(shell $(PYCFG) --includes) $(shell $(PYCFG) --ldflags) +PYTHONCOMMON += -DSWIG_TYPE_TABLE=znc +PYTHONCOMMON += -Wno-missing-field-initializers -Wno-unused +modpythonFLAGS := $(PYTHONCOMMON) -I. +PYTHONHOOK := modpython_install +else +FILES := $(shell echo $(FILES) | sed -e "s/modpython//") +endif + ifeq "@SASL@" "" FILES := $(shell echo $(FILES) | sed -e "s:extra/saslauth::") endif @@ -101,14 +112,16 @@ modtclFLAGS := $(TCL_FLAGS) TARGETS := $(addsuffix .so, $(FILES)) -CLEAN := *.so extra/*.so modperl/ZNC.so modperl/modperl_wrap.cxx modperl/ZNC.pm +CLEAN := *.so extra/*.so +CLEAN += modperl/ZNC.so modperl/ZNC.pm modpython/functions.cpp modpython/compiler +CLEAN += modpython/_znc_core.so modpython/_znc_core.cpp modpython/znc_core.py modpython/znc_core.pyc .PHONY: all clean install install_metadirs create_install_dir uninstall -.PHONY: modperl_install modtcl_install +.PHONY: modperl_install modtcl_install modpython_install modpython_compilepyc all: $(TARGETS) -install: all create_install_dir install_metadirs $(PERLHOOK) $(TCLHOOK) +install: all create_install_dir install_metadirs $(PERLHOOK) $(TCLHOOK) $(PYTHONHOOK) $(INSTALL_PROGRAM) $(TARGETS) $(DESTDIR)$(MODDIR) create_install_dir: @@ -149,6 +162,23 @@ modperl/ZNC.cpp: modperl/modperl.i Makefile modperl/module.h modperl/CString.i $(Q)rm -rf $@.warn endif +modpython.so: modpython/_znc_core.so modpython/swigpyrun.h +modpython/_znc_core.so: modpython/_znc_core.cpp Makefile modpython/functions.cpp + $(E) Building ZNC python bindings library... + $(Q)$(CXX) $(MODFLAGS) $(LDFLAGS) $(MODLINK) -I$(srcdir) $(PYTHONCOMMON) -o $@ $< -Wno-shadow +ifneq "$(SWIG)" "" +modpython/swigpyrun.h: + @mkdir -p modpython + $(Q)$(SWIG) -python -py3 -c++ -shadow -external-runtime $@ +modpython/_znc_core.cpp: modpython/modpython.i Makefile + $(E) Generating ZNC API for python... + @mkdir -p modpython + $(Q)$(SWIG) -python -py3 -c++ -shadow -outdir modpython -I$(srcdir) -w362,315,401 -o $@ $< +endif +modpython/functions.cpp: modpython/functions.in + @mkdir -p modpython + $(Q)$(PY_PERL) $(srcdir)/modpython/codegen.pl $< $@ + modperl_install: create_install_dir install_metadirs +for i in $(srcdir)/*.pm; do \ test -r $$i && $(INSTALL_DATA) $$i $(DESTDIR)$(MODDIR); \ @@ -165,6 +195,32 @@ modperl_install: create_install_dir install_metadirs $(INSTALL_DATA) modperl/ZNC.pm $(DESTDIR)$(MODDIR)/modperl $(INSTALL_DATA) $(srcdir)/modperl/startup.pl $(DESTDIR)$(MODDIR)/modperl +modpython/compiler: modpython/compiler.cpp + $(CXX) $(PYTHONCOMMON) -o $@ $< + +modpython_compilepyc: modpython/compiler + +cp -p $(srcdir)/*.py ./ + +cp -p $(srcdir)/*.pyc ./ + +cp -p $(srcdir)/modpython/znc*.py modpython/ + $< + +modpython_install: create_install_dir install_metadirs modpython_compilepyc + +for i in *.pyc; do \ + test -r $$i && $(INSTALL_DATA) $$i $(DESTDIR)$(MODDIR); \ + done + for a in $(srcdir)/*; do \ + if [ -d $$a ]; then \ + if [ -f $${a}.py ] || [ -f $${a}.pyc ]; then \ + cp -Rp $$a $(DESTDIR)$(DATADIR); \ + fi \ + fi \ + done + rm -rf $(DESTDIR)$(DATADIR)/modpython + mkdir -p $(DESTDIR)$(MODDIR)/modpython + $(INSTALL_PROGRAM) modpython/_znc_core.so $(DESTDIR)$(MODDIR)/modpython + $(INSTALL_DATA) modpython/znc_core.pyc $(DESTDIR)$(MODDIR)/modpython + $(INSTALL_DATA) modpython/znc.pyc $(DESTDIR)$(MODDIR)/modpython + modtcl_install: mkdir -p $(DESTDIR)$(DATADIR)/modtcl/ $(INSTALL_DATA) $(srcdir)/extra/modtcl.tcl $(srcdir)/extra/binds.tcl $(DESTDIR)$(DATADIR)/modtcl/ diff --git a/modules/modpython.cpp b/modules/modpython.cpp new file mode 100644 index 00000000..1ae9e848 --- /dev/null +++ b/modules/modpython.cpp @@ -0,0 +1,489 @@ +/* + * 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 + +#include "Modules.h" +#include "User.h" +#include "Chan.h" +#include "Nick.h" +#include "znc.h" +#include "IRCSock.h" + +#include "modpython/swigpyrun.h" +#include "modpython/module.h" + +class CPyRetString { +public: + CString& s; + CPyRetString(CString& S) : s(S) {} + static PyObject* wrap(CString& S) { + CPyRetString* x = new CPyRetString(S); + return SWIG_NewInstanceObj(x, SWIG_TypeQuery("CPyRetString*"), SWIG_POINTER_OWN); + } +}; + +class CModPython: public CGlobalModule { + + PyObject* m_PyZNCModule; + PyObject* m_PyFormatException; + +public: + + CString GetPyExceptionStr() { + PyObject* ptype; + PyObject* pvalue; + PyObject* ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + CString result; + if (!pvalue) { + Py_INCREF(Py_None); + pvalue = Py_None; + } + if (!ptraceback) { + Py_INCREF(Py_None); + ptraceback = Py_None; + } + PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); + PyObject* strlist = PyObject_CallFunctionObjArgs(m_PyFormatException, ptype, pvalue, ptraceback, NULL); + Py_CLEAR(ptype); + Py_CLEAR(pvalue); + Py_CLEAR(ptraceback); + if (!strlist) { + return "Couldn't get exact error message"; + } + + if (PySequence_Check(strlist)) { + PyObject* strlist_fast = PySequence_Fast(strlist, "Shouldn't happen (1)"); + PyObject** items = PySequence_Fast_ITEMS(strlist_fast); + Py_ssize_t L = PySequence_Fast_GET_SIZE(strlist_fast); + for (Py_ssize_t i = 0; i < L; ++i) { + PyObject* utf8 = PyUnicode_AsUTF8String(items[i]); + result += PyBytes_AsString(utf8); + Py_CLEAR(utf8); + } + Py_CLEAR(strlist_fast); + } else { + result = "Can't get exact error message"; + } + + Py_CLEAR(strlist); + + return result; + } + + GLOBALMODCONSTRUCTOR(CModPython) { + Py_Initialize(); + m_PyFormatException = NULL; + m_PyZNCModule = NULL; + } + + bool OnLoad(const CString& sArgsi, CString& sMessage) { + CString sModPath, sTmp; + if (!CModules::FindModPath("modpython/_znc_core.so", sModPath, sTmp)) { + sMessage = "modpython/_znc_core.so not found."; + return false; + } + sTmp = CDir::ChangeDir(sModPath, ".."); + + PyObject* pyModuleTraceback = PyImport_ImportModule("traceback"); + if (!pyModuleTraceback) { + sMessage = "Couldn't import python module traceback"; + return false; + } + m_PyFormatException = PyObject_GetAttrString(pyModuleTraceback, "format_exception"); + if (!m_PyFormatException) { + sMessage = "Couldn't get traceback.format_exception"; + Py_CLEAR(pyModuleTraceback); + return false; + } + Py_CLEAR(pyModuleTraceback); + + PyObject* pySysModule = PyImport_ImportModule("sys"); + if (!pySysModule) { + sMessage = GetPyExceptionStr(); + return false; + } + PyObject* pySysPath = PyObject_GetAttrString(pySysModule, "path"); + if (!pySysPath) { + sMessage = GetPyExceptionStr(); + Py_CLEAR(pySysModule); + return false; + } + Py_CLEAR(pySysModule); + PyObject* pyIgnored = PyObject_CallMethod(pySysPath, const_cast("append"), const_cast("s"), sTmp.c_str()); + if (!pyIgnored) { + sMessage = GetPyExceptionStr(); + Py_CLEAR(pyIgnored); + return false; + } + Py_CLEAR(pyIgnored); + Py_CLEAR(pySysPath); + + m_PyZNCModule = PyImport_ImportModule("znc"); + if (!m_PyZNCModule) { + sMessage = GetPyExceptionStr(); + return false; + } + + return true; + } + + virtual EModRet OnModuleLoading(const CString& sModName, const CString& sArgs, + bool& bSuccess, CString& sRetMsg) { + if (!GetUser()) { + return CONTINUE; + } + PyObject* pyFunc = PyObject_GetAttrString(m_PyZNCModule, "load_module"); + if (!pyFunc) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + return HALT; + } + PyObject* pyRes = PyObject_CallFunction(pyFunc, const_cast("ssNNN"), + sModName.c_str(), + sArgs.c_str(), + SWIG_NewInstanceObj(GetUser(), SWIG_TypeQuery("CUser*"), 0), + CPyRetString::wrap(sRetMsg), + SWIG_NewInstanceObj(reinterpret_cast(this), SWIG_TypeQuery("CGlobalModule*"), 0)); + if (!pyRes) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + Py_CLEAR(pyFunc); + return HALT; + } + Py_CLEAR(pyFunc); + long int ret = PyLong_AsLong(pyRes); + if (PyErr_Occurred()) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + Py_CLEAR(pyRes); + return HALT; + } + Py_CLEAR(pyRes); + switch (ret) { + case 0: + // Not found + return CONTINUE; + case 1: + // Error + bSuccess = false; + return HALT; + case 2: + // Success + bSuccess = true; + return HALT; + } + bSuccess = false; + sRetMsg += " unknown value returned by modperl.load_module"; + return HALT; + } + + virtual EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) { + CPyModule* pMod = AsPyModule(pModule); + if (pMod) { + CString sModName = pMod->GetModName(); + PyObject* pyFunc = PyObject_GetAttrString(m_PyZNCModule, "unload_module"); + if (!pyFunc) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + return HALT; + } + PyObject* pyRes = PyObject_CallFunctionObjArgs(pyFunc, pMod->GetPyObj(), NULL); + if (!pyRes) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + Py_CLEAR(pyFunc); + return HALT; + } + Py_CLEAR(pyFunc); + Py_CLEAR(pyRes); + bSuccess = true; + sRetMsg = "Module [" + sModName + "] unloaded"; + return HALT; + } + return CONTINUE; + } + + virtual EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule, + bool& bSuccess, CString& sRetMsg) { + PyObject* pyFunc = PyObject_GetAttrString(m_PyZNCModule, "get_mod_info"); + if (!pyFunc) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + return HALT; + } + PyObject* pyRes = PyObject_CallFunction(pyFunc, const_cast("sNN"), + sModule.c_str(), + CPyRetString::wrap(sRetMsg), + SWIG_NewInstanceObj(&ModInfo, SWIG_TypeQuery("CModInfo*"), 0)); + if (!pyRes) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + Py_CLEAR(pyFunc); + return HALT; + } + Py_CLEAR(pyFunc); + long int x = PyLong_AsLong(pyRes); + if (PyErr_Occurred()) { + sRetMsg = GetPyExceptionStr(); + DEBUG("modpython: " << sRetMsg); + bSuccess = false; + Py_CLEAR(pyRes); + return HALT; + } + Py_CLEAR(pyRes); + switch (x) { + case 0: + return CONTINUE; + case 1: + bSuccess = false; + return HALT; + case 2: + bSuccess = true; + return HALT; + } + bSuccess = false; + sRetMsg = CString("Shouldn't happen. ") + __PRETTY_FUNCTION__ + " on " + __FILE__ + ":" + CString(__LINE__); + DEBUG(sRetMsg); + return HALT; + } + + void TryAddModInfo(const CString& sPath, const CString& sName, set& ssMods, set& ssAlready) { + if (ssAlready.count(sName)) { + return; + } + PyObject* pyFunc = PyObject_GetAttrString(m_PyZNCModule, "get_mod_info_path"); + if (!pyFunc) { + CString sRetMsg = GetPyExceptionStr(); + DEBUG("modpython tried to get info about [" << sPath << "] (1) but: " << sRetMsg); + return; + } + CModInfo ModInfo; + PyObject* pyRes = PyObject_CallFunction(pyFunc, const_cast("ssN"), + sPath.c_str(), + sName.c_str(), + SWIG_NewInstanceObj(&ModInfo, SWIG_TypeQuery("CModInfo*"), 0)); + if (!pyRes) { + CString sRetMsg = GetPyExceptionStr(); + DEBUG("modpython tried to get info about [" << sPath << "] (2) but: " << sRetMsg); + Py_CLEAR(pyFunc); + return; + } + Py_CLEAR(pyFunc); + long int x = PyLong_AsLong(pyRes); + if (PyErr_Occurred()) { + CString sRetMsg = GetPyExceptionStr(); + DEBUG("modpython tried to get info about [" << sPath << "] (3) but: " << sRetMsg); + Py_CLEAR(pyRes); + return; + } + Py_CLEAR(pyRes); + if (x) { + ssMods.insert(ModInfo); + ssAlready.insert(sName); + } + } + + virtual void OnGetAvailableMods(set& ssMods, bool bGlobal) { + if (bGlobal) { + return; + } + + CDir Dir; + CModules::ModDirList dirs = CModules::GetModDirs(); + + while (!dirs.empty()) { + set already; + + Dir.FillByWildcard(dirs.front().first, "*.py"); + for (unsigned int a = 0; a < Dir.size(); a++) { + CFile& File = *Dir[a]; + CString sName = File.GetShortName(); + CString sPath = File.GetLongName(); + sPath.TrimSuffix(sName); + sName.RightChomp(3); + TryAddModInfo(sPath, sName, ssMods, already); + } + + Dir.FillByWildcard(dirs.front().first, "*.pyc"); + for (unsigned int a = 0; a < Dir.size(); a++) { + CFile& File = *Dir[a]; + CString sName = File.GetShortName(); + CString sPath = File.GetLongName(); + sPath.TrimSuffix(sName); + sName.RightChomp(4); + TryAddModInfo(sPath, sName, ssMods, already); + } + + Dir.FillByWildcard(dirs.front().first, "*.so"); + for (unsigned int a = 0; a < Dir.size(); a++) { + CFile& File = *Dir[a]; + CString sName = File.GetShortName(); + CString sPath = File.GetLongName(); + sPath.TrimSuffix(sName); + sName.RightChomp(3); + TryAddModInfo(sPath, sName, ssMods, already); + } + + dirs.pop(); + } + } + + virtual ~CModPython() { + const map& users = CZNC::Get().GetUserMap(); + for (map::const_iterator i = users.begin(); i != users.end(); ++i) { + CModules& M = i->second->GetModules(); + bool cont; + do { + cont = false; + for (CModules::iterator it = M.begin(); it != M.end(); ++it) { + CModule* m = *it; + CPyModule* mod = AsPyModule(m); + if (mod) { + cont = true; + bool bSuccess = false; + CString sRetMsg; + OnModuleUnloading(mod, bSuccess, sRetMsg); + if (!bSuccess) { + DEBUG("Error unloading python module in ~CModPython: " << sRetMsg); + } + break; + } + } + } while (cont); + } + Py_CLEAR(m_PyFormatException); + Py_CLEAR(m_PyZNCModule); + Py_Finalize(); + } + +}; + +CString CPyModule::GetPyExceptionStr() { + return m_pModPython->GetPyExceptionStr(); +} + +#include "modpython/functions.cpp" + +VWebSubPages& CPyModule::GetSubPages() { + VWebSubPages* result = _GetSubPages(); + if (!result) { + return CModule::GetSubPages(); + } + return *result; +} + +void CPyTimer::RunJob() { + CPyModule* pMod = AsPyModule(GetModule()); + if (pMod) { + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("RunJob"), const_cast("")); + if (!pyRes) { + CString sRetMsg = m_pModPython->GetPyExceptionStr(); + DEBUG("python timer failed: " << sRetMsg); + Stop(); + } + Py_CLEAR(pyRes); + } +} + +CPyTimer::~CPyTimer() { + CPyModule* pMod = AsPyModule(GetModule()); + if (pMod) { + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("OnShutdown"), const_cast("")); + if (!pyRes) { + CString sRetMsg = m_pModPython->GetPyExceptionStr(); + DEBUG("python timer shutdown failed: " << sRetMsg); + } + Py_CLEAR(pyRes); + Py_CLEAR(m_pyObj); + } +} + +#define CHECKCLEARSOCK(Func)\ +if (!pyRes) {\ + CString sRetMsg = m_pModPython->GetPyExceptionStr();\ + DEBUG("python socket failed in " Func ": " << sRetMsg);\ + Close();\ +}\ +Py_CLEAR(pyRes) + +#define CBSOCK(Func) void CPySocket::Func() {\ + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("On" #Func), const_cast(""));\ + CHECKCLEARSOCK(#Func);\ +} +CBSOCK(Connected); +CBSOCK(Disconnected); +CBSOCK(Timeout); +CBSOCK(ConnectionRefused); + +void CPySocket::ReadData(const char *data, size_t len) { + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("OnReadData"), const_cast("y#"), data, (int)len); + CHECKCLEARSOCK("OnReadData"); +} + +void CPySocket::ReadLine(const CString& sLine) { + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("OnReadLine"), const_cast("s"), sLine.c_str()); + CHECKCLEARSOCK("OnReadLine"); +} + +Csock* CPySocket::GetSockObj(const CString& sHost, unsigned short uPort) { + CPySocket* result = NULL; + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("_Accepted"), const_cast("sH"), sHost.c_str(), uPort); + if (!pyRes) { + CString sRetMsg = m_pModPython->GetPyExceptionStr(); + DEBUG("python socket failed in OnAccepted: " << sRetMsg); + Close(); + } + int res = SWIG_ConvertPtr(pyRes, (void**)&result, SWIG_TypeQuery("CPySocket*"), 0); + if (!SWIG_IsOK(res)) { + DEBUG("python socket was expected to return new socket from OnAccepted, but error=" << res); + Close(); + result = NULL; + } + if (!result) { + DEBUG("modpython: OnAccepted didn't return new socket"); + } + Py_CLEAR(pyRes); + return result; +} + +CPySocket::~CPySocket() { + PyObject* pyRes = PyObject_CallMethod(m_pyObj, const_cast("OnShutdown"), const_cast("")); + if (!pyRes) { + CString sRetMsg = m_pModPython->GetPyExceptionStr(); + DEBUG("python socket failed in OnShutdown: " << sRetMsg); + } + Py_CLEAR(pyRes); + Py_CLEAR(m_pyObj); +} + +PyObject* CPySocket::WriteBytes(PyObject* data) { + if (!PyBytes_Check(data)) { + PyErr_SetString(PyExc_TypeError, "socket.WriteBytes needs bytes as argument"); + return NULL; + } + char* buffer; + Py_ssize_t length; + if (-1 == PyBytes_AsStringAndSize(data, &buffer, &length)) { + return NULL; + } + if (Write(buffer, length)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +GLOBALMODULEDEFS(CModPython, "Loads python scripts as ZNC modules") diff --git a/modules/modpython/codegen.pl b/modules/modpython/codegen.pl new file mode 100644 index 00000000..ca50e784 --- /dev/null +++ b/modules/modpython/codegen.pl @@ -0,0 +1,418 @@ +#!/usr/bin/env perl +# +# 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. +# +# Parts of SWIG are used here. + +use strict; +use warnings; +use IO::File; +use feature 'switch', 'say'; + +open my $in, $ARGV[0] or die; +open my $out, ">", $ARGV[1] or die; + +print $out <<'EOF'; +/* + * 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. + * + * Parts of SWIG are used here. + */ + +/*************************************************************************** + * This file is generated automatically using codegen.pl from functions.in * + * Don't change it manually. * + ***************************************************************************/ + +namespace { +/* template + struct pyobj_to_ptr { + CString m_sType; + SvToPtr(const CString& sType) { + m_sType = sType; + } + bool operator()(PyObject* py, T** result) { + T* x = NULL; + int res = SWIG_ConvertPtr(sv, (void**)&x, SWIG_TypeQuery(m_sType.c_str()), 0); + if (SWIG_IsOK(res)) { + *result = x; + return true; + } + DEBUG("modpython: "); + return false; + } + }; + + CModule::EModRet SvToEModRet(PyObject* py, CModule::EModRet* result) { + long int x = PyLong_AsLong(); + return static_cast(SvUV(sv)); + }*/ + + inline swig_type_info* SWIG_pchar_descriptor(void) { + static int init = 0; + static swig_type_info* info = 0; + if (!init) { + info = SWIG_TypeQuery("_p_char"); + init = 1; + } + return info; + } + + inline int SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) { +#if PY_VERSION_HEX>=0x03000000 + if (PyUnicode_Check(obj)) +#else + if (PyString_Check(obj)) +#endif + { + char *cstr; Py_ssize_t len; +#if PY_VERSION_HEX>=0x03000000 + if (!alloc && cptr) { + /* We can't allow converting without allocation, since the internal + representation of string in Python 3 is UCS-2/UCS-4 but we require + a UTF-8 representation. + TODO(bhy) More detailed explanation */ + return SWIG_RuntimeError; + } + obj = PyUnicode_AsUTF8String(obj); + PyBytes_AsStringAndSize(obj, &cstr, &len); + if(alloc) *alloc = SWIG_NEWOBJ; +#else + PyString_AsStringAndSize(obj, &cstr, &len); +#endif + if (cptr) { + if (alloc) { + /* + In python the user should not be able to modify the inner + string representation. To warranty that, if you define + SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string + buffer is always returned. + + The default behavior is just to return the pointer value, + so, be careful. + */ +#if defined(SWIG_PYTHON_SAFE_CSTRINGS) + if (*alloc != SWIG_OLDOBJ) +#else + if (*alloc == SWIG_NEWOBJ) +#endif + { + *cptr = (char *)memcpy((char *)malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); + *alloc = SWIG_NEWOBJ; + } + else { + *cptr = cstr; + *alloc = SWIG_OLDOBJ; + } + } else { +#if PY_VERSION_HEX>=0x03000000 + assert(0); /* Should never reach here in Python 3 */ +#endif + *cptr = SWIG_Python_str_AsChar(obj); + } + } + if (psize) *psize = len + 1; +#if PY_VERSION_HEX>=0x03000000 + Py_XDECREF(obj); +#endif + return SWIG_OK; + } else { + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + if (pchar_descriptor) { + void* vptr = 0; + if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { + if (cptr) *cptr = (char *) vptr; + if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + } + } + return SWIG_TypeError; + } + + inline int SWIG_AsPtr_std_string (PyObject * obj, CString **val) { + char* buf = 0 ; size_t size = 0; int alloc = SWIG_OLDOBJ; + if (SWIG_IsOK((SWIG_AsCharPtrAndSize(obj, &buf, &size, &alloc)))) { + if (buf) { + if (val) *val = new CString(buf, size - 1); + if (alloc == SWIG_NEWOBJ) free((char*)buf); + return SWIG_NEWOBJ; + } else { + if (val) *val = 0; + return SWIG_OLDOBJ; + } + } else { + static int init = 0; + static swig_type_info* descriptor = 0; + if (!init) { + descriptor = SWIG_TypeQuery("CString" " *"); + init = 1; + } + if (descriptor) { + CString *vptr; + int res = SWIG_ConvertPtr(obj, (void**)&vptr, descriptor, 0); + if (SWIG_IsOK(res) && val) *val = vptr; + return res; + } + } + return SWIG_ERROR; + } +} + +EOF +=b +bool OnFoo(const CString& x) { + PyObject* pyName = Py_BuildValue("s", "OnFoo"); + if (!pyName) { + CString s = GetPyExceptionStr(); + DEBUG("modpython: username/module/OnFoo: can't name method to call: " << s); + return default; + } + PyObject* pyArg1 = Py_BuildValue("s", x.c_str()); + if (!pyArg1) { + CString s = GetPyExceptionStr(); + DEBUG("modpython: username/module/OnFoo: can't convert parameter x to PyObject*: " << s); + Py_CLEAR(pyName); + return default; + } + PyObject* pyArg2 = ...; + if (!pyArg2) { + CString s = ...; + DEBUG(...); + Py_CLEAR(pyName); + Py_CLEAR(pyArg1); + return default; + } + PyObject* pyArg3 = ...; + if (!pyArg3) { + CString s = ...; + DEBUG(...); + Py_CLEAR(pyName); + Py_CLEAR(pyArg1); + Py_CLEAR(pyArg2); + return default; + } + PyObject* pyRes = PyObject_CallMethodObjArgs(m_pyObj, pyName, pyArg1, pyArg2, pyArg3, NULL); + if (!pyRes) { + CString s = ...; + DEBUG("modpython: username/module/OnFoo failed: " << s); + Py_CLEAR(pyName); + Py_CLEAR(pyArg1); + Py_CLEAR(pyArg2); + Py_CLEAR(pyArg3); + return default; + } + Py_CLEAR(pyName); + Py_CLEAR(pyArg1); + Py_CLEAR(pyArg2); + Py_CLEAR(pyArg3); + bool res = PyLong_AsLong(pyRes); + if (PyErr_Occured()) { + CString s = GetPyExceptionStr(); + DEBUG("modpython: username/module/OnFoo returned unexpected value: " << s); + Py_CLEAR(pyRes); + return default; + } + Py_CLEAR(pyRes); + return res; +} +=cut + +while (<$in>) { + my ($type, $name, $args, $default) = /(\S+)\s+(\w+)\((.*)\)(?:=(\w+))?/ or next; + $type =~ s/(EModRet)/CModule::$1/; + $type =~ s/^\s*(.*?)\s*$/$1/; + unless (defined $default) { + given ($type) { + when ('bool') { $default = 'true' } + when ('CModule::EModRet') { $default = 'CONTINUE' } + when ('CString') { $default = '""' } + when (/\*$/) { $default = "($type)NULL" } + } + } + my @arg = map { + my ($t, $v) = /^\s*(.*\W)\s*(\w+)\s*$/; + $t =~ s/^\s*(.*?)\s*$/$1/; + my ($tb, $tm) = $t =~ /^(.*?)\s*?(\*|&)?$/; + {type=>$t, var=>$v, base=>$tb, mod=>$tm//'', pyvar=>"pyArg_$v", error=>"can't convert parameter '$v' to PyObject"} + } split /,/, $args; + + unshift @arg, {type=>'$func$', var=>"", base=>"", mod=>"", pyvar=>"pyName", error=>"can't convert string '$name' to PyObject"}; + + my $cleanup = ''; + $default = '' if $type eq 'void'; + + say $out "$type CPyModule::$name($args) {"; + for my $a (@arg) { + print $out "\tPyObject* $a->{pyvar} = "; + given ($a->{type}) { + when ('$func$') { + say $out "Py_BuildValue(\"s\", \"$name\");"; + } + when (/vector\s*<\s*.*\*\s*>/) { + say $out "PyList_New(0);"; + } + when (/CString/) { + if ($a->{base} eq 'CString' && $a->{mod} eq '&') { + say $out "CPyRetString::wrap($a->{var});"; + } else { + say $out "Py_BuildValue(\"s\", $a->{var}.c_str());"; + } + } + when (/\*$/) { + (my $t = $a->{type}) =~ s/^const//; + say $out "SWIG_NewInstanceObj(const_cast<$t>($a->{var}), SWIG_TypeQuery(\"$t\"), 0);"; + } + when (/&$/) { + (my $b = $a->{base}) =~ s/^const//; + say $out "SWIG_NewInstanceObj(const_cast<$b*>(&$a->{var}), SWIG_TypeQuery(\"$b*\"), 0);"; + } + when ('bool') { + say $out "Py_BuildValue(\"l\", (long int)$a->{var});"; + } + default { + my %letter = ( + 'int' => 'i', + 'char' => 'b', + 'short int' => 'h', + 'long int' => 'l', + 'unsigned char' => 'B', + 'unsigned short' => 'H', + 'unsigned int' => 'I', + 'unsigned long' => 'k', + 'long long' => 'L', + 'unsigned long long' => 'K', + 'ssize_t' => 'n', + 'double' => 'd', + 'float' => 'f', + ); + if (exists $letter{$a->{type}}) { + say $out "Py_BuildValue(\"$letter{$a->{type}}\", $a->{var});" + } else { + say $out "...;"; + } + } + } + say $out "\tif (!$a->{pyvar}) {"; + say $out "\t\tCString sPyErr = m_pModPython->GetPyExceptionStr();"; + say $out "\t\tDEBUG".'("modpython: " << GetUser()->GetUserName() << "/" << GetModName() << '."\"/$name: $a->{error}: \" << sPyErr);"; + print $out $cleanup; + say $out "\t\treturn $default;"; + say $out "\t}"; + + $cleanup .= "\t\tPy_CLEAR($a->{pyvar});\n"; + + if ($a->{type} =~ /(vector\s*<\s*(.*)\*\s*>)/) { + my ($vec, $sub) = ($1, $2); + (my $cleanup1 = $cleanup) =~ s/\t\t/\t\t\t/g; + my $dot = '.'; + $dot = '->' if $a->{mod} eq '*'; + say $out "\tfor (${vec}::const_iterator i = $a->{var}${dot}begin(); i != $a->{var}${dot}end(); ++i) {"; + say $out "\t\tPyObject* pyVecEl = SWIG_NewInstanceObj(*i, SWIG_TypeQuery(\"$sub*\"), 0);"; + say $out "\t\tif (!pyVecEl) {"; + say $out "\t\t\tCString sPyErr = m_pModPython->GetPyExceptionStr();"; + say $out "\t\t\tDEBUG".'("modpython: " << GetUser()->GetUserName() << "/" << GetModName() << '. + "\"/$name: can't convert element of vector '$a->{var}' to PyObject: \" << sPyErr);"; + print $out $cleanup1; + say $out "\t\t\treturn $default;"; + say $out "\t\t}"; + say $out "\t\tif (PyList_Append($a->{pyvar}, pyVecEl)) {"; + say $out "\t\t\tCString sPyErr = m_pModPython->GetPyExceptionStr();"; + say $out "\t\t\tDEBUG".'("modpython: " << GetUser()->GetUserName() << "/" << GetModName() << '. + "\"/$name: can't add element of vector '$a->{var}' to PyObject: \" << sPyErr);"; + say $out "\t\t\tPy_CLEAR(pyVecEl);"; + print $out $cleanup1; + say $out "\t\t\treturn $default;"; + say $out "\t\t}"; + say $out "\t\tPy_CLEAR(pyVecEl);"; + say $out "\t}"; + } + } + + print $out "\tPyObject* pyRes = PyObject_CallMethodObjArgs(m_pyObj"; + print $out ", $_->{pyvar}" for @arg; + say $out ", NULL);"; + say $out "\tif (!pyRes) {"; + say $out "\t\tCString sPyErr = m_pModPython->GetPyExceptionStr();"; + say $out "\t\tDEBUG".'("modpython: " << GetUser()->GetUserName() << "/" << GetModName() << '."\"/$name failed: \" << sPyErr);"; + print $out $cleanup; + say $out "\t\treturn $default;"; + say $out "\t}"; + + $cleanup =~ s/\t\t/\t/g; + + print $out $cleanup; + + if ($type ne 'void') { + say $out "\t$type result;"; + say $out "\tif (pyRes == Py_None) {"; + say $out "\t\tresult = $default;"; + say $out "\t} else {"; + given ($type) { + when (/^(.*)\*$/) { + say $out "\t\tint res = SWIG_ConvertPtr(pyRes, (void**)&result, SWIG_TypeQuery(\"$type\"), 0);"; + say $out "\t\tif (!SWIG_IsOK(res)) {"; + say $out "\t\t\tDEBUG(\"modpython: \" << GetUser()->GetUserName() << \"/\" << GetModName() << \"/$name was expected to return '$type' but error=\" << res);"; + say $out "\t\t\tresult = $default;"; + say $out "\t\t}"; + } + when ('CString') { + say $out "\t\tCString* p = NULL;"; + say $out "\t\tint res = SWIG_AsPtr_std_string(pyRes, &p);"; + say $out "\t\tif (!SWIG_IsOK(res)) {"; + say $out "\t\t\tDEBUG(\"modpython: \" << GetUser()->GetUserName() << \"/\" << GetModName() << \"/$name was expected to return '$type' but error=\" << res);"; + say $out "\t\t\tresult = $default;"; + say $out "\t\t} else if (!p) {"; + say $out "\t\t\tDEBUG(\"modpython: \" << GetUser()->GetUserName() << \"/\" << GetModName() << \"/$name was expected to return '$type' but returned NULL\");"; + say $out "\t\t\tresult = $default;"; + say $out "\t\t} else result = *p;"; + say $out "\t\tif (SWIG_IsNewObj(res)) free((char*)p); // Don't ask me, that's how SWIG works..."; + } + when ('CModule::EModRet') { + say $out "\t\tlong int x = PyLong_AsLong(pyRes);"; + say $out "\t\tif (PyErr_Occurred()) {"; + say $out "\t\t\tCString sPyErr = m_pModPython->GetPyExceptionStr();"; + say $out "\t\t\tDEBUG".'("modpython: " << GetUser()->GetUserName() << "/" << GetModName() << '."\"/$name was expected to return EModRet but: \" << sPyErr);"; + say $out "\t\t\tresult = $default;"; + say $out "\t\t} else { result = (CModule::EModRet)x; }"; + } + when ('bool') { + say $out "\t\tint x = PyObject_IsTrue(pyRes);"; + say $out "\t\tif (-1 == x) {"; + say $out "\t\t\tCString sPyErr = m_pModPython->GetPyExceptionStr();"; + say $out "\t\t\tDEBUG".'("modpython: " << GetUser()->GetUserName() << "/" << GetModName() << '."\"/$name was expected to return EModRet but: \" << sPyErr);"; + say $out "\t\t\tresult = $default;"; + say $out "\t\t} else result = x ? true : false;"; + } + default { + say $out "\t\tI don't know how to convert PyObject to $type :("; + } + } + say $out "\t}"; + say $out "\tPy_CLEAR(pyRes);"; + say $out "\treturn result;"; + } else { + say $out "\tPy_CLEAR(pyRes);"; + } + say $out "}\n"; +} + +sub getres { + my $type = shift; + given ($type) { + when (/^(.*)\*$/) { return "pyobj_to_ptr<$1>(\"$type\")" } + when ('CString') { return 'PString' } + when ('CModule::EModRet') { return 'SvToEModRet' } + when (/unsigned/) { return 'SvUV' } + default { return 'SvIV' } + } +} diff --git a/modules/modpython/compiler.cpp b/modules/modpython/compiler.cpp new file mode 100644 index 00000000..6cafe31f --- /dev/null +++ b/modules/modpython/compiler.cpp @@ -0,0 +1,16 @@ +/* + * 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 + +int main() { + Py_Initialize(); + int res = PyRun_SimpleString("import compileall; print('Optimizing python files for later use...'); compileall.compile_dir('.')"); + Py_Finalize(); + return res; +} diff --git a/modules/modpython/cstring.i b/modules/modpython/cstring.i new file mode 100644 index 00000000..78b740d5 --- /dev/null +++ b/modules/modpython/cstring.i @@ -0,0 +1,332 @@ +/* + SWIG-generated sources are used here. +*/ + +// +// String +// + +%{ +#include +%} + +%feature("naturalvar") CString; +class CString; + +/*@SWIG:/usr/share/swig1.3/typemaps/std_strings.swg,74,%typemaps_std_string@*/ + +/*@SWIG:/usr/share/swig1.3/typemaps/std_strings.swg,4,%std_string_asptr@*/ +%fragment("SWIG_" "AsPtr" "_" {CString},"header",fragment="SWIG_AsCharPtrAndSize") { +SWIGINTERN int +SWIG_AsPtr_std_string (PyObject * obj, CString **val) +{ + char* buf = 0 ; size_t size = 0; int alloc = SWIG_OLDOBJ; + if (SWIG_IsOK((SWIG_AsCharPtrAndSize(obj, &buf, &size, &alloc)))) { + if (buf) { + if (val) *val = new CString(buf, size - 1); + if (alloc == SWIG_NEWOBJ) free((char*)buf); + return SWIG_NEWOBJ; + } else { + if (val) *val = 0; + return SWIG_OLDOBJ; + } + } else { + static int init = 0; + static swig_type_info* descriptor = 0; + if (!init) { + descriptor = SWIG_TypeQuery("CString" " *"); + init = 1; + } + if (descriptor) { + CString *vptr; + int res = SWIG_ConvertPtr(obj, (void**)&vptr, descriptor, 0); + if (SWIG_IsOK(res) && val) *val = vptr; + return res; + } + } + return SWIG_ERROR; +} +} +/*@SWIG@*/ +/*@SWIG:/usr/share/swig1.3/typemaps/std_strings.swg,52,%std_string_asval@*/ +%fragment("SWIG_" "AsVal" "_" {CString},"header", fragment="SWIG_" "AsPtr" "_" {CString}) { +SWIGINTERN int +SWIG_AsVal_std_string (PyObject * obj, CString *val) +{ + CString* v = (CString *) 0; + int res = SWIG_AsPtr_std_string (obj, &v); + if (!SWIG_IsOK(res)) return res; + if (v) { + if (val) *val = *v; + if (SWIG_IsNewObj(res)) { + free((char*)v); + res = SWIG_DelNewMask(res); + } + return res; + } + return SWIG_ERROR; +} +} +/*@SWIG@*/ +/*@SWIG:/usr/share/swig1.3/typemaps/std_strings.swg,38,%std_string_from@*/ +%fragment("SWIG_" "From" "_" {CString},"header",fragment="SWIG_FromCharPtrAndSize") { +SWIGINTERNINLINE PyObject * +SWIG_From_std_string (const CString& s) +{ + if (s.size()) { + return SWIG_FromCharPtrAndSize(s.data(), s.size()); + } else { + return SWIG_FromCharPtrAndSize(s.c_str(), 0); + } +} +} +/*@SWIG@*/ + +/*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,204,%typemaps_asptrfromn@*/ +/*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,193,%typemaps_asptrfrom@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,163,%typemaps_asptr@*/ + %fragment("SWIG_" "AsVal" "_" {CString},"header",fragment="SWIG_" "AsPtr" "_" {CString}) { + SWIGINTERNINLINE int + SWIG_AsVal_std_string (PyObject * obj, CString *val) + { + CString *v = (CString *)0; + int res = SWIG_AsPtr_std_string (obj, &v); + if (!SWIG_IsOK(res)) return res; + if (v) { + if (val) *val = *v; + if (SWIG_IsNewObj(res)) { + free((char*)v); + res = SWIG_DelNewMask(res); + } + return res; + } + return SWIG_ERROR; + } + } + /*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,31,%ptr_in_typemap@*/ + %typemap(in,fragment="SWIG_" "AsPtr" "_" {CString}) CString { + CString *ptr = (CString *)0; + int res = SWIG_AsPtr_std_string($input, &ptr); + if (!SWIG_IsOK(res) || !ptr) { + SWIG_exception_fail(SWIG_ArgError((ptr ? res : SWIG_TypeError)), "in method '" "$symname" "', argument " "$argnum"" of type '" "$type""'"); + } + $1 = *ptr; + if (SWIG_IsNewObj(res)) free((char*)ptr); + } + %typemap(freearg) CString ""; + %typemap(in,fragment="SWIG_" "AsPtr" "_" {CString}) const CString & (int res = SWIG_OLDOBJ) { + CString *ptr = (CString *)0; + res = SWIG_AsPtr_std_string($input, &ptr); + if (!SWIG_IsOK(res)) { SWIG_exception_fail(SWIG_ArgError(res), "in method '" "$symname" "', argument " "$argnum"" of type '" "$type""'"); } + if (!ptr) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "$symname" "', argument " "$argnum"" of type '" "$type""'"); } + $1 = ptr; + } + %typemap(freearg,noblock=1) const CString & { + if (SWIG_IsNewObj(res$argnum)) free((char*)$1); + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,56,%ptr_varin_typemap@*/ + %typemap(varin,fragment="SWIG_" "AsPtr" "_" {CString}) CString { + CString *ptr = (CString *)0; + int res = SWIG_AsPtr_std_string($input, &ptr); + if (!SWIG_IsOK(res) || !ptr) { + SWIG_exception_fail(SWIG_ArgError((ptr ? res : SWIG_TypeError)), "in variable '""$name""' of type '""$type""'"); + } + $1 = *ptr; + if (SWIG_IsNewObj(res)) free((char*)ptr); + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,71,%ptr_directorout_typemap@*/ + %typemap(directorargout,noblock=1,fragment="SWIG_" "AsPtr" "_" {CString}) CString *DIRECTOROUT ($*ltype temp) { + CString *swig_optr = 0; + int swig_ores = $input ? SWIG_AsPtr_std_string($input, &swig_optr) : 0; + if (!SWIG_IsOK(swig_ores) || !swig_optr) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError((swig_optr ? swig_ores : SWIG_TypeError))), "in output value of type '""$type""'"); + } + temp = *swig_optr; + $result = &temp; + if (SWIG_IsNewObj(swig_ores)) free((char*)swig_optr); + } + + %typemap(directorout,noblock=1,fragment="SWIG_" "AsPtr" "_" {CString}) CString { + CString *swig_optr = 0; + int swig_ores = SWIG_AsPtr_std_string($input, &swig_optr); + if (!SWIG_IsOK(swig_ores) || !swig_optr) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError((swig_optr ? swig_ores : SWIG_TypeError))), "in output value of type '""$type""'"); + } + $result = *swig_optr; + if (SWIG_IsNewObj(swig_ores)) free((char*)swig_optr); + } + + %typemap(directorout,noblock=1,fragment="SWIG_" "AsPtr" "_" {CString},warning= "473:Returning a pointer or reference in a director method is not recommended." ) CString* { + CString *swig_optr = 0; + int swig_ores = SWIG_AsPtr_std_string($input, &swig_optr); + if (!SWIG_IsOK(swig_ores)) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError(swig_ores)), "in output value of type '""$type""'"); + } + $result = swig_optr; + if (SWIG_IsNewObj(swig_ores)) { + swig_acquire_ownership(swig_optr); + } + } + %typemap(directorfree,noblock=1) CString* + { + if (director) { + director->swig_release_ownership(SWIG_as_voidptr($input)); + } + } + + %typemap(directorout,noblock=1,fragment="SWIG_" "AsPtr" "_" {CString},warning= "473:Returning a pointer or reference in a director method is not recommended." ) CString& { + CString *swig_optr = 0; + int swig_ores = SWIG_AsPtr_std_string($input, &swig_optr); + if (!SWIG_IsOK(swig_ores)) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError(swig_ores)), "in output value of type '""$type""'"); + } else { + if (!swig_optr) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ValueError), "invalid null reference " "in output value of type '""$type""'"); + } + } + $result = swig_optr; + if (SWIG_IsNewObj(swig_ores)) { + swig_acquire_ownership(swig_optr); + } + } + %typemap(directorfree,noblock=1) CString& + { + if (director) { + director->swig_release_ownership(SWIG_as_voidptr($input)); + } + } + + + %typemap(directorout,fragment="SWIG_" "AsPtr" "_" {CString}) CString &DIRECTOROUT = CString + +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/ptrtypes.swg,146,%ptr_typecheck_typemap@*/ +%typemap(typecheck,noblock=1,precedence=135,fragment="SWIG_" "AsPtr" "_" {CString}) CString * { + int res = SWIG_AsPtr_std_string($input, (CString**)(0)); + $1 = SWIG_CheckState(res); +} + +%typemap(typecheck,noblock=1,precedence=135,fragment="SWIG_" "AsPtr" "_" {CString}) CString, const CString& { + int res = SWIG_AsPtr_std_string($input, (CString**)(0)); + $1 = SWIG_CheckState(res); +} +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,254,%ptr_input_typemap@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,117,%_ptr_input_typemap@*/ + %typemap(in,noblock=1,fragment="SWIG_" "AsPtr" "_" {CString}) CString *INPUT(int res = 0) { + res = SWIG_AsPtr_std_string($input, &$1); + if (!SWIG_IsOK(res)) { + SWIG_exception_fail(SWIG_ArgError(res), "in method '" "$symname" "', argument " "$argnum"" of type '" "$type""'"); + } + res = SWIG_AddTmpMask(res); + } + %typemap(in,noblock=1,fragment="SWIG_" "AsPtr" "_" {CString}) CString &INPUT(int res = 0) { + res = SWIG_AsPtr_std_string($input, &$1); + if (!SWIG_IsOK(res)) { + SWIG_exception_fail(SWIG_ArgError(res), "in method '" "$symname" "', argument " "$argnum"" of type '" "$type""'"); + } + if (!$1) { + SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "$symname" "', argument " "$argnum"" of type '" "$type""'"); + } + res = SWIG_AddTmpMask(res); + } + %typemap(freearg,noblock=1,match="in") CString *INPUT, CString &INPUT { + if (SWIG_IsNewObj(res$argnum)) free((char*)$1); + } + %typemap(typecheck,noblock=1,precedence=135,fragment="SWIG_" "AsPtr" "_" {CString}) CString *INPUT, CString &INPUT { + int res = SWIG_AsPtr_std_string($input, (CString**)0); + $1 = SWIG_CheckState(res); + } +/*@SWIG@*/ +/*@SWIG@*/; +/*@SWIG@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/valtypes.swg,184,%typemaps_from@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/valtypes.swg,55,%value_out_typemap@*/ + %typemap(out,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString, const CString { + $result = SWIG_From_std_string((CString)($1)); + } + %typemap(out,noblock=1,fragment="SWIG_" "From" "_" {CString}) const CString& { + $result = SWIG_From_std_string((CString)(*$1)); + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/valtypes.swg,79,%value_varout_typemap@*/ + %typemap(varout,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString, const CString& { + $result = SWIG_From_std_string((CString)($1)); + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/valtypes.swg,87,%value_constcode_typemap@*/ + %typemap(constcode,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString { + SWIG_Python_SetConstant(d, "$symname",SWIG_From_std_string((CString)($value))); + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/valtypes.swg,98,%value_directorin_typemap@*/ + %typemap(directorin,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString *DIRECTORIN { + $input = SWIG_From_std_string((CString)(*$1_name)); + } + %typemap(directorin,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString, const CString& { + $input = SWIG_From_std_string((CString)($1_name)); + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/valtypes.swg,154,%value_throws_typemap@*/ + %typemap(throws,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString { + SWIG_Python_Raise(SWIG_From_std_string((CString)($1)), "$type", 0); SWIG_fail; + } +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,258,%value_output_typemap@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,175,%_value_output_typemap@*/ + %typemap(in,numinputs=0,noblock=1) + CString *OUTPUT ($*1_ltype temp, int res = SWIG_TMPOBJ), + CString &OUTPUT ($*1_ltype temp, int res = SWIG_TMPOBJ) { + $1 = &temp; + } + %typemap(argout,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString *OUTPUT, CString &OUTPUT { + if (SWIG_IsTmpObj(res$argnum)) { + $result = SWIG_Python_AppendOutput($result, SWIG_From_std_string((*$1))); + } else { + int new_flags = SWIG_IsNewObj(res$argnum) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj((void*)($1), $1_descriptor, new_flags)); + } + } +/*@SWIG@*/ +/*@SWIG@*/; +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,258,%value_output_typemap@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,175,%_value_output_typemap@*/ + %typemap(in,numinputs=0,noblock=1) + CString *OUTPUT ($*1_ltype temp, int res = SWIG_TMPOBJ), + CString &OUTPUT ($*1_ltype temp, int res = SWIG_TMPOBJ) { + $1 = &temp; + } + %typemap(argout,noblock=1,fragment="SWIG_" "From" "_" {CString}) CString *OUTPUT, CString &OUTPUT { + if (SWIG_IsTmpObj(res$argnum)) { + $result = SWIG_Python_AppendOutput($result, SWIG_From_std_string((*$1))); + } else { + int new_flags = SWIG_IsNewObj(res$argnum) ? (SWIG_POINTER_OWN | 0 ) : 0 ; + $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj((void*)($1), $1_descriptor, new_flags)); + } + } +/*@SWIG@*/ +/*@SWIG@*/; + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,240,%_ptr_inout_typemap@*/ + /*@SWIG:/usr/share/swig1.3/typemaps/inoutlist.swg,230,%_value_inout_typemap@*/ + %typemap(in) CString *INOUT = CString *INPUT; + %typemap(in) CString &INOUT = CString &INPUT; + %typemap(typecheck) CString *INOUT = CString *INPUT; + %typemap(typecheck) CString &INOUT = CString &INPUT; + %typemap(argout) CString *INOUT = CString *OUTPUT; + %typemap(argout) CString &INOUT = CString &OUTPUT; +/*@SWIG@*/ + %typemap(typecheck) CString *INOUT = CString *INPUT; + %typemap(typecheck) CString &INOUT = CString &INPUT; + %typemap(freearg) CString *INOUT = CString *INPUT; + %typemap(freearg) CString &INOUT = CString &INPUT; +/*@SWIG@*/; +/*@SWIG@*/; +/*@SWIG@*/; + +/*@SWIG@*/; + + + diff --git a/modules/modpython/functions.in b/modules/modpython/functions.in new file mode 100644 index 00000000..8d175983 --- /dev/null +++ b/modules/modpython/functions.in @@ -0,0 +1,66 @@ +bool OnBoot()=true +bool WebRequiresLogin()=true +bool WebRequiresAdmin()=false +CString GetWebMenuTitle() +bool OnWebPreRequest(CWebSock& WebSock, const CString& sPageName)=false +bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl)=false +VWebSubPages* _GetSubPages() +void OnPreRehash() +void OnPostRehash() +void OnIRCDisconnected() +void OnIRCConnected() +EModRet OnIRCConnecting(CIRCSock *pIRCSock) +EModRet OnIRCRegistration(CString& sPass, CString& sNick, CString& sIdent, CString& sRealName) +EModRet OnBroadcast(CString& sMessage) +EModRet OnConfigLine(const CString& sName, const CString& sValue, CUser* pUser, CChan* pChan) +void OnWriteUserConfig(CFile& Config) +void OnWriteChanConfig(CFile& Config, CChan& Chan) +EModRet OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const CString& sFile, unsigned long uFileSize) +void OnChanPermission(const CNick& OpNick, const CNick& Nick, CChan& Channel, unsigned char uMode, bool bAdded, bool bNoChange) +void OnOp(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) +void OnDeop(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) +void OnVoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) +void OnDevoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) +void OnMode(const CNick& OpNick, CChan& Channel, char uMode, const CString& sArg, bool bAdded, bool bNoChange) +void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) +EModRet OnRaw(CString& sLine) +EModRet OnStatusCommand(CString& sCommand) +void OnModCommand(const CString& sCommand) +void OnModNotice(const CString& sMessage) +void OnModCTCP(const CString& sMessage) +void OnQuit(const CNick& Nick, const CString& sMessage, const vector& vChans) +void OnNick(const CNick& Nick, const CString& sNewNick, const vector& vChans) +void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) +void OnJoin(const CNick& Nick, CChan& Channel) +void OnPart(const CNick& Nick, CChan& Channel) +EModRet OnChanBufferStarting(CChan& Chan, CClient& Client) +EModRet OnChanBufferEnding(CChan& Chan, CClient& Client) +EModRet OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine) +EModRet OnPrivBufferPlayLine(CClient& Client, CString& sLine) +void OnClientLogin() +void OnClientDisconnect() +EModRet OnUserRaw(CString& sLine) +EModRet OnUserCTCPReply(CString& sTarget, CString& sMessage) +EModRet OnUserCTCP(CString& sTarget, CString& sMessage) +EModRet OnUserAction(CString& sTarget, CString& sMessage) +EModRet OnUserMsg(CString& sTarget, CString& sMessage) +EModRet OnUserNotice(CString& sTarget, CString& sMessage) +EModRet OnUserJoin(CString& sChannel, CString& sKey) +EModRet OnUserPart(CString& sChannel, CString& sMessage) +EModRet OnUserTopic(CString& sChannel, CString& sTopic) +EModRet OnUserTopicRequest(CString& sChannel) +EModRet OnCTCPReply(CNick& Nick, CString& sMessage) +EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) +EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) +EModRet OnPrivAction(CNick& Nick, CString& sMessage) +EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) +EModRet OnPrivMsg(CNick& Nick, CString& sMessage) +EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) +EModRet OnPrivNotice(CNick& Nick, CString& sMessage) +EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) +EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) +bool OnServerCapAvailable(const CString& sCap)=false +void OnServerCapResult(const CString& sCap, bool bSuccess) +EModRet OnTimerAutoJoin(CChan& Channel) +bool OnEmbeddedWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl)=false + diff --git a/modules/modpython/modpython.i b/modules/modpython/modpython.i new file mode 100644 index 00000000..fe43d8c8 --- /dev/null +++ b/modules/modpython/modpython.i @@ -0,0 +1,176 @@ +/* + * 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. + */ + +%module znc_core %{ +#include +#include "../Utils.h" +#include "../Socket.h" +#include "../Modules.h" +#include "../Nick.h" +#include "../Chan.h" +#include "../User.h" +#include "../Client.h" +#include "../IRCSock.h" +#include "../Listener.h" +#include "../HTTPSock.h" +#include "../Template.h" +#include "../WebModules.h" +#include "../znc.h" +#include "../Server.h" +#include "../ZNCString.h" +#include "../DCCBounce.h" +#include "../DCCSock.h" +#include "../FileUtils.h" +#include "modpython/module.h" + +class CPyRetString { +public: + CString& s; + CPyRetString(CString& S) : s(S) {} + static PyObject* wrap(CString& S) { + CPyRetString* x = new CPyRetString(S); + return SWIG_NewInstanceObj(x, SWIG_TypeQuery("CPyRetString*"), SWIG_POINTER_OWN); + } +}; + +#define stat struct stat +using std::allocator; +%} + +%include +%include +%include +%include + +namespace std { + template class set { + public: + set(); + set(const set&); + }; +} + +%include "modpython/cstring.i" +%template(_stringlist) std::list; +/*%typemap(out) std::list { + std::list::const_iterator i; + unsigned int j; + int len = $1.size(); + SV **svs = new SV*[len]; + for (i=$1.begin(), j=0; i!=$1.end(); i++, j++) { + svs[j] = sv_newmortal(); + SwigSvFromString(svs[j], *i); + } + AV *myav = av_make(len, svs); + delete[] svs; + $result = newRV_noinc((SV*) myav); + sv_2mortal($result); + argvi++; +}*/ + +%typemap(out) CModules::ModDirList %{ + $result = PyList_New($1.size()); + if ($result) { + for (size_t i = 0; !$1.empty(); $1.pop(), ++i) { + PyList_SetItem($result, i, Py_BuildValue("ss", $1.front().first.c_str(), $1.front().second.c_str())); + } + } +%} + +%typemap(in) CString& { + String* p; + int res = SWIG_IsOK(SWIG_ConvertPtr($input, (void**)&p, SWIG_TypeQuery("String*"), 0)); + if (SWIG_IsOK(res)) { + $1 = &p->s; + } else { + SWIG_exception_fail(SWIG_ArgError(res), "need znc.String object as argument $argnum $1_name"); + } +} + +#define u_short unsigned short +#define u_int unsigned int +#include "../ZNCString.h" +%include "../defines.h" +%include "../Utils.h" +%include "../Csocket.h" +%template(ZNCSocketManager) TSocketManager; +%include "../Socket.h" +%include "../DCCBounce.h" +%include "../DCCSock.h" +%include "../FileUtils.h" +%include "../Modules.h" +%include "../Nick.h" +%include "../Chan.h" +%include "../User.h" +%include "../Client.h" +%include "../IRCSock.h" +%include "../Listener.h" +%include "../HTTPSock.h" +%include "../Template.h" +%include "../WebModules.h" +%include "../znc.h" +%include "../Server.h" + +%include "modpython/module.h" + +/* Really it's CString& inside, but SWIG shouldn't know that :) */ +class CPyRetString { + CPyRetString(); +public: + CString s; +}; + +%extend CModule { + MCString_iter BeginNV_() { + return MCString_iter($self->BeginNV()); + } + bool ExistsNV(const CString& sName) { + return $self->EndNV() != $self->FindNV(sName); + } +} + +%extend CModules { + void push_back(CModule* p) { + $self->push_back(p); + } + bool removeModule(CModule* p) { + for (CModules::iterator i = $self->begin(); $self->end() != i; ++i) { + if (*i == p) { + $self->erase(i); + return true; + } + } + return false; + } +} + +/* Web */ + +%template(StrPair) pair; +%template(VPair) vector >; +typedef vector > VPair; +%template(VWebSubPages) vector; + +%inline %{ + void VPair_Add2Str_(VPair* self, const CString& a, const CString& b) { + self->push_back(std::make_pair(a, b)); + } +%} + +%extend CTemplate { + void set(const CString& key, const CString& value) { + (*$self)[key] = value; + } +} + +%inline %{ + TWebSubPage CreateWebSubPage_(const CString& sName, const CString& sTitle, const VPair& vParams, unsigned int uFlags) { + return new CWebSubPage(sName, sTitle, vParams, uFlags); + } +%} + diff --git a/modules/modpython/module.h b/modules/modpython/module.h new file mode 100644 index 00000000..a1bc82e4 --- /dev/null +++ b/modules/modpython/module.h @@ -0,0 +1,221 @@ +#pragma once + +class String { +public: + CString s; +}; + +class CModPython; + +class CPyModule : public CModule { + PyObject* m_pyObj; + CModPython* m_pModPython; + VWebSubPages* _GetSubPages(); +public: + CPyModule(CUser* pUser, const CString& sModName, const CString& sDataPath, + PyObject* pyObj, CGlobalModule* pModPython) + : CModule(NULL, pUser, sModName, sDataPath) { + m_pyObj = pyObj; + Py_INCREF(pyObj); + m_pModPython = reinterpret_cast(pModPython); + } + PyObject* GetPyObj() { // borrows + return m_pyObj; + } + PyObject* GetNewPyObj() { + Py_INCREF(m_pyObj); + return m_pyObj; + } + void DeletePyModule() { + Py_CLEAR(m_pyObj); + delete this; + } + CString GetPyExceptionStr(); + CModPython* GetModPython() { + return m_pModPython; + } + + virtual bool OnBoot(); + virtual bool WebRequiresLogin(); + virtual bool WebRequiresAdmin(); + virtual CString GetWebMenuTitle(); + virtual bool OnWebPreRequest(CWebSock& WebSock, const CString& sPageName); + virtual bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl); + virtual VWebSubPages& GetSubPages(); + virtual void OnPreRehash(); + virtual void OnPostRehash(); + virtual void OnIRCDisconnected(); + virtual void OnIRCConnected(); + virtual EModRet OnIRCConnecting(CIRCSock *pIRCSock); + virtual EModRet OnIRCRegistration(CString& sPass, CString& sNick, CString& sIdent, CString& sRealName); + virtual EModRet OnBroadcast(CString& sMessage); + virtual EModRet OnConfigLine(const CString& sName, const CString& sValue, CUser* pUser, CChan* pChan); + virtual void OnWriteUserConfig(CFile& Config); + virtual void OnWriteChanConfig(CFile& Config, CChan& Chan); + virtual EModRet OnDCCUserSend(const CNick& RemoteNick, unsigned long uLongIP, unsigned short uPort, const CString& sFile, unsigned long uFileSize); + virtual void OnChanPermission(const CNick& OpNick, const CNick& Nick, CChan& Channel, unsigned char uMode, bool bAdded, bool bNoChange); + virtual void OnOp(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange); + virtual void OnDeop(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange); + virtual void OnVoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange); + virtual void OnDevoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange); + virtual void OnMode(const CNick& OpNick, CChan& Channel, char uMode, const CString& sArg, bool bAdded, bool bNoChange); + virtual void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs); + virtual EModRet OnRaw(CString& sLine); + virtual EModRet OnStatusCommand(CString& sCommand); + virtual void OnModCommand(const CString& sCommand); + virtual void OnModNotice(const CString& sMessage); + virtual void OnModCTCP(const CString& sMessage); + virtual void OnQuit(const CNick& Nick, const CString& sMessage, const vector& vChans); + virtual void OnNick(const CNick& Nick, const CString& sNewNick, const vector& vChans); + virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage); + virtual void OnJoin(const CNick& Nick, CChan& Channel); + virtual void OnPart(const CNick& Nick, CChan& Channel); + virtual EModRet OnChanBufferStarting(CChan& Chan, CClient& Client); + virtual EModRet OnChanBufferEnding(CChan& Chan, CClient& Client); + virtual EModRet OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine); + virtual EModRet OnPrivBufferPlayLine(CClient& Client, CString& sLine); + virtual void OnClientLogin(); + virtual void OnClientDisconnect(); + virtual EModRet OnUserRaw(CString& sLine); + virtual EModRet OnUserCTCPReply(CString& sTarget, CString& sMessage); + virtual EModRet OnUserCTCP(CString& sTarget, CString& sMessage); + virtual EModRet OnUserAction(CString& sTarget, CString& sMessage); + virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage); + virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage); + virtual EModRet OnUserJoin(CString& sChannel, CString& sKey); + virtual EModRet OnUserPart(CString& sChannel, CString& sMessage); + virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic); + virtual EModRet OnUserTopicRequest(CString& sChannel); + virtual EModRet OnCTCPReply(CNick& Nick, CString& sMessage); + virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage); + virtual EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage); + virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage); + virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage); + virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage); + virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage); + virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage); + virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage); + virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic); + virtual bool OnServerCapAvailable(const CString& sCap); + virtual void OnServerCapResult(const CString& sCap, bool bSuccess); + virtual EModRet OnTimerAutoJoin(CChan& Channel); + bool OnEmbeddedWebRequest(CWebSock&, const CString&, CTemplate&); +}; + +static inline CPyModule* AsPyModule(CModule* p) { + return dynamic_cast(p); +} + +inline CPyModule* CreatePyModule(CUser* pUser, const CString& sModName, const CString& sDataPath, PyObject* pyObj, CGlobalModule* pModPython) { + return new CPyModule(pUser, sModName, sDataPath, pyObj, pModPython); +} + +class CPyTimer : public CTimer { + PyObject* m_pyObj; + CModPython* m_pModPython; +public: + CPyTimer(CPyModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription, PyObject* pyObj) + : CTimer (pModule, uInterval, uCycles, sLabel, sDescription), m_pyObj(pyObj) { + Py_INCREF(pyObj); + pModule->AddTimer(this); + m_pModPython = pModule->GetModPython(); + } + virtual void RunJob(); + PyObject* GetPyObj() { return m_pyObj; } + PyObject* GetNewPyObj() { + Py_INCREF(m_pyObj); + return m_pyObj; + } + ~CPyTimer(); +}; + +inline CPyTimer* CreatePyTimer(CPyModule* pModule, unsigned int uInterval, unsigned int uCycles, + const CString& sLabel, const CString& sDescription, PyObject* pyObj) { + return new CPyTimer(pModule, uInterval, uCycles, sLabel, sDescription, pyObj); +} + +class CPySocket : public CSocket { + PyObject* m_pyObj; + CModPython* m_pModPython; +public: + CPySocket(CPyModule* pModule, PyObject* pyObj) : CSocket(pModule), m_pyObj(pyObj) { + Py_INCREF(pyObj); + m_pModPython = pModule->GetModPython(); + } + PyObject* GetPyObj() { return m_pyObj; } + PyObject* GetNewPyObj() { + Py_INCREF(m_pyObj); + return m_pyObj; + } + ~CPySocket(); + virtual void Connected(); + virtual void Disconnected(); + virtual void Timeout(); + virtual void ConnectionRefused(); + virtual void ReadData(const char *data, size_t len); + virtual void ReadLine(const CString& sLine); + virtual Csock* GetSockObj(const CString& sHost, unsigned short uPort); + PyObject* WriteBytes(PyObject* data); +}; + +inline CPySocket* CreatePySocket(CPyModule* pModule, PyObject* pyObj) { + return new CPySocket(pModule, pyObj); +} + +inline bool HaveIPv6_() { +#ifdef HAVE_IPV6 + return true; +#endif + return false; +} + +inline bool HaveSSL_() { +#ifdef HAVE_LIBSSL + return true; +#endif + return false; +} + +inline bool HaveCAres_() { +#ifdef HAVE_C_ARES + return true; +#endif + return false; +} + +inline int GetSOMAXCONN() { + return SOMAXCONN; +} + +inline int GetVersionMajor() { + return VERSION_MAJOR; +} + +inline int GetVersionMinor() { + return VERSION_MINOR; +} + +inline double GetVersion() { + return VERSION; +} + +inline CString GetVersionExtra() { + return VERSION_EXTRA; +} + +class MCString_iter { +public: + MCString_iter() {} + MCString::iterator x; + MCString_iter(MCString::iterator z) : x(z) {} + void plusplus() { + ++x; + } + CString get() { + return x->first; + } + bool is_end(CModule* m) { + return m->EndNV() == x; + } +}; + diff --git a/modules/modpython/znc.py b/modules/modpython/znc.py new file mode 100644 index 00000000..14d622ba --- /dev/null +++ b/modules/modpython/znc.py @@ -0,0 +1,377 @@ +from znc_core import * +import imp +import re +import traceback +import collections + +class Socket: + def _Accepted(self, host, port): + psock = self.OnAccepted(host, port) + print(psock) + try: + return psock._csock + except AttributeError: + return None + def GetModule(self): + return AsPyModule(self._csock.GetModule()).GetNewPyObj() + def Listen(self, addrtype='all', port=None, bindhost='', ssl=False, maxconns=GetSOMAXCONN(), timeout=0): + addr = ADDR_ALL + addrtype = addrtype.lower() + if addrtype == 'ipv4': + addr = ADDR_IPV4ONLY + elif addrtype == 'ipv6': + addr = ADDR_IPV6ONLY + elif addrtype != 'all': + raise ValueError("Specified addrtype [{0}] isn't supported".format(addrtype)) + if port is None: + return self.GetModule().GetManager().ListenRand( + "python socket for {0}".format(self.GetModule().GetModName()), + bindhost, + ssl, + maxconns, + self._csock, + timeout, + addr + ) + if self.GetModule().GetManager().ListenHost( + port, + 'python socket for {0}'.format(self.GetModule().GetModName()), + bindhost, + ssl, + maxconns, + self._csock, + timeout, + addr): + return port + return 0 + def Connect(self, host, port, timeout=60, ssl=False, bindhost=''): + return self.GetModule().GetManager().Connect( + host, + port, + 'python conn socket for {0}'.format(self.GetModule().GetModName()), + timeout, + ssl, + bindhost, + self._csock) + def Write(self, data): + if (isinstance(data, str)): + return self._csock.Write(data) + raise TypeError('socket.Write needs str. If you want binary data, use WriteBytes') + + def Init(self, *a, **b): pass + def OnConnected(self): pass + def OnDisconnected(self): pass + def OnTimeout(self): pass + def OnConnectionRefused(self): pass + def OnReadData(self, bytess): pass + def OnReadLine(self, line): pass + def OnAccepted(self, host, port): pass + def OnShutdown(self): pass + +class Timer: + def GetModule(self): + return AsPyModule(self._ctimer.GetModule()).GetNewPyObj() + def RunJob(self): pass + def OnShutdown(self): pass + +class ModuleNVIter(collections.Iterator): + def __init__(self, cmod): + self._cmod = cmod + self.it = cmod.BeginNV_() + def __next__(self): + if self.it.is_end(self._cmod): + raise StopIteration + res = self.it.get() + self.it.plusplus() + return res + +class ModuleNV(collections.MutableMapping): + def __init__(self, cmod): + self._cmod = cmod + def __setitem__(self, key, value): + self._cmod.SetNV(key, value) + def __getitem__(self, key): + if not self._cmod.ExistsNV(key): + raise KeyError + return self._cmod.GetNV(key) + def __contains__(self, key): + return self._cmod.ExistsNV(key) + def __delitem__(self, key): + self._cmod.DelNV(key) + def keys(self): + return ModuleNVIter(self._cmod) + __iter__ = keys + def __len__(self): + raise NotImplemented + +class Module: + def OnLoad(self, sArgs, sMessage): + return True + def _GetSubPages(self): + return self.GetSubPages() + def CreateSocket(self, socketclass, *the, **rest): + socket = socketclass() + socket._csock = CreatePySocket(self._cmod, socket) + socket.Init(*the, **rest) + return socket + def CreateTimer(self, timer, interval=10, cycles=1, label='pytimer', description='Some python timer'): + t = timer() + t._ctimer = CreatePyTimer(self._cmod, interval, cycles, label, description, t) + return t + def GetNV(self): + return self._nv + def GetSubPages(self): pass + def OnShutdown(self): pass + def OnBoot(self): pass + def WebRequiresLogin(self): pass + def WebRequiresAdmin(self): pass + def GetWebMenuTitle(self): pass + def OnWebPreRequest(self, WebSock, sPageName): pass + def OnWebRequest(self, WebSock, sPageName, Tmpl): pass + def OnPreRehash(self): pass + def OnPostRehash(self): pass + def OnIRCDisconnected(self): pass + def OnIRCConnected(self): pass + def OnIRCConnecting(self, IRCSock): pass + def OnIRCRegistration(self, sPass, sNick, sIdent, sRealName): pass + def OnBroadcast(self, sMessage): pass + def OnConfigLine(self, sName, sValue, pUser, pChan): pass + def OnWriteUserConfig(self, Config): pass + def OnWriteChanConfig(self, Config, Chan): pass + def OnDCCUserSend(self, RemoteNick, uLongIP, uPort, sFile, uFileSize): pass + def OnChanPermission(self, OpNick, Nick, Channel, uMode, bAdded, bNoChange): pass + def OnOp(self, OpNick, Nick, Channel, bNoChange): pass + def OnDeop(self, OpNick, Nick, Channel, bNoChange): pass + def OnVoice(self, OpNick, Nick, Channel, bNoChange): pass + def OnDevoice(self, OpNick, Nick, Channel, bNoChange): pass + def OnMode(self, OpNick, Channel, uMode, sArg, bAdded, bNoChange): pass + def OnRawMode(self, OpNick, Channel, sModes, sArgs): pass + def OnRaw(self, sLine): pass + def OnStatusCommand(self, sCommand): pass + def OnModCommand(self, sCommand): pass + def OnModNotice(self, sMessage): pass + def OnModCTCP(self, sMessage): pass + def OnQuit(self, Nick, sMessage, vChans): pass + def OnNick(self, Nick, sNewNick, vChans): pass + def OnKick(self, OpNick, sKickedNick, Channel, sMessage): pass + def OnJoin(self, Nick, Channel): pass + def OnPart(self, Nick, Channel): pass + def OnChanBufferStarting(self, Chan, Client): pass + def OnChanBufferEnding(self, Chan, Client): pass + def OnChanBufferPlayLine(self, Chan, Client, sLine): pass + def OnPrivBufferPlayLine(self, Client, sLine): pass + def OnClientLogin(self): pass + def OnClientDisconnect(self): pass + def OnUserRaw(self, sLine): pass + def OnUserCTCPReply(self, sTarget, sMessage): pass + def OnUserCTCP(self, sTarget, sMessage): pass + def OnUserAction(self, sTarget, sMessage): pass + def OnUserMsg(self, sTarget, sMessage): pass + def OnUserNotice(self, sTarget, sMessage): pass + def OnUserJoin(self, sChannel, sKey): pass + def OnUserPart(self, sChannel, sMessage): pass + def OnUserTopic(self, sChannel, sTopic): pass + def OnUserTopicRequest(self, sChannel): pass + def OnCTCPReply(self, Nick, sMessage): pass + def OnPrivCTCP(self, Nick, sMessage): pass + def OnChanCTCP(self, Nick, Channel, sMessage): pass + def OnPrivAction(self, Nick, sMessage): pass + def OnChanAction(self, Nick, Channel, sMessage): pass + def OnPrivMsg(self, Nick, sMessage): pass + def OnChanMsg(self, Nick, Channel, sMessage): pass + def OnPrivNotice(self, Nick, sMessage): pass + def OnChanNotice(self, Nick, Channel, sMessage): pass + def OnTopic(self, Nick, Channel, sTopic): pass + def OnServerCapAvailable(self, sCap): pass + def OnServerCapResult(self, sCap, bSuccess): pass + def OnTimerAutoJoin(self, Channel): pass + def OnEmbeddedWebRequest(self, WebSock, sPageName, Tmpl): pass + +def make_inherit(cl, parent, attr): + def make_caller(parent, name, attr): + return lambda self, *a: parent.__dict__[name](self.__dict__[attr], *a) + while True: + for x in parent.__dict__: + if x[:1] != '_' and x not in cl.__dict__: + setattr(cl, x, make_caller(parent, x, attr)) + if '_s' in parent.__dict__: + parent = parent._s + else: + break +make_inherit(Socket, CPySocket, '_csock') +make_inherit(Module, CPyModule, '_cmod') +make_inherit(Timer, CPyTimer, '_ctimer') + +def find_open(modname): + '''Returns (pymodule, datapath)''' + for d in CModules.GetModDirs(): + # d == (libdir, datadir) + try: + x = imp.find_module(modname, [d[0]]) + except ImportError: + # no such file in dir d + continue + # x == (, './modules/admin.so', ('.so', 'rb', 3)) + # x == (, './modules/pythontest.py', ('.py', 'U', 1)) + if x[0] is None: + # the same + continue + if x[2][0] == '.so': + try: + pymodule = imp.load_module(modname, *x) + except ImportError: + # found needed .so but can't load it... + # maybe it's normal (non-python) znc module? + # another option here could be to "continue" + # search of python module in other moddirs. + # but... we respect C++ modules ;) + return (None, None) + finally: + x[0].close() + else: + # this is not .so, so it can be only python module .py or .pyc + try: + pymodule = imp.load_module(modname, *x) + finally: + x[0].close() + return (pymodule, d[1]) + else: + # nothing found + return (None, None) + +def load_module(modname, args, user, retmsg, modpython): + '''Returns 0 if not found, 1 on loading error, 2 on success''' + if re.search(r'[^a-zA-Z0-9_]', modname) is not None: + retmsg.s = 'Module names can only contain letters, numbers and underscores, [{0}] is invalid.'.format(modname) + return 1 + pymodule, datapath = find_open(modname) + if pymodule is None: + return 0 + if modname not in pymodule.__dict__: + retmsg.s = "Python module [{0}] doesn't have class named [{1}]".format(pymodule.__file__, modname) + return 1 + cl = pymodule.__dict__[modname] + module = cl() + module._cmod = CreatePyModule(user, modname, datapath, module, modpython) + module._nv = ModuleNV(module._cmod) + descr = None + if '__doc__' in cl.__dict__: + descr = cl.__doc__ + if descr is None: + descr = '< Placeholder for a description >' + module.SetDescription(descr) + module.SetArgs(args) + module.SetModPath(pymodule.__file__) + user.GetModules().push_back(module._cmod) + try: + loaded = True + if not module.OnLoad(args, retmsg): + if retmsg.s == '': + retmsg.s = 'Module [{0}] aborted.'.format(modname) + else: + retmsg.s = 'Module [{0}] aborted: {1}'.format(modname, retmsg.s) + loaded = False + except BaseException: + if retmsg.s == '': + retmsg.s = 'Got exception: {0}'.format(traceback.format_exc()) + else: + retmsg.s = '{0}; Got exception: {1}'.format(retmsg.s, traceback.format_exc()) + loaded = False + except: + if retmsg.s == '': + retmsg.s = 'Got exception.' + else: + retmsg.s = '{0}; Got exception.'.format(retmsg.s) + loaded = False + + if loaded: + if retmsg.s == '': + retmsg.s = "Loaded module [{0}] [{1}]".format(modname, pymodule.__file__) + else: + retmsg.s = "Loaded module [{0}] [{2}] [{1}]".format(modname, pymodule.__file__, retmsg.s) + return 2 + print(retmsg.s) + + unload_module(module) + return 1 + +def unload_module(module): + module.OnShutdown() + cmod = module._cmod + del module._cmod + cmod.GetUser().GetModules().removeModule(cmod) + cmod.DeletePyModule() + del cmod + +def get_mod_info(modname, retmsg, modinfo): + '''0-not found, 1-error, 2-success''' + pymodule, = find_open(modname) + if pymodule is None: + return 0 + if modname not in pymodule.__dict__: + retmsg.s = "Python module [{0}] doesn't have class named [{1}]".format(pymodule.__file__, modname) + return 1 + cl = pymodule.__dict__[modname] + descr = None + if '__doc__' in cl.__dict__: + descr = cl.__doc__ + if descr is None: + descr = '< Placeholder for a description >' + modinfo.SetGlobal(False) + modinfo.SetDescription(descr) + modinfo.SetName(modname) + modinfo.SetPath(pymodule.__file__) + return 2 + +def get_mod_info_path(path, modname, modinfo): + try: + x = imp.find_module(modname, [path]) + except ImportError: + return 0 + # x == (, './modules/admin.so', ('.so', 'rb', 3)) + # x == (, './modules/pythontest.py', ('.py', 'U', 1)) + if x[0] is None: + return 0 + try: + pymodule = imp.load_module(modname, *x) + except ImportError: + return 0 + finally: + x[0].close() + if modname not in pymodule.__dict__: + return 0 + cl = pymodule.__dict__[modname] + descr = None + if '__doc__' in cl.__dict__: + descr = cl.__doc__ + if descr is None: + descr = '< Placeholder for a description >' + modinfo.SetGlobal(False) + modinfo.SetDescription(descr) + modinfo.SetName(modname) + modinfo.SetPath(pymodule.__file__) + return 1 + +CONTINUE = CModule.CONTINUE +HALT = CModule.HALT +HALTMODS = CModule.HALTMODS +HALTCORE = CModule.HALTCORE +UNLOAD = CModule.UNLOAD + +HaveSSL = HaveSSL_() +HaveCAres = HaveCAres_() +HaveIPv6 = HaveIPv6_() +Version = GetVersion() +VersionMajor = GetVersionMajor() +VersionMinor = GetVersionMinor() +VersionExtra = GetVersionExtra() + +def CreateWebSubPage(name, title='', params=dict(), admin=False): + vpair = VPair() + for k, v in params.items(): + VPair_Add2Str_(vpair, k, v) + flags = 0 + if admin: + flags |= CWebSubPage.F_ADMIN + return CreateWebSubPage_(name, title, vpair, flags) +