diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml deleted file mode 100644 index 485733521..000000000 --- a/.github/workflows/ccpp.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: C/C++ CI - -on: - pull_request: - branches: - - develop - -jobs: - build: - name: compile test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: install dependencies - run: sudo apt-get install tcl tcl-dev openssl libssl-dev - - name: configure - run: ./configure - - name: make config - run: make config - - name: make - run: make - - name: make install - run: make install diff --git a/.github/workflows/configure_flags.yml b/.github/workflows/configure_flags.yml deleted file mode 100644 index 60a8c07bb..000000000 --- a/.github/workflows/configure_flags.yml +++ /dev/null @@ -1,41 +0,0 @@ -on: - workflow_dispatch: - inputs: - name: - description: 'Test configure flags' - -jobs: - configure-nosslflag: - name: Configure, --disable-tls - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: sudo apt-get install tcl tcl-dev - - run: ./configure --disable-tls - - run: make config - - run: make - - run: make install - - - configure-noipv6: - name: Configure, --disable-ipv6 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: sudo apt-get install tcl tcl-dev - - run: ./configure --disable-ipv6 - - run: make config - - run: make - - run: make install - - - configure-notdns: - name: Configure, --disable-tdns - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: sudo apt-get install tcl tcl-dev - - run: ./configure --disable-tdns - - run: make config - - run: make - - run: make install diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml new file mode 100644 index 000000000..d76a5e6c7 --- /dev/null +++ b/.github/workflows/dependencies.yml @@ -0,0 +1,96 @@ +name: Tcl/SSL Versions + +on: + pull_request: + branches: [ develop ] + push: + branches: [ develop ] + +jobs: + tcl-versions: + name: Tcl Versions + strategy: + matrix: + tcl_version: [ '8.5.19', '8.6.14', '8.7a5', '9.0b2' ] + continue-on-error: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install dependencies + run: sudo apt-get install openssl libssl-dev + - name: Build Tcl + run: | + wget http://prdownloads.sourceforge.net/tcl/tcl${{ matrix.tcl_version }}-src.tar.gz && \ + tar xzf tcl${{ matrix.tcl_version }}-src.tar.gz && \ + cd tcl${{ matrix.tcl_version }}/unix && \ + ./configure --prefix=$HOME/tcl && \ + make -j4 && make install + - name: Build + run: ./configure --with-tcl=$HOME/tcl/lib && LD_LIBRARY_PATH=$HOME/tcl/lib make config eggdrop + ssl-version-10: + name: OpenSSL 1.0 + continue-on-error: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + path: 'eggdrop' + - name: install dependencies + run: sudo apt-get install tcl tcl-dev + - name: Build OpenSSL + run: | + wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz && tar xzf openssl-1.0.2u.tar.gz && \ + cd openssl-1.0.2u && ./config --prefix=$HOME/ssl -fPIC && make -j4 && make install_sw + - name: Build + run: cd $GITHUB_WORKSPACE/eggdrop && ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib && LD_LIBRARY_PATH=$HOME/ssl/lib make config eggdrop + ssl-version-11: + name: OpenSSL 1.1 + continue-on-error: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: openssl/openssl + ref: 'OpenSSL_1_1_1w' + path: 'openssl' + - name: Build OpenSSL + run: | + cd $GITHUB_WORKSPACE/openssl && ./config --prefix=$HOME/ssl && make -j4 && make install_sw + - name: install dependencies + run: sudo apt-get install tcl tcl-dev + - uses: actions/checkout@v4 + with: + path: 'eggdrop' + - name: Build + run: cd $GITHUB_WORKSPACE/eggdrop && ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib && LD_LIBRARY_PATH=$HOME/ssl/lib make config eggdrop + ssl-versions-3x: + name: OpenSSL 3.x + strategy: + matrix: + ssl_version: [ '3.0', '3.1', '3.2', '3.3' ] + continue-on-error: true + runs-on: ubuntu-latest + steps: + - uses: oprypin/find-latest-tag@v1 + with: + repository: openssl/openssl + releases-only: true + prefix: 'openssl-' + regex: "${{ matrix.ssl_version }}.[0-9]+" + sort-tags: true + id: openssl + - uses: actions/checkout@v4 + with: + repository: openssl/openssl + ref: ${{ steps.openssl.outputs.tag }} + path: 'openssl' + - name: Build OpenSSL + run: | + cd $GITHUB_WORKSPACE/openssl && ./config --prefix=$HOME/ssl && make -j4 && make install_sw + - uses: actions/checkout@v4 + with: + path: 'eggdrop' + - name: install dependencies + run: sudo apt-get install tcl tcl-dev + - name: Build + run: cd $GITHUB_WORKSPACE/eggdrop && ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib64 && LD_LIBRARY_PATH=$HOME/ssl/lib64 make config eggdrop diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml new file mode 100644 index 000000000..e1dc3be6b --- /dev/null +++ b/.github/workflows/make.yml @@ -0,0 +1,39 @@ +name: Eggdrop Compile + +on: + pull_request: + branches: [ develop ] + push: + branches: [ develop ] + +jobs: + default-build: + name: Compile Test + strategy: + matrix: + cc: [ 'gcc', 'clang' ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install dependencies + run: sudo apt-get install clang tcl tcl-dev openssl libssl-dev + - name: Build + env: + CC: ${{ matrix.cc }} + run: ./configure && make config && make -j4 && make install + feature-test: + name: Features + continue-on-error: true + needs: default-build + strategy: + matrix: + conf_tls: [ '', '--disable-tls' ] + conf_ipv6: [ '', '--disable-ipv6' ] + conf_tdns: [ '', '--disable-tdns' ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install dependencies + run: sudo apt-get install tcl tcl-dev openssl libssl-dev + - name: Build + run: ./configure ${{ matrix.conf_tls }} ${{ matrix.conf_ipv6 }} ${{ matrix.conf_tdns }} && make config && make -j4 diff --git a/.github/workflows/misc.yml b/.github/workflows/misc.yml new file mode 100644 index 000000000..b6a894b30 --- /dev/null +++ b/.github/workflows/misc.yml @@ -0,0 +1,45 @@ +name: Check autotools/makedepend + +on: + pull_request: + branches: [ develop ] + push: + branches: [ develop ] + +jobs: + autotools-check: + name: Check if misc/runautotools needs to be run + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install dependencies + run: sudo apt-get install build-essential autoconf + - name: Stage configure with revision removed + run: | + for i in `find . -name configure`; do sed -i 's/From configure.ac .*//' $i; git add $i; done + - name: Run autotools + run: misc/runautotools + - name: Remove configure revision again + run: | + for i in `find . -name configure`; do sed -i 's/From configure.ac .*//' $i; done + - name: Check diff + run: | + git diff | tee .gitdiff + if [ -s .gitdiff ]; then + exit 1 + fi + makedepend-check: + name: Check if misc/makedepend needs to be run + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install dependencies + run: sudo apt-get install build-essential autoconf tcl-dev tcl openssl libssl-dev + - name: Run makedepend + run: misc/makedepend + - name: Check diff + run: | + git diff | tee .gitdiff + if [ -s .gitdiff ]; then + exit 1 + fi diff --git a/INSTALL b/INSTALL index 81e2a5184..1db949afb 100644 --- a/INSTALL +++ b/INSTALL @@ -158,5 +158,5 @@ the README file. If not, then READ IT!&@#%@! Have fun with Eggdrop! - Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2023 Eggheads + Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2024 Eggheads Development Team diff --git a/README b/README index 2d74900ca..468549297 100644 --- a/README +++ b/README @@ -216,5 +216,5 @@ OBTAINING HELP - Don't ask to ask- just state your question, along with any relevant details and error messages -Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2023 Eggheads +Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2024 Eggheads Development Team diff --git a/config.h.in b/config.h.in index 30cc2b7da..4ac55ced2 100644 --- a/config.h.in +++ b/config.h.in @@ -132,9 +132,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the `isascii' function. */ -#undef HAVE_ISASCII - /* Define to 1 if you have the `crypto' library (-lcrypto). */ #undef HAVE_LIBCRYPTO @@ -287,6 +284,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_TIME_H + /* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use `HAVE_STRUCT_TM_TM_ZONE' instead. */ #undef HAVE_TM_ZONE diff --git a/configure b/configure index 2a6387317..9c1c4eeb9 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac 98f8972a. +# From configure.ac 4af46aa3. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Eggdrop 1.9.5. # @@ -2789,7 +2789,6 @@ as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H" as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H" -as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H" as_fn_append ac_header_c_list " sys/select.h sys_select_h HAVE_SYS_SELECT_H" as_fn_append ac_header_c_list " sys/socket.h sys_socket_h HAVE_SYS_SOCKET_H" as_fn_append ac_header_c_list " sys/param.h sys_param_h HAVE_SYS_PARAM_H" @@ -6855,8 +6854,6 @@ fi fi - - ac_fn_c_check_header_compile "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default" if test "x$ac_cv_header_arpa_inet_h" = xyes then : @@ -6940,6 +6937,12 @@ if test "x$ac_cv_header_sys_time_h" = xyes then : printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "time.h" "ac_cv_header_time_h" "$ac_includes_default" +if test "x$ac_cv_header_time_h" = xyes +then : + printf "%s\n" "#define HAVE_TIME_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" if test "x$ac_cv_header_unistd_h" = xyes @@ -8215,12 +8218,6 @@ if test "x$ac_cv_func_inet_aton" = xyes then : printf "%s\n" "#define HAVE_INET_ATON 1" >>confdefs.h -fi -ac_fn_c_check_func "$LINENO" "isascii" "ac_cv_func_isascii" -if test "x$ac_cv_func_isascii" = xyes -then : - printf "%s\n" "#define HAVE_ISASCII 1" >>confdefs.h - fi ac_fn_c_check_func "$LINENO" "memset_s" "ac_cv_func_memset_s" if test "x$ac_cv_func_memset_s" = xyes @@ -10635,7 +10632,6 @@ printf "%s\n" "#define EGG_TDNS 1" >>confdefs.h # Check for Python -EGG_PYTHON_ENABLE # Check whether --with-python-config was given. diff --git a/configure.ac b/configure.ac index 78a7e70d5..86756534d 100644 --- a/configure.ac +++ b/configure.ac @@ -91,9 +91,8 @@ EGG_CHECK_OS # Checks for header files. EGG_HEADER_STDC AC_HEADER_DIRENT -AC_CHECK_HEADERS_ONCE([sys/time.h]) -AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h locale.h netdb.h netinet/in.h stdio.h stdarg.h stddef.h sys/file.h sys/param.h sys/select.h sys/socket.h sys/time.h unistd.h wchar.h]) +AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h locale.h netdb.h netinet/in.h stdio.h stdarg.h stddef.h sys/file.h sys/param.h sys/select.h sys/socket.h sys/time.h time.h unistd.h wchar.h]) # Checks for typedefs, structures, and compiler characteristics. @@ -113,7 +112,7 @@ AX_TYPE_SOCKLEN_T AX_CREATE_STDINT_H([eggint.h]) # Checks for functions and their arguments. -AC_CHECK_FUNCS([dprintf explicit_bzero memset_explicit explicit_memset getrandom inet_aton isascii memset_s snprintf strlcpy vsnprintf]) +AC_CHECK_FUNCS([dprintf explicit_bzero memset_explicit explicit_memset getrandom inet_aton memset_s snprintf strlcpy vsnprintf]) AC_FUNC_SELECT_ARGTYPES EGG_FUNC_B64_NTOP AC_FUNC_MMAP @@ -165,7 +164,6 @@ EGG_TLS_DETECT EGG_TDNS_ENABLE # Check for Python -EGG_PYTHON_ENABLE EGG_PYTHON_WITHCONFIG diff --git a/doc/sphinx_source/modules/mod/python.rst b/doc/sphinx_source/modules/mod/python.rst index 55307eb6f..5e06a45b2 100644 --- a/doc/sphinx_source/modules/mod/python.rst +++ b/doc/sphinx_source/modules/mod/python.rst @@ -8,6 +8,11 @@ Python Module This module adds a Python interpreter to Eggdrop, allowing you to run Python scripts. +------------------- +System Requirements +------------------- +This module requires Python version 3.8 or higher in order to run. Similar to Tcl requirements, Eggdrop requires both python and python development libraries to be installed on the host machine. On Debian/Ubuntu machines, this means the packages ``python``, ``python-dev`` AND ``python-is-python3`` to be installed. The python-is-python3 updates symlinks on the host system that allow Eggdrop to find it. + -------------- Loading Python -------------- @@ -50,105 +55,3 @@ pysource ^^^^^^^^^^^^^^^^^^^^^^^ The ``pysource`` command is analogous to the Tcl ``source`` command, except that it loads a Python script into Eggdrop instead of a Tcl script. - ------------------------ -Eggdrop Python Commands ------------------------ - -The Python module is built to use the existing core Tcl commands integrated into Eggdrop via the ``eggdrop.tcl`` module. To call an existing Tcl command from Python, you can either load the entire catalog by running ``import eggdrop.tcl``, or be more specific by ``from eggdrop.tcl import putserv, putlog, chanlist``, etc. - -Arguments to the Tcl functions are automatically converted as follows: - -* ``None`` is converted to an empty Tcl object (the empty string, ``""``) -* ``List`` and ``Tuple`` is converted to a ``Tcl list`` -* ``Dict`` is converted to a ``Tcl dictionary`` -* Everything else is converted to a string using the str() method - -Return values from Tcl functions must be manually converted: - -* ``""`` the empty string is automatically converted to None -* everything else is returned as string -* ``Tcl list`` as string can be converted to a Python ``List`` using ``parse_tcl_list`` -* ``Tcl dictionary`` as string can be converted to a Python ``Dict`` using ``parse_tcl_list`` - -Additionally, a few extra python commands have been created for use without these conversions: - -^^^^^^^^^^^^^^^^ -bind -^^^^^^^^^^^^^^^^ - -The python version of the bind command is used to create a bind that triggers a python function. The python bind takes the same arguments as the Tcl binds, but here each argument is passed individually. For example, a bind that would look like ``bind pub * !foo myproc`` in Tcl is written as ``bind("pub", "*", "!foo", myproc)``. For more information on Eggsrop bind argument syntax please see :ref:`bind_types`. The eggdrop.tcl.bind command should not be used as it will attempt to call a Tcl proc. - -^^^^^^^^^^^^^^^^^^^^^^^ -parse_tcl_list -^^^^^^^^^^^^^^^^^^^^^^^ - -When a python script calls a Tcl command that returns a list via the eggdrop.tcl module, the return value will be a Tcl-formatted list- also simply known as a string. The ``parse_tcl_list`` command will convert the Tcl-formatted list into a Python list, which can then freely be used within the Python script. - -^^^^^^^^^^^^^^^^^^^^^^^ -parse_tcl_dict -^^^^^^^^^^^^^^^^^^^^^^^ - -When a python script calls a Tcl command that returns a dict via the eggdrop.tcl module, the return value will be a Tcl-formatted dict- also simply known as a string. The ``parse_tcl_dict`` command will c -onvert the Tcl-formatted dict into a Python list, which can then freely be used within the Python script. - ----------------- -Config variables ----------------- - -There are also some variables you can set in your config file: - - set allow-resync 0 - When two bots get disconnected, this setting allows them to create a - resync buffer which saves all changes done to the userfile during - the disconnect. When they reconnect, they will not have to transfer - the complete user file, but, instead, just send the resync buffer. - --------------------------------- -Writing an Eggdrop Python script --------------------------------- - -This is how to write a python script for Eggdrop. - -You can view examples of Python scripts in the exampleScripts folder included with this module. - -.. glossary:: - - bestfriend.py - This example script demonstrates how to use the parse_tcl_list() python command to convert a list returned by a Tcl command into a list that is usable by Python. - - greet.py - This is a very basic script that demonstrates how a Python script with binds can be run by Eggdrop. - - imdb.py - This script shows how to use an existing third-party module to extend a Python script, in this case retrieving information from imdb.com. - - listtls.py - This script demonstrates how to use parse-tcl_list() and parse_tcl_dict() to convert a list of dicts provided by Tcl into something that is usable by Python. - - urltitle.py - This script shows how to use an existing third-party module to extend a Python script, in this case using an http parser to collect title information from a provided web page. - - -^^^^^^^^^^^^^^ -Header section -^^^^^^^^^^^^^^ - -An Eggdrop python script requires you to import X Y and Z, in this format. - -^^^^^^^^^^^^ -Code Section -^^^^^^^^^^^^ - -Normal python code works here. To run a command from the Eggdrop Tcl library, use this format. - -Use this format all over. - -------------------------------------- -Writing a module for use with Eggdrop -------------------------------------- - -This is how you import a module for use with an egg python script. - - -Copyright (C) 2000 - 2024 Eggheads Development Team diff --git a/doc/sphinx_source/using/python.rst b/doc/sphinx_source/using/python.rst new file mode 100644 index 000000000..520aa1810 --- /dev/null +++ b/doc/sphinx_source/using/python.rst @@ -0,0 +1,113 @@ +======================= +Using the Python Module +======================= + +In Eggdrop 1.10.0, Eggdrop was shipped with a Python module that, similar to the existing core Tcl capability, allows Eggdrop to run python scripts. + +------------------- +System Requirements +------------------- +Similar to Tcl requirements, Eggdrop requires both python and python development libraries to be installed on the host machine. On Debian/Ubuntu machines, this requires the packages python-dev AND python-is-python3 to be installed. The python-is-python3 updates symlinks on the host system that allow Eggdrop to find it. + +-------------- +Loading Python +-------------- + +Put this line into your Eggdrop configuration file to load the python module:: + + loadmodule python + +To load a python script from your config file, place the .py file in the scripts/ folder and add the following line to your config:: + + pysource scripts/myscript.py + +----------------------- +Eggdrop Python Commands +----------------------- + +The Python module is built to use the existing core Tcl commands integrated into Eggdrop via the ``eggdrop.tcl`` module. To call an existing Tcl command from Python, you can either load the entire catalog by running ``import eggdrop.tcl``, or be more specific by ``from eggdrop.tcl import putserv, putlog, chanlist``, etc. + +Arguments to the Tcl functions are automatically converted as follows: + +* ``None`` is converted to an empty Tcl object (the empty string, ``""``) +* ``List`` and ``Tuple`` is converted to a ``Tcl list`` +* ``Dict`` is converted to a ``Tcl dictionary`` +* Everything else is converted to a string using the str() method + +Return values from Tcl functions must be manually converted: + +* ``""`` the empty string is automatically converted to None +* everything else is returned as string +* ``Tcl list`` as string can be converted to a Python ``List`` using ``parse_tcl_list`` +* ``Tcl dictionary`` as string can be converted to a Python ``Dict`` using ``parse_tcl_list`` + +^^^^^^^^^^^^^^^^ +bind +^^^^^^^^^^^^^^^^ + +An important difference to note is that Eggdrop Python has its own ``bind`` command implemented. You will generally want to create binds using the Python ``bind`` command and not import bind from eggdrop.tcl because a Python bind will call a Python function, whereas using the Tcl bind will call a Tcl function (not one from the script you are writing). + +The python version of the bind command is used to create a bind that triggers a python function. The python bind takes the same arguments as the Tcl binds, but here each argument is passed individually. For example, a bind that would look like ``bind pub * !foo myproc`` in Tcl is written as ``bind("pub", "*", "!foo", myproc)``. For more information on Eggsrop bind argument syntax please see :ref:`bind_types`. The eggdrop.tcl.bind command should not be used as it will attempt to call a Tcl proc. + +^^^^^^^^^^^^^^^^^^^^^^^ +parse_tcl_list +^^^^^^^^^^^^^^^^^^^^^^^ + +When a python script calls a Tcl command that returns a list via the eggdrop.tcl module, the return value will be a Tcl-formatted list- also simply known as a string. The ``parse_tcl_list`` command will convert the Tcl-formatted list into a Python list, which can then freely be used within the Python script. + +^^^^^^^^^^^^^^^^^^^^^^^ +parse_tcl_dict +^^^^^^^^^^^^^^^^^^^^^^^ + +When a python script calls a Tcl command that returns a dict via the eggdrop.tcl module, the return value will be a Tcl-formatted dict- also simply known as a string. The ``parse_tcl_dict`` command will convert the Tcl-formatted dict into a Python list, which can then freely be used within the Python script. + +-------------------------------- +Writing an Eggdrop Python script +-------------------------------- + +Some example scripts, complete with documentation, are included with the Python module that ships with Eggdrop (src/mod/python.mod/scripts). These scripts are included to help demonstrate script formatting and usage. The scripts are: + + +.. glossary:: + + bestfriend.py + This example script demonstrates how to use the parse_tcl_list() python command to convert a list returned by a Tcl command into a list that is usable by Python. + + greet.py + This is a very basic script that demonstrates how a Python script with binds can be run by Eggdrop. + + imdb.py + This script shows how to use an existing third-party module to extend a Python script, in this case retrieving information from imdb.com. + + listtls.py + This script demonstrates how to use parse-tcl_list() and parse_tcl_dict() to convert a list of dicts provided by Tcl into something that is usable by Python. + + urltitle.py + This script shows how to use an existing third-party module to extend a Python script, in this case using an http parser to collect title information from a provided web page. + + +^^^^^^^^^^^^^^ +Header section +^^^^^^^^^^^^^^ + +Python is able to call any Tcl command by importing the ``eggdrop`` module. For example, to use the ``putlog`` command in a python script, you would import it as:: + + from eggdrop.tcl import putlog + +and then call it using:: + + putlog("This is a logged message") + + +An important difference to note is that Eggdrop Python has its own ``bind`` command implemented. You will generally want to create binds using the Python ``bind`` command and not import bind from eggdrop.tcl because a Python bind will call a Python function, whereas using the Tcl bind will call a Tcl function (not one from the script you are writing). + +Where does python print go? + +------------------------------------- +Writing a module for use with Eggdrop +------------------------------------- + +This is how you import a module for use with an egg python script. + +Copyright (C) 2000 - 2024 Eggheads Development Team + diff --git a/doc/sphinx_source/using/tcl-commands.rst b/doc/sphinx_source/using/tcl-commands.rst index 6a662052c..339c728c3 100644 --- a/doc/sphinx_source/using/tcl-commands.rst +++ b/doc/sphinx_source/using/tcl-commands.rst @@ -2200,6 +2200,26 @@ setflags [ [channel]] Module: filesys +PBKDF2 Module +------------- + +^^^^^^^^^^^^^^^ +encpass2 +^^^^^^^^^^^^^^^ + + + Returns: a hash in the format of "$pbkdf2-$rounds=$$" where digest is the digest set in the config variable pbkdf2-method, rounds is the number of rounds set in the config variable pbkdf2-rounds, salt is the base64 salt used to generate the hash, and hash is the generated base64 hash. + + Module: pbkdf2 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +pbkdf2 [-bin] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Returns: a derived key from the provided "pass" string using "salt" and "rounds" count as specified in RFC 2898 as a hexadecimal string. Using the optional -bin flag will return the result as binary data. + + Module: pbkdf2 + Miscellaneous Commands ---------------------- diff --git a/eggdrop-basic.conf b/eggdrop-basic.conf index d14753d0e..849575610 100755 --- a/eggdrop-basic.conf +++ b/eggdrop-basic.conf @@ -300,9 +300,8 @@ set ssl-capath "/etc/ssl/" ## Eggdrop, things change. ## This path specifies the path were Eggdrop should look for its modules. -## If you run the bot from the compilation directory, you will want to set -## this to "". If you use 'make install' (like all good kiddies do ;), this -## is a fine default. Otherwise, use your head :) +## If you use 'make install' (like all good kiddies do ;), this is a fine +## default. Otherwise, use your head :) set mod-path "modules/" #### CHANNELS MODULE #### @@ -416,10 +415,7 @@ if {[file exists aclocal.m4]} { die {You are attempting to run Eggdrop from the if {[info exists net-type]} { switch -- ${net-type} { - "EFnet" { - # EFnet - source scripts/quotepong.tcl - } + "EFnet" - "0" { # EFnet source scripts/quotepong.tcl diff --git a/eggdrop.conf b/eggdrop.conf index 86ac99e64..852758286 100755 --- a/eggdrop.conf +++ b/eggdrop.conf @@ -605,9 +605,8 @@ die "Please make sure you edit your config file completely." # Eggdrop, things change. # This path specifies the path were Eggdrop should look for its modules. -# If you run the bot from the compilation directory, you will want to set -# this to "". If you use 'make install' (like all good kiddies do ;), this -# is a fine default. Otherwise, use your head :) +# If you use 'make install' (like all good kiddies do ;), this is a fine +# default. Otherwise, use your head :) set mod-path "modules/" @@ -656,6 +655,7 @@ loadmodule pbkdf2 # This setting is planned to be enabled by default in Eggdrop 2.0. #set remove-pass 0 + #### BLOWFISH MODULE #### # # This module is planned to be removed in Eggdrop 2.0 @@ -676,21 +676,14 @@ loadmodule blowfish set blowfish-use-mode cbc -#### DNS MODULE (Deprecated) #### - -## This module provided asynchronous dns support, but as of v1.9.2, this -## functionality was moved into the core code. If you are having issues with the -## new DNS functionality, or just want to continue using this module, compile -## Eggdrop with the --disable-tdns flag (./configure --disdable-tdns). +#### PYTHON MODULE ##### # -## You really probably don't want to uncomment this!!!! -# -#loadmodule dns -#set dns-servers "8.8.8.8 1.1.1.1 185.222.222.222" -#set dns-cache 86400 -#set dns-negcache 600 -#set dns-maxsends 4 -#set dns-retrydelay 3 +# This module gives Eggdrop the ability to run python scripts. if loaded, +# Python scripts can be loaded at the end of the config file using the pysouce +# command to tell Eggdrop where the file is loaded. The module requires Python +# version 3.8 or higher to run. To load the python module, uncomment it below. +#loadmodule python + #### CHANNELS MODULE #### @@ -1448,6 +1441,7 @@ set xfer-timeout 30 # be v1.9.0 or higher). #set sharefail-unlink 1 + #### SHARE MODULE #### # This module provides userfile sharing support between two directly @@ -1710,10 +1704,7 @@ loadhelp userinfo.help if {[info exists net-type]} { switch -- ${net-type} { - "EFnet" { - # EFnet - source scripts/quotepong.tcl - } + "EFnet" - "0" { # EFnet source scripts/quotepong.tcl diff --git a/misc/modconfig b/misc/modconfig index 0fd07b063..a22acd9dd 100755 --- a/misc/modconfig +++ b/misc/modconfig @@ -274,7 +274,7 @@ xdetect-modules) mc_new_mod_state=enabled fi fi - if test "${mc_new_mod_state}" = enabled; then + if test "${mc_new_mod_state}" = enabled && test -n "`find $mc_mod_bin_dir/${mc_mod}.mod -name \*.c`"; then ${mc_self_call} -q add ${mc_mod} else ${mc_self_call} -q del ${mc_mod} diff --git a/misc/updatecopyright b/misc/updatecopyright index a17b5edaf..a220e45d0 100755 --- a/misc/updatecopyright +++ b/misc/updatecopyright @@ -49,8 +49,8 @@ update_copyright() { mv ${i}_ $i fi # Cats and Dogs - sed -i '/Eggdrop v%s (C) 1997 Robey Pointer/c\ "Eggdrop v%s (C) 1997 Robey Pointer (C) 2010-'$YEAR' Eggheads",' src/main.c - sed -i '/Eggdrop v%s+%s (C) 1997 Robey Pointer/c\ "Eggdrop v%s+%s (C) 1997 Robey Pointer (C) 2010-'$YEAR' Eggheads",' src/main.c + sed -i '/Eggdrop v" EGG_STRINGVER " (C) 1997 Robey Pointer (C) 1999-/c\ "Eggdrop v" EGG_STRINGVER " (C) 1997 Robey Pointer (C) 1999-'$YEAR' Eggheads Development Team",' src/main.c + sed -i '/Eggdrop v" EGG_STRINGVER "+" EGG_PATCH " (C) 1997 Robey Pointer (C) 1999-/c\ "Eggdrop v" EGG_STRINGVER "+" EGG_PATCH " (C) 1997 Robey Pointer (C) 1999-'$YEAR' Eggheads Development Team",' src/main.c } diff --git a/src/Makefile.in b/src/Makefile.in index 6c6be3ac1..21e359131 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -6,6 +6,7 @@ srcdir = @srcdir@ VPATH = @srcdir@ @SET_MAKE@ +EGGEXEC = @EGGEXEC@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ @@ -18,6 +19,8 @@ CFLAGS = @CFLAGS@ -I.. -I$(top_srcdir) @SSL_INCLUDES@ @DEFS@ $(CFLGS) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ +XLIBS = @SSL_LIBS@ @TCL_LIB_SPEC@ @LIBS@ + eggdrop_objs = bg.o botcmd.o botmsg.o botnet.o chanprog.o cmds.o dcc.o \ dccutil.o dns.o flags.o language.o match.o main.o mem.o misc.o misc_file.o \ modules.o net.o rfc1459.o tcl.o tcldcc.o tclhash.o tclmisc.o tcluser.o \ @@ -173,7 +176,8 @@ modules.o: modules.c main.h ../config.h ../eggint.h ../lush.h lang.h \ net.o: net.c main.h ../config.h ../eggint.h ../lush.h lang.h eggdrop.h \ compat/in6.h flags.h proto.h misc_file.h cmdt.h tclegg.h tclhash.h \ chan.h users.h compat/compat.h compat/base64.h compat/inet_aton.h \ - ../src/main.h compat/snprintf.h compat/explicit_bzero.h compat/strlcpy.h + ../src/main.h compat/snprintf.h compat/explicit_bzero.h compat/strlcpy.h \ + modules.h mod/modvals.h rfc1459.o: rfc1459.c main.h ../config.h ../eggint.h ../lush.h lang.h \ eggdrop.h compat/in6.h flags.h proto.h misc_file.h cmdt.h tclegg.h \ tclhash.h chan.h users.h compat/compat.h compat/base64.h \ diff --git a/src/chan.h b/src/chan.h index d5f11257d..31b639834 100644 --- a/src/chan.h +++ b/src/chan.h @@ -50,7 +50,8 @@ typedef struct memstruct { time_t split; /* in case they were just netsplit */ time_t last; /* for measuring idle time */ time_t delay; /* for delayed autoop */ - int tried_getuser; // TODO: use it to invalidate user cache + struct userrec *user; /* cached user lookup */ + int tried_getuser; /* negative user lookup cache */ struct memstruct *next; } memberlist; diff --git a/src/chanprog.c b/src/chanprog.c index 37e03d61a..f589593fb 100644 --- a/src/chanprog.c +++ b/src/chanprog.c @@ -118,8 +118,11 @@ struct chanset_t *findchan_by_dname(const char *name) /* Clear the user pointers in the chanlists. * - * Necessary when a hostmask is added/removed, a user is added or a new - * userfile is loaded. + * Necessary when: + * - a hostmask is added/removed + * - an account is added/removed + * - a user is added + * - new userfile is loaded */ void clear_chanlist(void) { @@ -128,14 +131,16 @@ void clear_chanlist(void) for (chan = chanset; chan; chan = chan->next) for (m = chan->channel.member; m && m->nick[0]; m = m->next) { + m->user = NULL; m->tried_getuser = 0; } } /* Clear the user pointer of a specific nick in the chanlists. - - * Necessary when a hostmask is added/removed, a nick changes, etc. - * Does not completely invalidate the channel cache like clear_chanlist(). + * + * Necessary when: + * - their hostmask changed (chghost) + * - their account changed */ void clear_chanlist_member(const char *nick) { @@ -145,6 +150,7 @@ void clear_chanlist_member(const char *nick) for (chan = chanset; chan; chan = chan->next) for (m = chan->channel.member; m && m->nick[0]; m = m->next) if (!rfc_casecmp(m->nick, nick)) { + m->user = NULL; m->tried_getuser = 0; break; } diff --git a/src/cmds.c b/src/cmds.c index e1730507a..507c69687 100644 --- a/src/cmds.c +++ b/src/cmds.c @@ -1908,11 +1908,11 @@ static int add_to_handle(struct userrec *u, int idx, char *handle, char *host, i } } if ( !type && !glob_botmast(fr) && !chan_master(fr) && get_user_by_host(host)) { - dprintf(idx, "You cannot add %s matching another user!\n", - type ? "an account" : "a host"); + dprintf(idx, "You cannot add a host matching another user!\n"); return 1; } if (type) { + // host-variable contains account u2 = get_user_by_account(host); if (u2) { dprintf(idx, "That account already exists for user %s\n", u2->handle); @@ -1920,6 +1920,7 @@ static int add_to_handle(struct userrec *u, int idx, char *handle, char *host, i } addaccount_by_handle(handle, host); } else { + // host for (q = get_user(&USERENTRY_HOSTS, u); q; q = q->next) { if (!strcasecmp(q->extra, host)) { dprintf(idx, "That %s is already there.\n", diff --git a/src/eggdrop.h b/src/eggdrop.h index cca60c09a..c91ae91e2 100644 --- a/src/eggdrop.h +++ b/src/eggdrop.h @@ -183,12 +183,8 @@ #endif /* Almost every module needs some sort of time thingy, so... */ -#ifdef HAVE_SYS_TIME_H -# include -#else -# include -#endif - +#include /* gettimeofday() POSIX 2001 */ +#include /* POSIX 2001 */ /* Yikes...who would have thought finding a usable random() would be so much * trouble? diff --git a/src/main.c b/src/main.c index 17133c5b5..0b321d025 100644 --- a/src/main.c +++ b/src/main.c @@ -53,12 +53,6 @@ #include #include -#ifdef HAVE_SYS_TIME_H -# include -#else -# include -#endif - #ifdef STOP_UAC /* OSF/1 complains a lot */ # include # define UAC_NOPRINT 0x00000001 /* Don't report unaligned fixups */ @@ -129,7 +123,7 @@ int make_userfile = 0; /* Using bot in userfile-creation mode? */ int save_users_at = 0; /* Minutes past the hour to save the userfile? */ int notify_users_at = 0; /* Minutes past the hour to notify users of notes? */ -char version[81]; /* Version info (long form) */ +char version[128]; /* Version info (long form) */ char ver[41]; /* Version info (short form) */ volatile sig_atomic_t do_restart = 0; /* .restart has been called, restart ASAP */ @@ -982,15 +976,15 @@ int main(int arg_c, char **arg_v) #ifdef EGG_PATCH egg_snprintf(egg_version, sizeof egg_version, "%s+%s %u", EGG_STRINGVER, EGG_PATCH, egg_numver); egg_snprintf(ver, sizeof ver, "eggdrop v%s+%s", EGG_STRINGVER, EGG_PATCH); - egg_snprintf(version, sizeof version, - "Eggdrop v%s+%s (C) 1997 Robey Pointer (C) 2010-2024 Eggheads", - EGG_STRINGVER, EGG_PATCH); + strlcpy(version, + "Eggdrop v" EGG_STRINGVER "+" EGG_PATCH " (C) 1997 Robey Pointer (C) 1999-2024 Eggheads Development Team", + sizeof version); #else egg_snprintf(egg_version, sizeof egg_version, "%s %u", EGG_STRINGVER, egg_numver); egg_snprintf(ver, sizeof ver, "eggdrop v%s", EGG_STRINGVER); - egg_snprintf(version, sizeof version, - "Eggdrop v%s (C) 1997 Robey Pointer (C) 2010-2024 Eggheads", - EGG_STRINGVER); + strlcpy(version, + "Eggdrop v" EGG_STRINGVER " (C) 1997 Robey Pointer (C) 1999-2024 Eggheads Development Team", + sizeof version); #endif /* For OSF/1 */ @@ -1029,6 +1023,10 @@ int main(int arg_c, char **arg_v) sigaction(SIGILL, &sv, NULL); sv.sa_handler = got_alarm; sigaction(SIGALRM, &sv, NULL); + // Added for python.mod because the _signal handler otherwise overwrites it + // see https://discuss.python.org/t/asyncio-skipping-signal-handling-setup-during-import-for-python-embedded-context/37054/6 + sv.sa_handler = got_term; + sigaction(SIGINT, &sv, NULL); /* Initialize variables and stuff */ now = time(NULL); diff --git a/src/mod/compress.mod/configure b/src/mod/compress.mod/configure index 2c3f4c0cc..21927d124 100755 --- a/src/mod/compress.mod/configure +++ b/src/mod/compress.mod/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac 98f8972a. +# From configure.ac 4af46aa3. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Eggdrop Compress Module 1.9.5. # diff --git a/src/mod/dns.mod/configure b/src/mod/dns.mod/configure index 07c7b3b57..54e38bf94 100755 --- a/src/mod/dns.mod/configure +++ b/src/mod/dns.mod/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac 98f8972a. +# From configure.ac 4af46aa3. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Eggdrop DNS Module 1.9.5. # diff --git a/src/mod/filesys.mod/filesys.c b/src/mod/filesys.mod/filesys.c index 1c12721ba..dcaafef76 100644 --- a/src/mod/filesys.mod/filesys.c +++ b/src/mod/filesys.mod/filesys.c @@ -48,12 +48,6 @@ # endif #endif -#ifdef HAVE_SYS_TIME_H -# include -#else -# include -#endif - #include "filedb3.h" #include "filesys.h" #include "src/tandem.h" diff --git a/src/mod/irc.mod/chan.c b/src/mod/irc.mod/chan.c index 1d0082547..9124d428c 100644 --- a/src/mod/irc.mod/chan.c +++ b/src/mod/irc.mod/chan.c @@ -116,6 +116,8 @@ static void setaccount(char *nick, char *account) } } } + /* Username for nick could be different after account change, invalidate cache */ + clear_chanlist_member(nick); } /* Returns the current channel mode. @@ -238,15 +240,7 @@ static int detect_chan_flood(char *floodnick, char *floodhost, char *from, if (!m && (which != FLOOD_JOIN)) return 0; - if (m) { - u = get_user_from_member(m); - } else { - u = victim_or_account ? get_user_by_account(victim_or_account) : NULL; - if (!u) { - u = get_user_by_host(from); - } - } - + u = lookup_user_record(m, victim_or_account, from); get_user_flagrec(u, &fr, chan->dname); if (glob_bot(fr) || ((which == FLOOD_DEOP) && (glob_master(fr) || chan_master(fr)) && (glob_friend(fr) || chan_friend(fr))) || @@ -331,7 +325,6 @@ static int detect_chan_flood(char *floodnick, char *floodhost, char *from, chan->floodwho[which][0] = 0; if (which == FLOOD_DEOP) chan->deopd[0] = 0; - u = get_user_from_member(m); if (check_tcl_flud(floodnick, floodhost, u, ftype, chan->dname)) return 0; switch (which) { @@ -1220,7 +1213,7 @@ static int got354(char *from, char *msg) * :geo!awesome@eggdrop.com CHGHOST tehgeo foo.io * changes user hostmask to tehgeo@foo.io */ -static int gotchghost(char *from, char *msg){ +static int gotchghost(char *from, char *msg) { struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; struct userrec *u; struct chanset_t *chan; @@ -1247,6 +1240,8 @@ static int gotchghost(char *from, char *msg){ check_this_member(chan, m->nick, &fr); } } + /* Username for nick could be different after host change, invalidate cache */ + clear_chanlist_member(nick); return 0; } @@ -1839,7 +1834,7 @@ static int gottopic(char *from, char *msg) if (m != NULL) m->last = now; set_topic(chan, msg); - u = get_user_from_member(m); + u = lookup_user_record(m, NULL, from); // TODO: get account from msgtags check_tcl_topc(nick, from, u, chan->dname, msg); } return 0; @@ -2075,9 +2070,8 @@ static int gotjoin(char *from, char *channame) strlcpy(m->userhost, uhost, sizeof m->userhost); m->flags |= STOPWHO; - u = get_user_from_member(m); - if (extjoin) { + u = lookup_user_record(m, account, from); /* calls check_tcl_account which can delete the channel */ setaccount(nick, account); @@ -2085,6 +2079,8 @@ static int gotjoin(char *from, char *channame) /* The channel doesn't exist anymore, so get out of here. */ goto exit; } + } else { + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags } check_tcl_join(nick, uhost, u, chan->dname); @@ -2125,9 +2121,9 @@ static int gotjoin(char *from, char *channame) if (u) { struct laston_info *li = 0; - cr = get_chanrec(get_user_from_member(m), chan->dname); + cr = get_chanrec(u, chan->dname); if (!cr && no_chanrec_info) - li = get_user(&USERENTRY_LASTON, get_user_from_member(m)); + li = get_user(&USERENTRY_LASTON, u); if (channel_greet(chan) && use_info && ((cr && now - cr->laston > wait_info) || (no_chanrec_info && (!li || now - li->laston > wait_info)))) { @@ -2349,7 +2345,7 @@ static int gotkick(char *from, char *origmsg) return 0; m = ismember(chan, whodid); - u = get_user_from_member(m); + u = lookup_user_record(m, NULL, from); // TODO: get account from msgtags if (m) m->last = now; /* This _needs_ to use chan->dname */ @@ -2565,7 +2561,6 @@ static int gotmsg(char *from, char *msg) int ctcp_count = 0, ignoring; struct chanset_t *chan; struct userrec *u; - memberlist *m; /* Only handle if message is to a channel, or to @#channel. */ /* FIXME: Properly handle ovNotices (@+#channel), vNotices (+#channel), etc. */ @@ -2609,15 +2604,7 @@ static int gotmsg(char *from, char *msg) ctcp_count++; if (ctcp[0] != ' ') { code = newsplit(&ctcp); - u = NULL; - for (chan = chanset; chan; chan = chan->next) { - for (m = chan->channel.member; m && m->nick[0]; m = m->next) { - if (!rfc_casecmp(m->nick, nick)) { - u = get_user_from_member(m); - break; - } - } - } + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags if (!ignoring || trigger_on_ignore) { if (!check_tcl_ctcp(nick, uhost, u, to, code, ctcp)) { chan = findchan(realto); @@ -2696,7 +2683,6 @@ static int gotnotice(char *from, char *msg) char *ctcp, *code; struct userrec *u; struct chanset_t *chan; - memberlist *m; int ignoring; if (!strchr(CHANMETA "@", *msg)) @@ -2710,15 +2696,7 @@ static int gotnotice(char *from, char *msg) fixcolon(msg); strlcpy(uhost, from, sizeof buf); nick = splitnick(&uhost); - u = NULL; - for (chan = chanset; chan; chan = chan->next) { - for (m = chan->channel.member; m && m->nick[0]; m = m->next) { - if (!rfc_casecmp(m->nick, nick)) { - u = get_user_from_member(m); - break; - } - } - } + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags /* Check for CTCP: */ p = strchr(msg, 1); while (p && *p) { diff --git a/src/mod/irc.mod/cmdsirc.c b/src/mod/irc.mod/cmdsirc.c index 3b387c8f6..c9bf58ab1 100644 --- a/src/mod/irc.mod/cmdsirc.c +++ b/src/mod/irc.mod/cmdsirc.c @@ -751,7 +751,6 @@ static void cmd_channel(struct userrec *u, int idx, char *par) } else strlcpy(s, " --- ", sizeof s); egg_snprintf(s, sizeof s, "%s!%s", m->nick, m->userhost); - u = get_user_from_member(m); if (u == NULL) strlcpy(handle, "*", sizeof handle); diff --git a/src/mod/irc.mod/irc.c b/src/mod/irc.mod/irc.c index dc03c7f24..bc3f578e4 100644 --- a/src/mod/irc.mod/irc.c +++ b/src/mod/irc.mod/irc.c @@ -224,7 +224,7 @@ static void punish_badguy(struct chanset_t *chan, char *whobad, static void maybe_revenge(struct chanset_t *chan, char *whobad, char *whovictim, int type) { - char *badnick, *victim; + char *badnick, *victim, buf[NICKLEN + UHOSTLEN]; int mevictim; struct userrec *u, *u2; memberlist *m; @@ -233,14 +233,16 @@ static void maybe_revenge(struct chanset_t *chan, char *whobad, return; /* Get info about offender */ + strlcpy(buf, whobad, sizeof buf); badnick = splitnick(&whobad); m = ismember(chan, badnick); - u = get_user_from_member(m); + u = lookup_user_record(m, NULL, buf); // TODO: get account from msgtags /* Get info about victim */ + strlcpy(buf, whovictim, sizeof buf); victim = splitnick(&whovictim); m = ismember(chan, victim); - u2 = get_user_from_member(m); + u2 = lookup_user_record(m, NULL, buf); // TODO: get account from msgtags mevictim = match_my_nick(victim); /* Do we want to revenge? */ @@ -908,7 +910,7 @@ static int check_tcl_pub(char *nick, char *from, char *chname, char *msg) int x; char buf[512], *args = buf, *cmd, host[161], *hand; struct chanset_t *chan; - struct userrec *u; + struct userrec *u = NULL; memberlist *m; strlcpy(buf, msg, sizeof buf); @@ -916,7 +918,7 @@ static int check_tcl_pub(char *nick, char *from, char *chname, char *msg) simple_sprintf(host, "%s!%s", nick, from); chan = findchan(chname); m = ismember(chan, nick); - u = get_user_from_member(m); + u = lookup_user_record(m ? m : find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags hand = u ? u->handle : "*"; get_user_flagrec(u, &fr, chname); Tcl_SetVar(interp, "_pub1", nick, 0); @@ -946,7 +948,7 @@ static int check_tcl_pubm(char *nick, char *from, char *chname, char *msg) simple_sprintf(host, "%s!%s", nick, from); chan = findchan(chname); m = ismember(chan, nick); - u = get_user_from_member(m); + u = lookup_user_record(m ? m : find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags get_user_flagrec(u, &fr, chname); Tcl_SetVar(interp, "_pubm1", nick, 0); Tcl_SetVar(interp, "_pubm2", from, 0); diff --git a/src/mod/irc.mod/mode.c b/src/mod/irc.mod/mode.c index 88814d3bf..5350fafd7 100644 --- a/src/mod/irc.mod/mode.c +++ b/src/mod/irc.mod/mode.c @@ -52,11 +52,11 @@ static struct chanset_t *modebind_refresh(char *chname, if (!chname || !(chan = findchan(chname))) return NULL; if (usrhost) { - u = get_user_by_host(usrhost); + u = lookup_user_record(NULL, NULL, usrhost); // TODO: get account from somewhere get_user_flagrec(u, usr, chan->dname); } if (vcrhost) { - u = get_user_by_host(vcrhost); + u = lookup_user_record(NULL, NULL, vcrhost); // TODO: get account from somewhere get_user_flagrec(u, vcr, chan->dname); } return chan; diff --git a/src/mod/irc.mod/msgcmds.c b/src/mod/irc.mod/msgcmds.c index 282687425..1c8a45f6b 100644 --- a/src/mod/irc.mod/msgcmds.c +++ b/src/mod/irc.mod/msgcmds.c @@ -383,6 +383,7 @@ static int msg_who(char *nick, char *host, struct userrec *u, char *par) struct userrec *u; egg_snprintf(s, sizeof s, "%s!%s", m->nick, m->userhost); + /* Don't use s for host here, b/c if we don't have m, we won't have s */ u = get_user_from_member(m); info = get_user(&USERENTRY_INFO, u); if (u && (u->flags & USER_BOT)) @@ -431,7 +432,7 @@ static int msg_who(char *nick, char *host, struct userrec *u, char *par) static int msg_whois(char *nick, char *host, struct userrec *u, char *par) { - char s1[143], *s2, stime[14]; + char s1[143], *s2, stime[14], uhost[NICKLEN + UHOSTLEN]; int ok; struct chanset_t *chan; memberlist *m; @@ -454,7 +455,8 @@ static int msg_whois(char *nick, char *host, struct userrec *u, char *par) } if (strlen(par) > NICKMAX) par[NICKMAX] = 0; - putlog(LOG_CMDS, "*", "(%s!%s) !%s! WHOIS %s", nick, host, u->handle, par); + egg_snprintf(uhost, sizeof uhost, "%s!%s", nick, host); + putlog(LOG_CMDS, "*", "(%s) !%s! WHOIS %s", uhost, u->handle, par); u2 = get_user_by_handle(userlist, par); if (!u2) { /* No such handle -- maybe it's a nickname of someone on a chan? */ diff --git a/src/mod/module.h b/src/mod/module.h index da1651c0a..efab4174f 100644 --- a/src/mod/module.h +++ b/src/mod/module.h @@ -202,7 +202,7 @@ typedef void (*chanout_butfunc)(int, int, const char *, ...) ATTRIBUTE_FORMAT(pr #define open_telnet ((int (*) (int, char *, int))global[87]) /* 88 - 91 */ #define check_tcl_event ((void (*) (const char *))global[88]) -/* was my_memcpy -- use memcpy() instead */ +/* was my_memcpy() -- use memcpy() instead */ #define my_atoul ((IP(*)(char *))global[90]) #define my_strcpy ((int (*)(char *, const char *))global[91]) /* 92 - 95 */ @@ -426,10 +426,10 @@ typedef void (*chanout_butfunc)(int, int, const char *, ...) ATTRIBUTE_FORMAT(pr /* 252 - 255 */ #define egg_snprintf (global[252]) #define egg_vsnprintf ((int (*)(char *, size_t, const char *, va_list))global[253]) -/* was egg_memset -- use memset() instead */ -/* was egg_strcasecmp -- use strcasecmp instead */ +/* was egg_memset() -- use memset() instead */ +/* was egg_strcasecmp() -- use strcasecmp() instead */ /* 256 - 259 */ -/* was egg_strncasecmp -- use strncasecmp instead */ +/* was egg_strncasecmp() -- use strncasecmp() instead */ #define is_file ((int (*)(const char *))global[257]) #define must_be_owner (*(int *)(global[258])) #define tandbot (*(tand_t **)(global[259])) @@ -497,7 +497,7 @@ typedef void (*chanout_butfunc)(int, int, const char *, ...) ATTRIBUTE_FORMAT(pr # define strlcpy ((size_t (*) (char *, const char *, size_t))global[303]) #endif /* 304 - 307 */ -#define strncpyz ((size_t (*) (char *, const char *, size_t))global[304]) +#define strncpyz strlcpy /* strncpyz() is deprecated, use strlcpy() instead */ #ifndef HAVE_BASE64 # define b64_ntop ((int (*) (uint8_t const *, size_t, char *, size_t))global[305]) # define b64_pton ((int (*) (const char *, uint8_t *, size_t))global[306]) @@ -524,8 +524,10 @@ typedef void (*chanout_butfunc)(int, int, const char *, ...) ATTRIBUTE_FORMAT(pr #define bind_bind_entry ((int(*)(tcl_bind_list_t *, const char *, const char *, const char *))global[320]) #define unbind_bind_entry ((int(*)(tcl_bind_list_t *, const char *, const char *, const char *))global[321]) #define argv0 ((char *)global[322]) -#define get_user_from_member ((struct userrec * (*)(memberlist *))global[323]) +#define lookup_user_record ((struct userrec * (*)(memberlist *, char *, char *))global[323]) /* 324 - 327 */ +#define find_member_from_nick ((memberlist * (*) (char *))global[324]) +#define get_user_from_member ((struct userrec * (*) (memberlist *))global[325]) /* hostmasking */ diff --git a/src/mod/modvals.h b/src/mod/modvals.h index 6b955cad3..a840e09cf 100644 --- a/src/mod/modvals.h +++ b/src/mod/modvals.h @@ -39,7 +39,11 @@ #define HOOK_LOADED 13 #define HOOK_BACKUP 14 #define HOOK_DIE 15 -#define REAL_HOOKS 16 +#define HOOK_PRE_SELECT 16 +#define HOOK_POST_SELECT 17 + +#define REAL_HOOKS 18 + #define HOOK_SHAREOUT 105 #define HOOK_SHAREIN 106 #define HOOK_ENCRYPT_PASS 107 diff --git a/src/mod/pbkdf2.mod/Makefile b/src/mod/pbkdf2.mod/Makefile index 6cf40c584..38e70d0fe 100644 --- a/src/mod/pbkdf2.mod/Makefile +++ b/src/mod/pbkdf2.mod/Makefile @@ -36,4 +36,5 @@ distclean: clean ../../../src/compat/compat.h ../../../src/compat/base64.h \ ../../../src/compat/inet_aton.h ../../../src/compat/snprintf.h \ ../../../src/compat/explicit_bzero.h ../../../src/compat/strlcpy.h \ - ../../../src/mod/modvals.h ../../../src/tandem.h + ../../../src/mod/modvals.h ../../../src/tandem.h \ + .././pbkdf2.mod/tclpbkdf2.c diff --git a/src/mod/pbkdf2.mod/pbkdf2.c b/src/mod/pbkdf2.mod/pbkdf2.c index 169d92bc8..faf7313d2 100644 --- a/src/mod/pbkdf2.mod/pbkdf2.c +++ b/src/mod/pbkdf2.mod/pbkdf2.c @@ -10,6 +10,8 @@ #include "src/mod/module.h" #if OPENSSL_VERSION_NUMBER >= 0x1000000fL /* 1.0.0 */ +#include "tclpbkdf2.c" + #define MODULE_NAME "encryption2" #include /* base64 encode b64_ntop() and base64 decode b64_pton() */ @@ -108,7 +110,7 @@ static char *pbkdf2_hash(const char *pass, const char *digest_name, digestlen, buf)) { explicit_bzero(buf, digestlen); explicit_bzero(out, outlen); - putlog(LOG_MISC, "*", "PBKDF2 error: PKCS5_PBKDF2_HMAC(): %s.", + putlog(LOG_MISC, "*", "PBKDF2 key derivation error: %s.", ERR_error_string(ERR_get_error(), NULL)); nfree(buf); return NULL; @@ -223,21 +225,6 @@ static char *pbkdf2_verify(const char *pass, const char *encrypted) return (char *) encrypted; } -static int tcl_encpass2 STDVAR -{ - BADARGS(2, 2, " string"); - if (strlen(argv[1]) > 0) - Tcl_AppendResult(irp, pbkdf2_encrypt(argv[1]), NULL); - else - Tcl_AppendResult(irp, "", NULL); - return TCL_OK; -} - -static tcl_cmds my_tcl_cmds[] = { - {"encpass2", tcl_encpass2}, - {NULL, NULL} -}; - static tcl_ints my_tcl_ints[] = { {"pbkdf2-re-encode", &pbkdf2_re_encode, 0}, {"pbkdf2-rounds", &pbkdf2_rounds, 0}, diff --git a/src/mod/pbkdf2.mod/tclpbkdf2.c b/src/mod/pbkdf2.mod/tclpbkdf2.c new file mode 100644 index 000000000..8fa7bf8dd --- /dev/null +++ b/src/mod/pbkdf2.mod/tclpbkdf2.c @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tclpbkdf2.c -- tcl functions for pbkdf2.mod + * + * Written by thommey and Michael Ortmann + * + * Copyright (C) 2017 - 2024 Eggheads Development Team + */ + +#include +#include + +static char *pbkdf2_encrypt(const char *); + +static int tcl_encpass2 STDVAR +{ + BADARGS(2, 2, " string"); + Tcl_SetResult(irp, pbkdf2_encrypt(argv[1]), TCL_STATIC); + return TCL_OK; +} + +static int tcl_pbkdf2 STDVAR +{ + int hex, digestlen, i; + unsigned int rounds; + const EVP_MD *digest; + unsigned char buf[256]; + char buf_hex[256]; + Tcl_Obj *result = 0; + + BADARGS(5, 6, " ?-bin? pass salt rounds digest"); + if (argc == 6) { + if (!strcmp(argv[1], "-bin")) + hex = 0; + else { + Tcl_AppendResult(irp, "bad option ", argv[1], ": must be -bin", NULL); + return TCL_ERROR; + } + } + else + hex = 1; + rounds = atoi(argv[3 + !hex]); + digest = EVP_get_digestbyname(argv[4 + !hex]); + if (!digest) { + Tcl_AppendResult(irp, "PBKDF2 error: Unknown message digest '", argv[4 + !hex], "'.", NULL); + return TCL_ERROR; + } + digestlen = EVP_MD_size(digest); + if (!PKCS5_PBKDF2_HMAC(argv[1 + !hex], strlen(argv[1 + !hex]), (const unsigned char *) argv[2+ !hex], strlen(argv[2 + !hex]), rounds, digest, digestlen, buf)) { + Tcl_AppendResult(irp, "PBKDF2 key derivation error: ", ERR_error_string(ERR_get_error(), NULL), ".", NULL); + return TCL_ERROR; + } + if (hex) { + for (i = 0; i < digestlen; i++) + sprintf(buf_hex + (i * 2), "%.2X", buf[i]); + result = Tcl_NewByteArrayObj((unsigned char *) buf_hex, digestlen * 2); + explicit_bzero(buf_hex, digestlen * 2); + } + else + result = Tcl_NewByteArrayObj(buf, digestlen); + explicit_bzero(buf, digestlen); + Tcl_SetObjResult(irp, result); + return TCL_OK; +} + +static tcl_cmds my_tcl_cmds[] = { + {"encpass2", tcl_encpass2}, + {"pbkdf2", tcl_pbkdf2}, + {NULL, NULL} +}; diff --git a/src/mod/python.mod/configure b/src/mod/python.mod/configure index eadf4ed9c..def84c793 100755 --- a/src/mod/python.mod/configure +++ b/src/mod/python.mod/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac 98f8972a. +# From configure.ac 4af46aa3. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Eggdrop Python Module 1.10.0. # @@ -623,7 +623,6 @@ ac_unique_file="python.c" ac_subst_vars='LTLIBOBJS LIBOBJS AWK -PYTHON PYTHON_VERSION PYTHON_LDFLAGS PYTHON_CFLAGS @@ -1924,7 +1923,8 @@ python_avail="false" if test "x$egg_enable_python" != "xno"; then if test "x$egg_with_python_config" = "x"; then - for ac_prog in python3-config python-config python3.12-config python3.11-config python3.10-config python3.9-config python3.8-config + # based on serial 20 https://www.gnu.org/software/autoconf-archive/ax_python.html + for ac_prog in python3-config python3.12-config python3.11-config python3.10-config python3.9-config python3.8-config python-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -1978,7 +1978,7 @@ done if test -d "$egg_with_python_config"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Checking for python-config binaries in $egg_with_python_config" >&5 printf "%s\n" "$as_me: Checking for python-config binaries in $egg_with_python_config" >&6;} - for ac_prog in python3-config python-config + for ac_prog in python3-config python3.12-config python3.11-config python3.10-config python3.9-config python3.8-config python-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -2031,9 +2031,6 @@ done else if test -x "$egg_with_python_config"; then python_config_bin="$egg_with_python_config" - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Specified --with-python-config=$egg_with_python_config does not exist." >&5 -printf "%s\n" "$as_me: WARNING: Specified --with-python-config=$egg_with_python_config does not exist." >&2;} fi fi fi @@ -2081,65 +2078,10 @@ fi # Check python path -# Extract the first word of "python[$PYTHON_VERSION]", so it can be a program name with args. -set dummy python$PYTHON_VERSION; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_path_PYTHON+y} -then : - printf %s "(cached) " >&6 -else $as_nop - case $PYTHON in - [\\/]* | ?:[\\/]*) - ac_cv_path_PYTHON="$PYTHON" # 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 - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_path_PYTHON="$as_dir$ac_word$ac_exec_ext" - printf "%s\n" "$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 -PYTHON=$ac_cv_path_PYTHON -if test -n "$PYTHON"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 -printf "%s\n" "$PYTHON" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - if test -z "$PYTHON"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&5 -printf "%s\n" "$as_me: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&2;} - if ! $ax_python_devel_optional; then - as_fn_error $? "Giving up, python development not available" "$LINENO" 5 - fi - ax_python_devel_found=no - PYTHON_VERSION="" -fi +PYTHON_VERSION=`echo $PYTHON_LDFLAGS | sed 's/.*-lpython\(.*[0-9]\).*/\1/'` # Check for python version - python_ver=`$PYTHON -c "import sys; \ - print(sys.version.split ()[0])"` - for ac_prog in gawk mawk nawk awk +for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -2198,7 +2140,7 @@ done # 0001001200050617. In other words, each number is zero padded to four # digits, and non digits are removed. - ax_compare_version_A=`echo "$python_ver" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ + ax_compare_version_A=`echo "$PYTHON_VERSION" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ @@ -2222,18 +2164,18 @@ x$ax_compare_version_B" | sed 's/^ *//' | sort -r | sed "s/x${ax_compare_version else ax_check_ver="no" fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking $PYTHON version is >= 3.8.0" >&5 -printf %s "checking $PYTHON version is >= 3.8.0... " >&6; } - if test "x$ax_check_ver" = x"no"; then - python_avail="false" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no ($python_ver)" >&5 -printf "%s\n" "no ($python_ver)" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Eggdrop requires python version 3.8.0 or higher" >&5 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking python version is >= 3.8.0" >&5 +printf %s "checking python version is >= 3.8.0... " >&6; } +if test "x$ax_check_ver" = x"no"; then + python_avail="false" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no ($PYTHON_VERSION)" >&5 +printf "%s\n" "no ($PYTHON_VERSION)" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Eggdrop requires python version 3.8.0 or higher" >&5 printf "%s\n" "$as_me: WARNING: Eggdrop requires python version 3.8.0 or higher" >&2;} - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($python_ver)" >&5 -printf "%s\n" "yes ($python_ver)" >&6; } - fi +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes ($PYTHON_VERSION)" >&5 +printf "%s\n" "yes ($PYTHON_VERSION)" >&6; } +fi # Disable the module if test "x$python_avail" = "xfalse"; then diff --git a/src/mod/python.mod/configure.ac b/src/mod/python.mod/configure.ac index d91c6e503..ddb3f28d2 100644 --- a/src/mod/python.mod/configure.ac +++ b/src/mod/python.mod/configure.ac @@ -21,16 +21,15 @@ python_avail="false" if test "x$egg_enable_python" != "xno"; then if test "x$egg_with_python_config" = "x"; then - AC_PATH_PROGS([python_config_bin], [python3-config python-config python3.12-config python3.11-config python3.10-config python3.9-config python3.8-config]) + # based on serial 20 https://www.gnu.org/software/autoconf-archive/ax_python.html + AC_PATH_PROGS([python_config_bin], [python3-config python3.12-config python3.11-config python3.10-config python3.9-config python3.8-config python-config]) else if test -d "$egg_with_python_config"; then AC_MSG_NOTICE([Checking for python-config binaries in $egg_with_python_config]) - AC_PATH_PROGS([python_config_bin], [python3-config python-config], [], [$egg_with_python_config]) + AC_PATH_PROGS([python_config_bin], [python3-config python3.12-config python3.11-config python3.10-config python3.9-config python3.8-config python-config], [], [$egg_with_python_config]) else if test -x "$egg_with_python_config"; then python_config_bin="$egg_with_python_config" - else - AC_MSG_WARN([Specified --with-python-config=$egg_with_python_config does not exist.]) fi fi fi @@ -66,28 +65,18 @@ version to use, for example '2.3'. This string will be appended to the Python interpreter canonical name.]) -AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) - if test -z "$PYTHON"; then - AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path]) - if ! $ax_python_devel_optional; then - AC_MSG_ERROR([Giving up, python development not available]) - fi - ax_python_devel_found=no - PYTHON_VERSION="" -fi +PYTHON_VERSION=`echo $PYTHON_LDFLAGS | sed 's/.*-lpython\(.*[[0-9]]\).*/\1/'` # Check for python version - python_ver=`$PYTHON -c "import sys; \ - print(sys.version.split ()[[0]])"` - AX_COMPARE_VERSION([$python_ver],[ge],[3.8.0], [ax_check_ver="yes"], [ax_check_ver="no"]) - AC_MSG_CHECKING([$PYTHON version is >= 3.8.0]) - if test "x$ax_check_ver" = x"no"; then - python_avail="false" - AC_MSG_RESULT([no ($python_ver)]) - AC_MSG_WARN([Eggdrop requires python version 3.8.0 or higher]) - else - AC_MSG_RESULT([yes ($python_ver)]) - fi +AX_COMPARE_VERSION([$PYTHON_VERSION],[ge],[3.8.0], [ax_check_ver="yes"], [ax_check_ver="no"]) +AC_MSG_CHECKING([python version is >= 3.8.0]) +if test "x$ax_check_ver" = x"no"; then + python_avail="false" + AC_MSG_RESULT([no ($PYTHON_VERSION)]) + AC_MSG_WARN([Eggdrop requires python version 3.8.0 or higher]) +else + AC_MSG_RESULT([yes ($PYTHON_VERSION)]) +fi # Disable the module if test "x$python_avail" = "xfalse"; then diff --git a/src/mod/python.mod/python.c b/src/mod/python.mod/python.c index 7a4ed7fce..c4221efac 100644 --- a/src/mod/python.mod/python.c +++ b/src/mod/python.mod/python.c @@ -31,7 +31,6 @@ #undef interp #define tclinterp (*(Tcl_Interp **)(global[128])) #undef days -#include #include #include #include "src/mod/irc.mod/irc.h" @@ -43,6 +42,7 @@ static PyObject *pirp, *pglobals; #undef global static Function *global = NULL, *irc_funcs = NULL; +static PyThreadState *_pythreadsave; #include "src/mod/python.mod/pycmds.c" #include "src/mod/python.mod/tclpython.c" @@ -53,42 +53,43 @@ static int python_expmem() return 0; // TODO } -// TODO: Do we really have to exit eggdrop on module load failure? -static void init_python() { +static int python_gil_unlock() { + _pythreadsave = PyEval_SaveThread(); + return 0; +} + +static int python_gil_lock() { + PyEval_RestoreThread(_pythreadsave); + return 0; +} + +static char *init_python() { PyObject *pmodule; PyStatus status; PyConfig config; - if (PY_VERSION_HEX < 0x0308) { - putlog(LOG_MISC, "*", "Python: Python version %d is lower than 3.8, not loading Python module", PY_VERSION_HEX); - return; - } PyConfig_InitPythonConfig(&config); config.install_signal_handlers = 0; config.parse_argv = 0; status = PyConfig_SetBytesString(&config, &config.program_name, argv0); if (PyStatus_Exception(status)) { PyConfig_Clear(&config); - putlog(LOG_MISC, "*", "Python: Fatal error: Could not set program base path"); - Py_ExitStatusException(status); + return "Python: Fatal error: Could not set program base path"; } if (PyImport_AppendInittab("eggdrop", &PyInit_eggdrop) == -1) { - putlog(LOG_MISC, "*", "Python: Error: could not extend in-built modules table"); - exit(1); + PyConfig_Clear(&config); + return "Python: Error: could not extend in-built modules table"; } status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) { PyConfig_Clear(&config); - putlog(LOG_MISC, "*", "Python: Fatal error: Could not initialize config"); - fatal(1); + return "Python: Fatal error: Could not initialize config"; } PyConfig_Clear(&config); PyDateTime_IMPORT; pmodule = PyImport_ImportModule("eggdrop"); if (!pmodule) { - PyErr_Print(); - putlog(LOG_MISC, "*", "Error: could not import module 'eggdrop'"); - fatal(1); + return "Error: could not import module 'eggdrop'"; } pirp = PyImport_AddModule("__main__"); @@ -100,7 +101,7 @@ static void init_python() { PyRun_SimpleString("import eggdrop"); PyRun_SimpleString("sys.displayhook = eggdrop.__displayhook__"); - return; + return NULL; } static void kill_python() { @@ -118,7 +119,8 @@ static void python_report(int idx, int details) static char *python_close() { - Context; + del_hook(HOOK_PRE_SELECT, (Function)python_gil_unlock); + del_hook(HOOK_POST_SELECT, (Function)python_gil_lock); kill_python(); rem_builtins(H_dcc, mydcc); rem_tcl_commands(my_tcl_cmds); @@ -135,12 +137,12 @@ static Function python_table[] = { char *python_start(Function *global_funcs) { + char *s; /* Assign the core function table. After this point you use all normal * functions defined in src/mod/modules.h */ global = global_funcs; - Context; /* Register the module. */ module_register(MODULE_NAME, python_table, 0, 1); @@ -155,10 +157,14 @@ char *python_start(Function *global_funcs) } // irc.mod depends on server.mod and channels.mod, so those were implicitely loaded - init_python(); + if ((s = init_python())) + return s; /* Add command table to bind list */ add_builtins(H_dcc, mydcc); add_tcl_commands(my_tcl_cmds); + add_hook(HOOK_PRE_SELECT, (Function)python_gil_unlock); + add_hook(HOOK_POST_SELECT, (Function)python_gil_lock); + return NULL; } diff --git a/src/mod/python.mod/scripts/urlTitle.py b/src/mod/python.mod/scripts/urlTitle.py index f8b498226..b2d35899a 100644 --- a/src/mod/python.mod/scripts/urlTitle.py +++ b/src/mod/python.mod/scripts/urlTitle.py @@ -17,6 +17,7 @@ def pubGetTitle(nick, host, handle, channel, text, **kwargs): try: reqs = requests.get(text) soup = BeautifulSoup(reqs.text, 'html.parser') + putlog("Found title for "+text) putmsg(channel, "The title of the webpage is: "+soup.find_all('title')[0].get_text()) except Exception as e: putmsg(channel, "Error: " + str(e)) diff --git a/src/mod/seen.mod/seen.c b/src/mod/seen.mod/seen.c index 5dbb095da..63ddd0593 100644 --- a/src/mod/seen.mod/seen.c +++ b/src/mod/seen.mod/seen.c @@ -73,12 +73,6 @@ #include "src/mod/module.h" -#ifdef HAVE_SYS_TIME_H -# include -#else -# include -#endif - #include "src/users.h" #include "src/chan.h" #include "channels.mod/channels.h" diff --git a/src/mod/server.mod/cmdsserv.c b/src/mod/server.mod/cmdsserv.c index ab580d208..ceb79e436 100644 --- a/src/mod/server.mod/cmdsserv.c +++ b/src/mod/server.mod/cmdsserv.c @@ -20,7 +20,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include static void cmd_servers(struct userrec *u, int idx, char *par) { diff --git a/src/mod/server.mod/servmsg.c b/src/mod/server.mod/servmsg.c index 952ff70bf..91a50e2e9 100644 --- a/src/mod/server.mod/servmsg.c +++ b/src/mod/server.mod/servmsg.c @@ -493,7 +493,7 @@ static int detect_flood(char *floodnick, char *floodhost, char *from, int which) if (!strcasecmp(floodhost, botuserhost)) return 0; - u = get_user_by_host(from); + u = lookup_user_record(NULL, NULL, from); // TODO: get account somehow atr = u ? u->flags : 0; if (atr & (USER_BOT | USER_FRIEND)) return 0; @@ -539,7 +539,7 @@ static int detect_flood(char *floodnick, char *floodhost, char *from, int which) lastmsgs[which] = 0; lastmsgtime[which] = 0; lastmsghost[which][0] = 0; - u = get_user_by_host(from); + u = lookup_user_record(NULL, NULL, from); // TODO: get account somehow if (check_tcl_flud(floodnick, floodhost, u, ftype, "*")) return 0; /* Private msg */ @@ -607,7 +607,7 @@ static int gotmsg(char *from, char *msg) putlog(LOG_PUBLIC, to, "CTCP %s: %s from %s (%s) to %s", code, ctcp, nick, uhost, to); } else { - u = get_user_by_host(from); + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags if (!ignoring || trigger_on_ignore) { if (!check_tcl_ctcp(nick, uhost, u, to, code, ctcp) && !ignoring) { if ((lowercase_ctcp && !strcasecmp(code, "DCC")) || @@ -670,7 +670,7 @@ static int gotmsg(char *from, char *msg) } detect_flood(nick, uhost, from, FLOOD_PRIVMSG); - u = get_user_by_host(from); + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags code = newsplit(&msg); rmspace(msg); @@ -730,7 +730,7 @@ static int gotnotice(char *from, char *msg) "CTCP reply %s: %s from %s (%s) to %s", code, ctcp, nick, uhost, to); } else { - u = get_user_by_host(from); + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags if (!ignoring || trigger_on_ignore) { check_tcl_ctcr(nick, uhost, u, to, code, ctcp); if (!ignoring) @@ -766,7 +766,7 @@ static int gotnotice(char *from, char *msg) } detect_flood(nick, uhost, from, FLOOD_NOTICE); - u = get_user_by_host(from); + u = lookup_user_record(find_member_from_nick(nick), NULL, from); // TODO: get account from msgtags if (!ignoring || trigger_on_ignore) if (check_tcl_notc(nick, uhost, u, botname, msg) == 2) diff --git a/src/modules.c b/src/modules.c index 61ae64716..0fd005760 100644 --- a/src/modules.c +++ b/src/modules.c @@ -23,6 +23,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include "main.h" #include "modules.h" @@ -60,16 +61,10 @@ # ifndef RTLD_NOW # define RTLD_NOW 1 # endif -# ifdef RTLD_LAZY -# define DLFLAGS RTLD_LAZY|RTLD_GLOBAL -# else -# define DLFLAGS RTLD_NOW|RTLD_GLOBAL -# endif +# define DLFLAGS RTLD_NOW|RTLD_GLOBAL # endif /* MOD_USE_DL */ #endif /* !STATIC */ -#define strncpyz strlcpy - extern struct dcc_t *dcc; extern struct userrec *userlist, *lastuser; extern struct chanset_t *chanset; @@ -301,7 +296,7 @@ Function global_table[] = { (Function) open_telnet, /* 88 - 91 */ (Function) check_tcl_event, - (Function) 0, /* was egg_memcpy -- use memcpy() instead */ + (Function) memcpy, /* was egg_memcpy -- use memcpy() instead */ (Function) my_atoul, (Function) my_strcpy, /* 92 - 95 */ @@ -524,10 +519,10 @@ Function global_table[] = { /* 252 - 255 */ (Function) egg_snprintf, (Function) egg_vsnprintf, - (Function) 0, /* was egg_memset -- use memset() or egg_bzero() instead */ - (Function) 0, /* was egg_strcasecmp -- use strcasecmp() instead */ + (Function) memset, /* was egg_memset -- use memset() or egg_bzero() instead */ + (Function) strcasecmp, /* was egg_strcasecmp -- use strcasecmp() instead */ /* 256 - 259 */ - (Function) 0, /* was egg_strncasecmp -- use strncasecmp() instead */ + (Function) strncasecmp, /* was egg_strncasecmp -- use strncasecmp() instead */ (Function) is_file, (Function) & must_be_owner, /* int */ (Function) & tandbot, /* tand_t * */ @@ -595,7 +590,7 @@ Function global_table[] = { (Function) 0, #endif /* 304 - 307 */ - (Function) strncpyz, + (Function) strlcpy, /* was strncpyz() -- use strlcpy() instead */ #ifndef HAVE_BASE64 (Function) b64_ntop, (Function) b64_pton, @@ -627,8 +622,10 @@ Function global_table[] = { (Function) bind_bind_entry, (Function) unbind_bind_entry, (Function) & argv0, - (Function) get_user_from_member + (Function) lookup_user_record, /* 324 - 327 */ + (Function) find_member_from_nick, + (Function) get_user_from_member, }; void init_modules(void) @@ -698,6 +695,7 @@ int module_register(char *name, Function *funcs, int major, int minor) const char *module_load(char *name) { + size_t len; module_entry *p; char *e; Function f; @@ -706,7 +704,7 @@ const char *module_load(char *name) #endif #ifndef STATIC - char workbuf[1024]; + char workbuf[PATH_MAX]; # ifdef MOD_USE_SHL shl_t hand; # endif @@ -732,11 +730,14 @@ const char *module_load(char *name) #ifndef STATIC if (moddir[0] != '/') { - if (getcwd(workbuf, 1024) == NULL) + if (getcwd(workbuf, sizeof workbuf) == NULL) { + debug1("modules: getcwd(): %s\n", strerror(errno)); return MOD_BADCWD; - sprintf(&(workbuf[strlen(workbuf)]), "/%s%s." EGG_MOD_EXT, moddir, name); + } + len = strlen(workbuf); + snprintf(workbuf + len, (sizeof workbuf) - len, "/%s%s." EGG_MOD_EXT, moddir, name); } else { - sprintf(workbuf, "%s%s." EGG_MOD_EXT, moddir, name); + snprintf(workbuf, sizeof workbuf, "%s%s." EGG_MOD_EXT, moddir, name); } # ifdef MOD_USE_SHL diff --git a/src/net.c b/src/net.c index 3e23acb97..5a63d5808 100644 --- a/src/net.c +++ b/src/net.c @@ -27,6 +27,7 @@ #include #include "main.h" +#include "modules.h" #include #include #include @@ -924,11 +925,13 @@ int sockread(char *s, int *len, sock_list *slist, int slistmax, int tclonly) t.tv_sec = td->blocktime.tv_sec; t.tv_usec = td->blocktime.tv_usec; + call_hook(HOOK_PRE_SELECT); x = select((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 (maxfd_r >= 0 ? &fdr : NULL), SELECT_TYPE_ARG234 (maxfd_w >= 0 ? &fdw : NULL), SELECT_TYPE_ARG234 (maxfd_e >= 0 ? &fde : NULL), SELECT_TYPE_ARG5 &t); + call_hook(HOOK_POST_SELECT); if (x == -1) return -2; /* socket error */ if (x == 0) @@ -973,7 +976,12 @@ int sockread(char *s, int *len, sock_list *slist, int slistmax, int tclonly) { if (slist[i].ssl) { x = SSL_read(slist[i].ssl, s, grab); - if (x < 0) { + if (!x && (SSL_get_shutdown(slist[i].ssl) == SSL_RECEIVED_SHUTDOWN)) { + *len = slist[i].sock; + slist[i].flags &= ~SOCK_CONNECT; + debug1("net: SSL_read(): received shutdown sock %i", slist[i].sock); + return -1; + } else if (x < 0) { int err = SSL_get_error(slist[i].ssl, x); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) errno = EAGAIN; @@ -1216,9 +1224,10 @@ int sockgets(char *s, int *len) /* Might be necessary to prepend stored-up data! */ if (socklist[ret].handler.sock.inbuf != NULL) { p = socklist[ret].handler.sock.inbuf; - socklist[ret].handler.sock.inbuf = nmalloc(strlen(p) + strlen(xx) + 1); - strcpy(socklist[ret].handler.sock.inbuf, p); - strcat(socklist[ret].handler.sock.inbuf, xx); + len2 = strlen(p); + socklist[ret].handler.sock.inbuf = nmalloc(len2 + strlen(xx) + 1); + memcpy(socklist[ret].handler.sock.inbuf, p, len2); + strcpy(socklist[ret].handler.sock.inbuf + len2, xx); nfree(p); if (strlen(socklist[ret].handler.sock.inbuf) < READMAX + 2) { strcpy(xx, socklist[ret].handler.sock.inbuf); @@ -1269,10 +1278,11 @@ int sockgets(char *s, int *len) /* Prepend old data back */ if (socklist[ret].handler.sock.inbuf != NULL) { p = socklist[ret].handler.sock.inbuf; - socklist[ret].handler.sock.inbuflen = strlen(p) + strlen(xx); + len2 = strlen(xx); + socklist[ret].handler.sock.inbuflen = len2 + strlen(p); socklist[ret].handler.sock.inbuf = nmalloc(socklist[ret].handler.sock.inbuflen + 1); - strcpy(socklist[ret].handler.sock.inbuf, xx); - strcat(socklist[ret].handler.sock.inbuf, p); + memcpy(socklist[ret].handler.sock.inbuf, xx, len2); + strcpy(socklist[ret].handler.sock.inbuf + len2, p); nfree(p); } else { socklist[ret].handler.sock.inbuflen = strlen(xx); diff --git a/src/tclmisc.c b/src/tclmisc.c index c5db7090c..bbfdb5299 100644 --- a/src/tclmisc.c +++ b/src/tclmisc.c @@ -25,13 +25,6 @@ #include "main.h" #include "modules.h" #include "md5/md5.h" - -#ifdef HAVE_SYS_TIME_H -# include -#else -# include -#endif - #include #include diff --git a/src/tls.c b/src/tls.c index 180af6b37..d3af0c6ea 100644 --- a/src/tls.c +++ b/src/tls.c @@ -531,6 +531,12 @@ static char *ssl_printname(X509_NAME *name) /* X509_NAME_oneline() is easier and shorter, but is deprecated and the manual discourages it's usage, so let's not be lazy ;) */ + if (!bio) { + debug0("TLS: ssl_printname(): BIO_new(): error"); + buf = nmalloc(1); + *buf = 0; + return buf; + } if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE & ~XN_FLAG_SPC_EQ)) { len = BIO_get_mem_data(bio, &data); if (len > 0) { @@ -564,6 +570,12 @@ static char *ssl_printtime(ASN1_UTCTIME *t) char *data, *buf; BIO *bio = BIO_new(BIO_s_mem()); + if (!bio) { + debug0("TLS: ssl_printtime(): BIO_new(): error"); + buf = nmalloc(1); + *buf = 0; + return buf; + } ASN1_UTCTIME_print(bio, t); len = BIO_get_mem_data(bio, &data); if (len > 0) { @@ -591,6 +603,12 @@ static char *ssl_printnum(ASN1_INTEGER *i) char *data, *buf; BIO *bio = BIO_new(BIO_s_mem()); + if (!bio) { + debug0("TLS: ssl_printnum(): BIO_new(): error"); + buf = nmalloc(1); + *buf = 0; + return buf; + } i2a_ASN1_INTEGER(bio, i); len = BIO_get_mem_data(bio, &data); if (len > 0) { @@ -712,7 +730,7 @@ int ssl_verify(int ok, X509_STORE_CTX *ctx) !(data->verify & TLS_VERIFYFROM)) || ((err == X509_V_ERR_CERT_HAS_EXPIRED) && !(data->verify & TLS_VERIFYTO))) { - debug1("TLS: peer certificate warning: %s", + putlog(data->loglevel, "*", "TLS: peer certificate warning: %s", X509_verify_cert_error_string(err)); ok = 1; } @@ -799,7 +817,7 @@ static void ssl_info(const SSL *ssl, int where, int ret) SSL_alert_desc_string_long(ret)); } else { /* Ignore close notify warnings */ - debug1("Received close notify warning during %s", + debug1("TLS: Received close notify during %s", (where & SSL_CB_READ) ? "read" : "write"); } } else if (where & SSL_CB_EXIT) { @@ -819,10 +837,16 @@ static void ssl_info(const SSL *ssl, int where, int ret) SSL_state_string_long(ssl)); } } - } else { - /* Display the state of the engine for debugging purposes */ - debug1("TLS: state change: %s", SSL_state_string_long(ssl)); } + /* Display the state of the engine for debugging purposes */ + else if (where == SSL_CB_HANDSHAKE_START) + debug1("TLS: handshake start: %s", SSL_state_string_long(ssl)); + else if (where == SSL_CB_CONNECT_LOOP) + debug1("TLS: connect loop: %s", SSL_state_string_long(ssl)); + else if (where == SSL_CB_ACCEPT_LOOP) + debug1("TLS: accept loop: %s", SSL_state_string_long(ssl)); + else + debug1("TLS: state change: %s", SSL_state_string_long(ssl)); } /* Switch a socket to SSL communication @@ -902,9 +926,9 @@ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host, SSL_set_mode(td->socklist[i].ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); if (data->flags & TLS_CONNECT) { - struct timespec req = { 0, 1000000L }; SSL_set_verify(td->socklist[i].ssl, SSL_VERIFY_PEER, ssl_verify); /* Introduce 1ms lag so an unpatched hub has time to setup the ssl handshake */ + const struct timespec req = { 0, 1000000L }; nanosleep(&req, NULL); #ifdef SSL_set_tlsext_host_name if (*data->host) diff --git a/src/userrec.c b/src/userrec.c index 61af179d5..66bb8f16c 100644 --- a/src/userrec.c +++ b/src/userrec.c @@ -171,6 +171,23 @@ static struct userrec *check_dcclist_hand(char *handle) return NULL; } +/* Search every channel record for the provided nickname. Used in cases where + * we are searching for a user record but don't have a memberlist to start from + */ +memberlist *find_member_from_nick(char *nick) { + struct chanset_t *chan; + memberlist *m = NULL; + + for (chan = chanset; chan; chan = chan->next) { + for (m = chan->channel.member; m && m->nick[0]; m = m->next) { + if (!rfc_casecmp(m->nick, nick)) { + return m; + } + } + } + return m; +} + /* Search userlist for a provided account name * Returns: userrecord for user containing the account */ @@ -224,24 +241,71 @@ struct userrec *get_user_by_handle(struct userrec *bu, char *handle) struct userrec *get_user_from_member(memberlist *m) { - struct userrec *ret; + struct userrec *ret = NULL; + + /* Check positive/negative cache first */ + if (m->user || m->tried_getuser) { + return m->user; + } /* Check if there is a user with a matching account if one is provided */ if (m->account[0] != '*') { ret = get_user_by_account(m->account); if (ret) { - return ret; + goto getuser_done; } } + /* Check if there is a user with a matching hostmask if one is provided */ if ((m->userhost[0] != '\0') && (m->nick[0] != '\0')) { char s[NICKMAX+UHOSTLEN+1]; sprintf(s, "%s!%s", m->nick, m->userhost); ret = get_user_by_host(s); if (ret) { - return ret; + goto getuser_done; } } + +getuser_done: + m->user = ret; + m->tried_getuser = 1; + return NULL; +} + +/* Wrapper function to find an Eggdrop user record based on either a provided + * channel memberlist record, host, or account. This function will first check + * a provided memberlist and return the result. If no user record is found (or + * the memberlist itself was NULL), this function will try again based on a + * provided account, and then again on a provided host. + * + * When calling this function it is best to provide all available independent + * variables- ie, if you provide 'm' for the memberlist, don't provide + * 'm->account' for the account, use the independent source variable 'account' + * if available. This allows redundant checking in case of unexpected NULLs + */ +struct userrec *lookup_user_record(memberlist *m, char *host, char *account) +{ + struct userrec *u = NULL; + +/* First check for a user record tied to a memberlist */ + if (m) { + u = get_user_from_member(m); + if (u) { + return u; + } + } +/* Next check for a user record tied to an account */ + if (account && account[0]) { + u = get_user_by_account(account); + if (u) { + return u; + } + } +/* Last check for a user record tied to a hostmask */ + if (host && host[0]) { + u = get_user_by_host(host); + return u; + } return NULL; } diff --git a/src/users.c b/src/users.c index d5c48f234..928f85ae9 100644 --- a/src/users.c +++ b/src/users.c @@ -523,8 +523,6 @@ void tell_user_ident(int idx, char *id) struct userrec *u; u = get_user_by_handle(userlist, id); - if (u == NULL) - u = get_user_by_host(id); if (u == NULL) { dprintf(idx, "%s.\n", USERF_NOMATCH); return; diff --git a/src/users.h b/src/users.h index a273ca95c..6bc27c1ce 100644 --- a/src/users.h +++ b/src/users.h @@ -185,7 +185,9 @@ struct userrec *get_user_by_host(char *); struct userrec *get_user_by_account(char *); struct userrec *get_user_by_nick(char *); struct userrec *get_user_from_member(memberlist *); +struct userrec *lookup_user_record(memberlist *, char *, char *); struct userrec *check_chanlist(const char *); +memberlist *find_member_from_nick(char *); /* All the default userentry stuff, for code re-use */