pax_global_header00006660000000000000000000000064152473004270014516gustar00rootroot0000000000000052 comment=9c00fe1c183dd733da3dacae8bdbc1ddc887dfdb eggheads-eggdrop-9c00fe1/000077500000000000000000000000001524730042700153235ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/.github/000077500000000000000000000000001524730042700166635ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/.github/FUNDING.yml000066400000000000000000000020451524730042700205010ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username buy_me_a_coffee: # Replace with a single Buy Me a Coffee username thanks_dev: # Replace with a single thanks.dev username custom: ['https://www.digitalocean.com/?refcode=2b2e5ea689b2&utm_campaign=Referral_Invite&utm_medium=Referral_Program', 'https://www.eggheads.org'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] eggheads-eggdrop-9c00fe1/.github/PULL_REQUEST_TEMPLATE000066400000000000000000000002171524730042700220650ustar00rootroot00000000000000Found by: Patch by: Fixes: One-line summary: Additional description (if needed): Test cases demonstrating functionality (if applicable): eggheads-eggdrop-9c00fe1/.github/problem-matchers/000077500000000000000000000000001524730042700221275ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/.github/problem-matchers/gcc.json000066400000000000000000000005121524730042700235540ustar00rootroot00000000000000{ "problemMatcher": [ { "owner": "gcc-problem-matcher", "pattern": [ { "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } ] } ] } eggheads-eggdrop-9c00fe1/.github/workflows/000077500000000000000000000000001524730042700207205ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/.github/workflows/dependencies.yml000066400000000000000000000162411524730042700240750ustar00rootroot00000000000000name: 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.0.1"] continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: install dependencies run: sudo apt-get update && sudo apt-get install openssl libssl-dev - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: tcl-cache with: path: ~/tcl key: ${{ runner.os }}-tcl-${{ matrix.tcl_version }} - name: Build Tcl if: steps.tcl-cache.outputs.cache-hit != 'true' 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: GCC Problem Matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Build run: | ./configure --with-tcl=$HOME/tcl/lib | tee configure.log LD_LIBRARY_PATH=$HOME/tcl/lib make config eggdrop fgrep -q "Tcl version: ${{ matrix.tcl_version }}" configure.log ssl-version-098: name: OpenSSL 0.9.8 continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: "eggdrop" - name: install dependencies run: sudo apt-get update && sudo apt-get install tcl tcl-dev - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: ssl-cache with: path: ~/ssl key: ${{ runner.os }}-ssl-0.9.8zh - name: Build OpenSSL if: steps.ssl-cache.outputs.cache-hit != 'true' run: | wget https://www.openssl.org/source/old/0.9.x/openssl-0.9.8zh.tar.gz && \ sha256sum --status --check <(echo f1d9f3ed1b85a82ecf80d0e2d389e1fda3fca9a4dba0bf07adbf231e1a5e2fd6 openssl-0.9.8zh.tar.gz) && \ tar xzf openssl-0.9.8zh.tar.gz && \ cd openssl-0.9.8zh && ./config --prefix=$HOME/ssl -fPIC && make -j4 && make install_sw - name: GCC Problem Matcher run: echo "::add-matcher::eggdrop/.github/problem-matchers/gcc.json" - name: Build run: | cd $GITHUB_WORKSPACE/eggdrop ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib | tee configure.log LD_LIBRARY_PATH=$HOME/ssl/lib make config eggdrop fgrep -q "SSL/TLS Support: yes" configure.log ssl-version-10: name: OpenSSL 1.0 continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: "eggdrop" - name: install dependencies run: sudo apt-get update && sudo apt-get install tcl tcl-dev - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: ssl-cache with: path: ~/ssl key: ${{ runner.os }}-ssl-1.0.2u - name: Build OpenSSL if: steps.ssl-cache.outputs.cache-hit != 'true' run: | wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz && \ sha256sum --status --check <(echo ecd0c6ffb493dd06707d38b14bb4d8c2288bb7033735606569d8f90f89669d16 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: GCC Problem Matcher run: echo "::add-matcher::eggdrop/.github/problem-matchers/gcc.json" - name: Build run: | cd $GITHUB_WORKSPACE/eggdrop ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib | tee configure.log LD_LIBRARY_PATH=$HOME/ssl/lib make config eggdrop fgrep -q "SSL/TLS Support: yes" configure.log ssl-version-11: name: OpenSSL 1.1 continue-on-error: true runs-on: ubuntu-latest steps: - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: ssl-cache with: path: ~/ssl key: ${{ runner.os }}-ssl-1.1.1w - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: steps.ssl-cache.outputs.cache-hit != 'true' with: repository: openssl/openssl ref: "OpenSSL_1_1_1w" path: "openssl" - name: Build OpenSSL if: steps.ssl-cache.outputs.cache-hit != 'true' run: | cd $GITHUB_WORKSPACE/openssl && ./config --prefix=$HOME/ssl && make -j4 && make install_sw - name: install dependencies run: sudo apt-get update && sudo apt-get install tcl tcl-dev - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: "eggdrop" - name: GCC Problem Matcher run: echo "::add-matcher::eggdrop/.github/problem-matchers/gcc.json" - name: Build run: | cd $GITHUB_WORKSPACE/eggdrop ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib | tee configure.log LD_LIBRARY_PATH=$HOME/ssl/lib make config eggdrop fgrep -q "SSL/TLS Support: yes" configure.log ssl-versions-3x: name: OpenSSL 3.x strategy: matrix: ssl_version: ["3.0", "3.1", "3.2", "3.3", "3.4", "3.5"] continue-on-error: true runs-on: ubuntu-latest steps: - uses: oprypin/find-latest-tag@dd2729fe78b0bb55523ae2b2a310c6773a652bd1 # v1.1.2 with: repository: openssl/openssl releases-only: true prefix: "openssl-" regex: "${{ matrix.ssl_version }}.[0-9]+" sort-tags: true id: openssl - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: ssl-cache with: path: ~/ssl key: ${{ runner.os }}-ssl-${{ steps.openssl.outputs.tag }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: steps.ssl-cache.outputs.cache-hit != 'true' with: repository: openssl/openssl ref: ${{ steps.openssl.outputs.tag }} path: "openssl" - name: Build OpenSSL if: steps.ssl-cache.outputs.cache-hit != 'true' run: | cd $GITHUB_WORKSPACE/openssl && ./config --prefix=$HOME/ssl && make -j4 && make install_sw - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: "eggdrop" - name: install dependencies run: sudo apt-get update && sudo apt-get install tcl tcl-dev - name: GCC Problem Matcher run: echo "::add-matcher::eggdrop/.github/problem-matchers/gcc.json" - name: Build run: | cd $GITHUB_WORKSPACE/eggdrop ./configure --with-sslinc=$HOME/ssl/include --with-ssllib=$HOME/ssl/lib64 | tee configure.log LD_LIBRARY_PATH=$HOME/ssl/lib64 make config eggdrop fgrep -q "SSL/TLS Support: yes" configure.log eggheads-eggdrop-9c00fe1/.github/workflows/make.yml000066400000000000000000000026541524730042700223670ustar00rootroot00000000000000name: 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: install dependencies run: sudo apt-get update && sudo apt-get install clang tcl tcl-dev openssl libssl-dev - name: GCC Problem Matcher if: ${{ matrix.cc == 'gcc' }} run: echo "::add-matcher::.github/problem-matchers/gcc.json" - 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: install dependencies run: sudo apt-get update && sudo apt-get install tcl tcl-dev openssl libssl-dev - name: GCC Problem Matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Build run: ./configure ${{ matrix.conf_tls }} ${{ matrix.conf_ipv6 }} ${{ matrix.conf_tdns }} && make config && make -j4 eggheads-eggdrop-9c00fe1/.github/workflows/manual_autoconf.yml000066400000000000000000000012241524730042700246150ustar00rootroot00000000000000name: Run and Commit autotools on: workflow_dispatch: jobs: run-script: runs-on: ubuntu-latest steps: - name: Check out repository code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install pre-requisites run: sudo apt-get update && sudo apt-get install build-essential autoconf - name: Run autotools run: bash ./misc/runautotools - name: Commit changes run: | git config --global user.name "GitHub Actions" git config --global user.email "actions@github.com" git commit -a -m "Run autotools" git push origin develop eggheads-eggdrop-9c00fe1/.github/workflows/misc.yml000066400000000000000000000027301524730042700224000ustar00rootroot00000000000000name: 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: install dependencies run: sudo apt-get update && 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: install dependencies run: sudo apt-get update && 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 0; # disabled fi eggheads-eggdrop-9c00fe1/.github/workflows/test.yml000066400000000000000000000064041524730042700224260ustar00rootroot00000000000000name: Tests + coverage on: workflow_dispatch: permissions: contents: read checks: write # for dorny/test-reporter jobs: test: name: Build, test, coverage runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install build + coverage deps run: | sudo apt-get update sudo apt-get install -y build-essential autoconf tcl-dev libssl-dev lcov - name: Configure + build eggdrop (--enable-coverage, debug) run: | ./configure --enable-coverage make config make debug - name: Set up uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b #v8.1.0 with: enable-cache: true cache-dependency-glob: tests/uv.lock - name: Install Python deps working-directory: tests run: uv sync --frozen --no-managed-python - name: Run pytest (populates .gcda, writes junit XML) working-directory: tests run: uv run --no-managed-python pytest --junitxml=results.xml -v - name: Publish test report uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0 if: always() # show results even on failure with: name: pytest results path: tests/results.xml reporter: java-junit # JUnit XML format - name: Upload eggdrop runtime logs on failure if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: eggdrop-logs # The harness keeps each test's tmpdir under /tmp/pytest-of-runner/. path: /tmp/pytest-of-runner/pytest-current/**/*.log if-no-files-found: ignore # ---------------- coverage report (only when tests pass) ---------------- - name: Capture coverage with lcov if: success() run: | lcov --capture --directory . --output-file coverage.info \ --rc lcov_branch_coverage=1 \ --ignore-errors source,unused lcov --remove coverage.info '/usr/*' --output-file coverage.info \ --ignore-errors unused lcov --summary coverage.info | tee coverage-summary.txt - name: Generate HTML report if: success() run: genhtml coverage.info --output-directory coverage-html --branch-coverage - name: Write job summary if: success() run: | { echo '## Coverage summary' echo '' echo '```' cat coverage-summary.txt echo '```' echo '' echo 'Full HTML report is available as the **coverage-html** artifact below.' } >> "$GITHUB_STEP_SUMMARY" - name: Upload HTML report as artifact if: success() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: coverage-html path: coverage-html/ retention-days: 30 - name: Upload coverage.info as artifact if: success() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: coverage-info path: coverage.info retention-days: 30 eggheads-eggdrop-9c00fe1/.gitignore000066400000000000000000000005711524730042700173160ustar00rootroot00000000000000# CVS default ignores begin tags TAGS .make.state .nse_depinfo *~ #* .#* ,* _$* *$ *.old *.bak *.BAK *.orig *.rej .del-* *.a *.olb *.o *.obj *.so *.exe *.Z *.elc *.ln core # CVS default ignores end autom4te.cache Makefile .modules .known_modules /config.h /eggint.h /lush.h config.log config.status config.cache eggdrop EGGMOD.stamp mod.xlibs __pycache__ .clangd *.gcno *.gcda eggheads-eggdrop-9c00fe1/AUTHORS000066400000000000000000000107201524730042700163730ustar00rootroot00000000000000Authors of Eggdrop ------------------------------------------------------------------------------ This file documents the developers, major contributors, and groups who've developed and maintained Eggdrop. A full list of contributors can be found in the file THANKS. Contributor information for specific versions of Eggdrop can be found in the corresponding doc/Changes files. Eggdrop was originally written by Robey Pointer. Versions 0.6 through 1.0o and 1.1alpha-lincoln through 1.1alpha-grant were developed by Robey from December 1993 through February 1997. As of version 1.0n, Robey relicensed Eggdrop under the GNU GPL. Robey's relicensing also allows for earlier versions of Eggdrop to be licensed under the GNU GPL. After version 1.1alpha-grant, Robey gave up his position as Eggdrop's maintainer. Version 1.0p was a maintenance release put together by Wade in March 1997 made from Eggdrop community patches. Later 1.0 series bugfix and maintenance releases were made by other Eggdrop community members. Eggdrop 1.1 development continued after 1.1alpha-grant with significant contributions from many Eggdrop community members. While it would be impossible to include a full list of contributors for each version in this file, below is a list of some of the major contributors and core Eggdrop community during 1.1alpha and 1.1.x: Beldin, ButchBub, cmwagner, dk, dtM, EraseMe, Ernst, Lefty, LuckyStar, poptix, Robey, Wade, Wild Around version 1.1.1, Beldin took on Robey's former role of lead developer and maintainer. Beldin and many other developers later regrouped as eggdrop.org and developed Eggdrop up through version 1.3.23. Below is a partial list of major contributors and core Eggdrop community members during 1.2 and 1.3 up through version 1.3.23: Beldin, BoGuS, ButchBub, Daklop, dk, dtM, easton, Ernst, KingKurly, Kirk, Melvan, |mmortal, NC, poptix, Q, Raistlin, smok, tdmarti, TheGhost, vod, wheely, zarni After version 1.3.23, Beldin decided to discontinue development of the 1.3.x branch and instead focus solely on 'Eggdrop2' development. Unfortunately, 'Eggdrop2' took more resources to compile and run than 1.3.x. Most users also found it difficult and sometimes impossible to compile because it lacked the portability of previous versions due to having been written in C++ instead of C. Because of its limitations, users continued to favor the more established 1.3.x codebase. After Beldin and a number of other developers publicly stated their intent to discontinue all development of 1.3.x after 1.3.23, a new development group began to form made up of other Eggdrop community members. This group eventually organized as eggheads.org. Because of the large number of outstanding bugs in 1.3.23 and community interest in continuing 1.3.x development, a new version was created from the numerous patches that had piled up after work on 1.3.x had stopped. This new version was released as 1.3.24i. The 'i' stood for independent and was intended to show that this release was independent of the "official" development group. The 'i' was dropped for the 1.3.25 and later releases. Below is a list of developers and contributors for version 1.3.24i released by eggheads.org on March 5, 1999: arthur2, Bass, Beige, Crotale, Daemus, deadgrrrl, Dude, guppy, Lobo^Loco, Niggurath, paralyse, Shayne, slennox, TheUnknown, TiTi, Tothwolf, vod, Whicked Eggdrop development after 1.3.24i has continued under eggheads.org up through versions 1.4.x, 1.5.x, 1.6.x and currently 1.8.x. Below are lists of major contributors and core Eggdrop community members for versions 1.3.24i-1.3.28, 1.4.x, 1.5.x, 1.6.x and 1.8.x. 1.3.24i-1.3.28: arthur2, Crotale, Cybah, Daemus, drummer, dw, Eule, Fabian, guppy, Jason Ede, John`, Michael, Mixter, poptix, Tothwolf 1.4: arthur2, drummer, dw, Eule, Fabian, guppy, Jason Ede, Lucas, mortmann, rtc, Sup, toot, Tothwolf, Wiktor 1.5: drummer, Eule, Fabian, G`Quann, guppy, Jason Ede, Sup, Tothwolf, Wiktor 1.6: Abraham, BarkerJr, bryand, darko``, drummer, dw, Eule, Fabian, guppy, ITE, Kirben, mortmann, Noyga, poptix, PPSlim, pseudo, simple, [sL], stdarg, Sup, SuperS, Sven, TaKeDa, thommey, Tothwolf, Wcc, Wiktor, Wingman 1.8: Cizzle, Geo, mortmann, pseudo, simple, thommey 1.9: Cizzle, Geo, mortmann, thommey 1.10 Geo, mortmann, thommey See also: THANKS, doc/Versions, doc/Changes ------------------------------------------------------------------------------ Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/CONTENTS000066400000000000000000000040561524730042700165100ustar00rootroot00000000000000Directory Contents - / Last revised: September 21, 2018 _____________________________________________________________________ Directory Contents - / doc/ Lots of IMPORTANT DOCUMENTATION that you MUST READ is in this directory. help/ All of the bot's help files are stored here. language/ All of the bot's language files are stored here. logs/ This is a good place for eggdrop to store log files. m4/ M4 autoconf macros. misc/ These are misc. source/script files. scripts/ Tcl scripts included with Eggdrop are contained in this directory. src/ This is the Eggdrop source. text/ This contains the motd and banner files. aclocal.m4 config.h.in configure configure.ac These are scripts for automatic configure of the bot. AUTHORS Contains a list of Eggdrop developers and major contributors. ChangeLog Contains git commit logs from each change made to Eggdrop in the git repository. COPYING Eggdrop legal information is contained in this file. disabled_modules Any module listed in this file will not be compiled by default. eggdrop-basic.conf This is a minimal sample configuration file for your bot. To unlock the full potential of your eggdrop, please use eggdrop.conf. eggdrop.conf This is a full configuration file for your bot. FEATURES This tells you what Eggdrop can do -- a kind of advertisement. INSTALL Information on installing your Eggdrop. Makefile.in This helps with the automated compiling of the bot. NEWS This lists significant differences with previous versions. README You should read this file before even trying to compile Eggdrop. ssl.conf This is an OpenSSL config file, used for generating certificates THANKS Contains a full list of Eggdrop contributors. UPGRADING Contains information on upgrading from a 1.6 bot to a 1.8 bot. _____________________________________________________________________ Copyright (C) 2003 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/COPYING000066400000000000000000000432541524730042700163660ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. eggheads-eggdrop-9c00fe1/ChangeLog.gz000066400000000000000000012462261524730042700175310ustar00rootroot00000000000000jChangeLog\r㶒&?b-*l&'gjI99U[[*%){4!%9MIe/>5UdF>F w:P<,49wҴ o-xqA=gf滺EL~m,Yُȃ:&bxCX.|+YˑaḋmΆ?\0|WMW1PbD8?fYσ?q5XJNP-!2 檮"]UɊ8wn@p&Xz޳*yU',%N8e5e f5aDѶD;]t@'”eU5',XVqZD8p `崨IΑR984性V,Y +i8GOP-J8̀Nl U=n\yEJU͇u㸩G|/q0!2safic{|z#| <51f>>'x|AO52 ||g.3@e3w7qxO;|a|A{|c>3f}3nac z| <ǚt33~Af>=/u)7S|aC|a惰3bةE< P' tbS`Qf>jYfKto ^S#l0wW>l6aA^[pZr nt?>f/4z B_/3%# A~i< AlH! tTEr74˴Lm/4awtdx#VgNV= d}#@!r+EUOV=盬Zgۉ7[DAT[1u[8,-[`$Na?IlGjy 04U3mFprkq&Fæ47%!i* eTW*( ) xaV̅m |O/ БD,ۤGՖ}&@h' `rB !^ǻc%{el8,_N$)Lnps]fzkYczE5DU _4PG-kڭ\J/5Y`p2 "[K7uS˝$-n3tljM(4RK<`e0>wQ!]ox t zi}{s՝v@ϯ}; || ?cmnI( \u_6.ae8KE*E|OSق }pR L{"v80 yx8:)tnw2ֽM7dnb,m@XKuBtgeU>5/Gx<Vh%IU>幊ˈiQ5u;' j8#Ĕ< K q6o" '$eiq)* kB5RdE(nɌKU凰'iϦaE( aVka6,ML/jDpHf=Eo41Aݖȗ>CcnR嵬k8>y p6lx/(ֻyL9)-ORmH_^U3Q0;^&,OԻ)o^~{-y/7s'淿9Gԅ*Sy_'NIf= g'0TvN9ɱna8DvrD~vrA7;9og'8wbqt/;9^ߏ;)p';9d'' qx/;9e'Vxf{ΪPwd_r9/ 57;96D'y9auۧtcHs<'9Tw"9T ձ;i5dSi! i!ܩ40vj98yi=Ms|09$B59iV3Ns^j1DWk&36sǣ>#g|Vg$f ]UYfUވ x L#?C H}_2QhN@=>6sE.hZʜSe+~uBq쥧zdڈS3Ϝq%Ih :.Dʱ{90/ [w4gdXsC_b'M91֙ /KiU~NhC9 cnGLgGo3E=?Nc{$7xcC[uOO/=)de=cn!Q-EKyr5 *dLnXB~W)߈uqOIǒ1Pr)9'Yq_$qe.\70+p =+`)T,yXhͲ{Q T':Sv|ˮ\5v>K(٣ɖQԉ(:Qcv4ً%K`\PZSApC,5׆fŻw?رcR0Vq/}Ee/B׷km}{oݡ cB iBbh}%i] yidgb?8U5dWnW7r%QyYb'(I`w_x?s*2ym #)qn- 0t٤16Ig*)Qg%I7b /šרr[Wabum*JP4|(R$xFސA\@LK99/od%4E ~]O7R>?Q@2IT,]k>NJ0>lFTD=z,c'8b J(؍Y"Ja ^i#P)0c} h|Tr^W%ʆޞ*9Jfw*PhlLޮAjZE#\dpUYNԽRN(ZX]Et+jLF#Ze[#+wƄȨ0"ZIP,3AL eO37DzF)8xU"uTqiUMvR}yI5aWޔ~2Lܪg;U`3XwƦ JLU;?_ -]c,Z"GaPתoV@`~=UN'~)$kVWOB~xЏXZ <2eǘ00 M9#xy+\=++VƛXҋW]]M5ݶBl>,ݷ.9 JW1uyZ!T7̕oՋ6qzjjA؁7ZVub*9pisY$sz|){*ILuiQqKSZYYTIks?^4w̱cDT@ۖ!'.Ǖ5 5l<1I%)*QV*M. (U \ՑHš꒮TQ !U)J:!xZ &q9@g˳ݺ|}6<7i@-ۆ_]s -I4po<݉7^@ b*!Y"H8TȻ B Hhguo=T0NYx#BV2K tPMDIX^<ǑS7SZX?wx@I`Zd!nlMY jj%;}ksƱ> nVkAЄ2ej}pt'bډ*s⮪uԑ)^Gj >w8ǡ,;nYZ|TM Kg4+Q /jmՐwRWEl D7I&-۠~+osknHҿ9[{%RV]6s|۫+HH@+_=p%*B*Uv堇O´r;9jࢎݮlGVCP(] g:7;[)M\? m>Qj;) w0#I$9]o Ą$)p!Ų}9*F1M Xu_Rk埚_-r~Gukt?Q,vQY\tx}_y:0cʩRxiR/N\y4 P4cjcl4Q̍\/#'\b rj| G8s^^}A"% 6w&u?3y^;Q"1,Y G]a=3WzF% bن9Tޢ]3'-v5y/-%#35- wFFʦ|Q}m)<>BR}MOb4Ja3GL"P-Km8v_!Bw;o[Td? #IZ nFP*@9Xn9@&`F2CW3?Ŗso _]t8 [E%yI3%bB'47߉%͇ ,N%C]њYHv?.z"bZ4Kw:_t#p?b&bRN3aB>uhǵPĹo;LtW&9y60 OG\Z}a6=MKc0΄danhZP 394LJH]Tm0*e*mQc.2<#ft|' H䓮<  nlC#$vE. v68$t˪ՊA .k(s"U6iԞN<ǔ$qjG a8ڤYM2s.ǧ)sXj"p" L:YD'Y܉Ch9(I%4rmmq@07s %T@qS+kuJt1UK GQlyN]MmCV,X4 4D3;:l 䄪WEϹ0c#Ue88]? D=i3դ}D~ڬZCfkY.!Qѥ'81t ִ=,I]gus;Nq40;l=^XǺ\Y yaRlaᜇiŤ -1lƹof@jViYUGbN8nH)Bb Tt<%>(lMOtZ{oc{;ZU#/^o| (|-kzQ gjRGvj=`3{$¬ď| #怴흴P̓Ŝ4R%E+s~Kn%19[]r-ݼN^&7vKS:ΈDq0ry^a*@%? |mR&ۗd2)o@I`^>"lO8q F‘أ Ӈb!xP18F"˹!l(kmh^9V++&_|m*=}gMٖCJݛygj믡<w~$@+fsD?ϥ,((yۓg7]b Y 0ݢQDerfCy K~c}m]|x7W =D~3(jrALۗHP`DfB7ne2F°+k7<41S)Է79Й@Bg;m 0w[1ڣ̷9`/]jYC94YZg.<9#Qm")F{FF|h (Tkk8H(O6edQte~zHU_Le/'i4&gR2]Y A:VʲI|'rXm)qR!F%Ts1;ˋ_A-:ttxgC3!i8BmB%VT! oxFcV'4`0#ySU}͡d_p0&EF';'ulyکV( ^"8:~Te XЀ0yadJwfC*A} ZVjXl ݨg/52USؚ 셟Rv\Zr_f 绞'L)9UKfPָ6CV/fDdTSE*zxI p,TŴ$IM^X+ WZΨ_/.X^A d1ԯ ?:tr)ۢK>eIz5 C 4U|_1&p Ki" ÒPRt4AFl.桊L!c bU2&{R^ 0i.QG즮@,a[2Uy7τFHV eĦ̫3|&#bZ9GMR'Mzc`~BRqQbJ=&!Hl/0 Ce~u%6UkʃWD]4ibƚ>o^/GoH/A)$)!8Zn9櫸<%[%w>oOeΈ, yL$3޾W=5; u2yVUB_*jļj%_d+ N55# *bc4 x9k};.Bv!;:\Y{,-cl\`LV#Ko-ݺ%dR6:4YA0\}ymV.:uH,CuRޣRҋo[֫8/Xyz!jzI'yI12ۊ'AgmG=ܠG-8'rc^(o< 7j^p/*}:&;0c:+nQhNb^Lp%'̍)i4s%(T!"]{fW =0YP}gڣ:JK'%6s˜ρR t1HJ])폡0Z5 mۇMQY7YJ_:ۓ@lϏCh6">ߞ#]Abm(LSûu?]}d͗U_`Nڮ%ׂlty^A|>,AX6Oʵ8VʢKc5Y‡:c|V\M%{ Ude-LPVx73Q )F݀qf7g8&8>dv|'i;HFixE@*mXBbU4瑚ј'4X(%!g;[p?yɗw5KoW?76-hk[ (aU2e[?̔Eu*E$Hf򵽙s})>7kD v=8F%)ݞëmҠ|gP8G7rub1؀T: ș08]~֚wQb}/xw7߫BB_0AU)VM-?yZ{ʒ6/8q[ǭgyR7CU;w9z[OH7f3C6[23nk>ݒ  9MnRKЉ9%qOj)gMLS|JF64{B5wY&-Z@Tg&$RIRsnl|t.1Dl$R;OgښƱ{ N$cHd&JӳIVG4d/R$+mm:2$n~uLu1OW[GEwب9֏~,t[y4(k J yګ>3V35պ/P3͚P|=gb*~y&k>Z7CL/-dXi53Hg!!4Au65"e*vED}Lj'Iw+:Ow/xΛjx7f"[盏# .:dN{~Z'H Z@I(d/Ͷ Or-"_jv>{XҔډE"m Dţq-(u0:_>_|/:o-a|!ʩq' ZPWZaat(oݬ JzRfo!!p@ ղ\/|VX7#!T r>~e IJ!ݶnH<]Br2d4beI#(NjaB3 hH^jn_YpUu`+ T@5>_goIh@E0^JvGTlW| _Y~Q+u+/hMFԮh&vFhFvcjkhF{h7hG5c |[= nk}0Ah;hh;xG=hGt5F{hpRP=,4;.X7t F U4kWy|XƇSĶ\ku6?q#ٵͪCq#T7;q#Zո?jƍj܄өرunT)'O\1.Pj10ոu*U㦯ƍApjnK/l] :wsZ/^ 8i[uJQEjSQU  8̈́  v_eh%+tx5;"Պ4".C.Rޠ1Jc#x:O{eն_?G0ܩd7`%띳,V\b;.狢?4gWPk=GzH yi^/IljmN>- }\_X#}lWXC0n wP+)L='& 4Ў&)i'G+)MRiƪ' Gj>-Q]{JO\9GŋG|CwG,?(EYs_ςTVftJ`Lw) 3w@ԨnTi[-(s3TCsc2Cffh`yx#Il/ng5b`ZYTݓdQk-$D?6@U.BYOf;:e2ļ`*x8$ߵ@?Ki/g.a5F$ } Y B &r-7ם29 r)hSgxyUKZGLi #=Z:Ӵ(E!B5嫂r[I* 2l1 z!Qκ)~ )V2OV@@VhQ:'D%ahK(|mfKbȎ5[f\vP_=\\W.XC{ 5LU+A9 tYS/Orq!*NP_z|י?6pL|:, ΥtUUQdr"3;kU<߸4" y %HlbA_.Wbv}~>/xy˧e1ڗh*B g=*VǥQ%W~\.?/t>y{1y˛.$;\n@w|%><~?jn._ޜksH7# >b_DQ&ݰWh^a{jjƺO}{~ \ \E"?<~cPdc tP;tע:[̱vcknp˥>KbMG]L# UgL0qΏ>gB#(q~a1Z`#'HL8@p.`a &΂<F@r׀*!kH1cbY Z--riKg^0iŴa%L b^4-iմAeӂM daQ  8[(tn945T}|˥s(,4iKI} +vRݻH6iw6Իo;)ݪo+yLa7̈́poZPۻm})aΦ^S-OP˖pgІ\ { wG MM\POy֓h$e@fLG%#7ߋiCK3}Kj{JoBB֙rs]p-n~bCdl%tw4nYdI7]ZWFpFPy;dޚa2g& Y+EU[ߋykJ_w2"室*젽erfEyiuk4k}w/8! -@dzr"q u # / v ^L/f=- O\q{^{wtm mF U[Xcn3zfVW"]joIřE?G~%zWM10޹b[7@scT쇾_n(8RokxoD1Cj^ohLfg1RM\OSZM;K<+mYi5ضg3eH>{/5T4 i1-L#zГ'{ݪ)wtpڴHI;e3=lBz;6ߡ!1} _6 :/vՇMA\˳i*?&>rgɯjUZϫ^ۮסMEƳa3!#4HӛN->ns8 &ǪX~#]5]d\J} 27?`ghgK;{AlW0sGմ6٤GZd)̓4Cr/Sؕ6ϡʢS0!.νlI+zO*{;¬1 El\|;v(DR-( 0ѻ?xkzvDjTaR+uNLʒd gy@Sh=woa 9AZ,Ρ;|maî)'!ai^U+^fsΝ r+üNɨZ aOBMhS  j)2&8 \#Rp9;2z@Tvix_OjTJ  ['qi}UizpEQ9Jb><Փ$a(iP=>wVs(`tIeSHƏߣl}5+}†u>n`Π5iLc+Eq>PBLM?۴Ai ! M~nd1b_J'ȓyfDsbv}%Y3Zer+ͨ2\="rsʙΉۚVhs7Șm;!}Qf$B* n33RZ?)]gS^tҼ<(-A^s T(|$NbCsx9]7c]CFcVA@I"NYҜC PS+:opUi/r"ÂM2-7|۩ rm%9h*2Y-\7CfyU,݃9?/Rڍѐ r5I(tYq;[*9n@/[ 6T8{٤~Q$uw.>v+z;YrM>84U왲W uV kqNbQ|ehG|j,܀-? nӕ$p[tBp.kRkEgyGȊi2A%<q!%s{˶=f_;Y8Q8v]T)(Kn$aRYWK g[@Q)|+޹EyݥO\ӎ HiF߃ύ4ƹߞ9~Ž3Gk/$rCWGt rNHp'۩pAԩJk@솒aUT5]>gHG-0sy܋\a{p)moE>f}B=QF:okrBvrzRCٹawX\PW+\7SeY~S]mjÜlFsp%d\sw2\]Tqͧ ®G/R Zt>wzJ&ֹGSӎpr# Uƙvӻ!kYU&aNy 6L}gaD<|郞鸾UNJOzgikg8[\Ejv[572H visTwgbR&I3W$ŏ-P"x-r㝡 BRԾ,qO7Y\hc5[of ߇k T^W H/1n < =pJ~x;bڑ߁B .3$ f^))}X~}wqO@uUeCnW+Ɗ+Y>X\,ׯx[qKХ뢢  f S sklNJ#u"KL(kLWÜG,&g#ݜ %1m V8BW)/X:USQؠ~tD?-d;9uیuN/R>΂ӹS@ΛEiy+[j#韹"ݺjy;%r̶ ˟|Cn#.:vnx>WbRsw.NX!Lg_៧b{]` S}/9Vj9ɜ7r {˞]% AQs"}? z5`) Aq%-8s7 (%4x%'_ǯ.ˤS^'·}ߴea#=U6Zl(3+[.ؙM)]oߊOgO:[)r>_\o%O٩Llܬ׫r C _d~f-Yy#^i F/a].~[B,.!L̷p.cR6hzQBZGyqIVK}Lm)N&_g#`y]/7EW1zb[/QksL>|k3UL.Ws~x/γ/|ٟ:Da)N|ݱdm3|.7?|Et t ?mp\2C ^L5֌;܉ℰ(qs`b`J'^৚xГZ;M+ODxpa"tG_$ "ayTeff\ԛ.pef| 'DQ)d|0R'u8 q,v6YG)MI{Pds'[lK6v55.β)V}Ttdu'.|n"<|Eϫk}Y,cS]ro.G3G2y5W.#y¹' fvVϷל5v޼|Z k2vY+O̍PGh[kS)!IJ+?hε.(>GQƾ r +#R9A',3Ps0/~#{y>E,D=)ylUNb)h "N5H JTN=]@I5ϫ#q'لh"Ρ?hꚠzeӅTmszXRYJQJ7ɒ9S`|kgHU5Vw2W*#=*vwؗ칓 "zN4 QLP>]7]ޮ>B~0|J5T7NZ'RMnkh4*f8A9Qj\H sss>LzMF ]"4%Oݲ[a4,m9#Vl'lH(w춄wg "sn.wF.<ྸ*!."iGhL~~Q0W: ߝvHcݑL`4īk~ lTw AuW\:u<~$ڍ/rN] 3EB>Op(Nu׋M ;:CSo_޳ep:@NA'~TFnq&nZ9ֹhM,m%@CBOoOp.W L>"DyR֧(b]f?r$Q?8ҸGpBǙՄc&"FxH6#ّK~ uf\&k| ]^݃р8 .I,<z8v8@nVWUt # {#(X̗48gySRBD^/8-~{nWb+Y>E,HDCO!s: pw_?]A JM ˥@& ::nvROG?A)(Ĭi#~T dqD~ Tn598Nc찓uB)b AvLbGᇆY.ʃA"O;KA5&\ày")pH*[rqڴTX fm;ӟ>+(]7N,#ڤj/!,AHB !?VGLg !NvBTffY$ʟ1[`Y1ߊdzK :=E$]2!K_%t&cyMP~oxgq !M-,M!~Yds9S#TtRK-4L  eBe&|PG;17lV@fd$z_{ՄT4+eo*RL.1gk+s|`}&zOmDbx_ȝWH>>ye7n|$UJ)XFm2'X ?o L^0fἓ68yS.%TU'P+/qibg? 5]>T1„a;Ր7{2w 2GwNf4dFOBf!ʥXZ}pgv@eٗo6.xkLFN<)a!psQd~GEQ .# FHdDd_"TfhFc4ǀ*qp%End<O('|^ϛDx*-MXGQjvf.7&j c$>naVh ^/sN/rDA4}0C\ekjxy˧nn8pGxAf-/q'=oꀒ' ԍG Ռ,uA!q]6-Ao!nE,лr)]qWuduuU/B-Ʀ%hf#kta?J%Zr-{`Yk 5,ѻJm Ty4KxUʟDCؒbe%Dn&&P`] \fxy:JU($ 8P㰺8t l`EzY VرpbOO;&~_OJΥ[@GNH* = P\}Wj V*J}N{h؏ e1bqF[yamb?yZkS*ׂ 1܌ C0(ɻdEsGX $Jb',Qd"Q*3bycP׏kP;Qj6h+i4|,RW2OrDg4| \W C4|flOg4|}o3ZFk4| 4} Xo al_7jFЅ(u5|螆oޕ w4|hoAW7 i= = _2o 5]eVuwVFݝݙݝݙݝ ݙݝݙp:ݝ;̺;Ypuw^ 0#3BG> LNŌA:AN$v9ܱa>r8T_pU;r嫷98E?nYzÊbcӎN5OCBsDi$~'#uQ7uԵOGӑ!A{Wx(Mԍӈy^ qR`vy(}sHV9a(Bbʲ8Fމ8jNtjsq]0%*™~ q䐺}̘!5Xw1H#t< bC7]pi|iB0 4MmJsqHB7 0dꅍcȭs  PQ1m&ABiH)‘|q4=(Ns1|"qau F8]EE_>ܚM4,=KK*܈v>b{vU+HL%'ERK-nZOk&\׼uMP:ɼTCy.:ie^Āym0&AbbEϢrI$H=Wly@=F cqÏ&n)\z|RwCqM$i'E>J4^g,݄89:+[}|b7?]9D䪿%G0&Cv) +\D(~[7vRU%aEM ,;I(ǜ~,@Ik*bGi5'џ66qn{#3FQR PSʬ5g~ƏD#=RXmI(5|uCki`qy˷[ )a$,C%zՐwpmaTd M޿3XwTEj6wHC;?q⺡i }m040w; &ֿwFaB; aZ6w4őǷ亽RĊ$xiYu I;t KfM`3_w;zF7Mc7;z҇ލݏ軠P;:ҧ+۫`&$}JH{}A@_рK!F~&cLzA@CwA@ZL[ [:! Emc! TBú! q; c;ׂdmoNT@:R{lu9z ]H ; z#9N(8o{{}WVzWl_NFE_4DހakNkc+#ov-p1lq_d./J4j3"f9|Oy1૕ef| qWM#; Y,bm @'Wo_^\5( 贜i$u^cGtVt/@ޞ ՚Gl~ϝ4![3vKZLccI,͓6oo.7BچuΦaa5M/da 0bM(Mj)XOQPqNSjsA]Y96" )}IWt4e, ٦; KrJuqm6,"?\gۄ_BRz|sNVHvcKGæT**cF ikCyQm^˪?y/s;ΈTi^˃3 i5ujWeZzd[8i6 $qҭ TWHW䃮ʏ`6LYd ѱaHGo+UrX_\om) gj`>cG'@S 4^9]%17T^)RR 4{icz]Pk3p0 rqerv_>^:OqyZS./lM]"'>~E8NX|՜)|&ik֦*ybf52T\]~ۜEy ڳ`>wxvnfFPb73}`z1{~o..>l&a L%9{Riu*-U]wqg?IgQݳ|4ISN;6^i;kcT}K| )׷+mצqzA3fi:Wr퟿i&i'fO_ i헛dlz?_:9YFnR KMW͙oj| y'ɪ*3YRY0"&uIs=<4ݎ3[ %Üo(=yr@GKl6S 8CPe}0}ran6qX֖'S[dQ!LkDRDĂL "%_S3RFvzI?s|=>n~fgsBnQQyن@Km2L_$rI{>zo)8wT]W[/r͓<FZä: y>}-mxOmCϏRUz}]MlȕHbG[t,Ȋ"&4D  v7|z (h,Qj֘fy4^2mEEFQC鮬B rɶU*'y/-uMk-s0SV۵V1"Pԡ < }ȶKn4dpNar_95l0\ɹٶ9VrIH.@~(샣(;u@T(z+)[)y |aCV-a){'ZEeWHwa@# muYݩOI'߶sXT1XT+\*,]θ_e ׆Rm_!(ѻUoHɈ*+YLMҵHӇZ.[go_-g 22<%E6:v%, 4D}e,_+cвޯS0 3ky Vn:j hHTnaq{@@k#PEs8b؞'h&YE:  +נO|,l?Tkm3 <mE.rk0GI&xȵ cg az1H?<;5o| -,Zp}Q/SB mHXGWo%ãKt†3X&71.5Xyw'-s0߀ vrcj1oCC~Oh1e\XNV϶,^萐k4} %]yV3^d;Q:LzA+=;gnzx" 95mT'DT2A`֪,+n;Z9 Zo;>T Jї˨e.(x)&6YF/`/U-ߜ}qɑ۶wpCY!S!h -C"eu8Ǵ ތ2'"a#C.r;[4JI}51d+{ ?0_>oחQIVɟNwP`,WufL}K9TD=rLb+Š5"I)yºʱ{"CΉYe)N]>Ń@=رaXǑĈMzc[TKl3b/BrBm :d6*+ݫƷUf-wjEG6Ƿ+>pcфi ~3 @oQTraF??Dc$0տ>ȫ`DҨSN͖}u_Ҍ1Cv: BcQ'c+Fb۽0P`+nD`Vc:[>*7~R m!lRj4WĞ1G^k,hX :au7b3mS@#\jfR{$C~$ ]7iiXIY!k #P4XI6FmC [ bS/]#VnXdҧW8A]m8  IӜ'}!3R hw3f`\84`9bsu*Ԓ^6K>m(ޮ02R4+K?29`_fY <LĢ) nNNeL P?Π9=؃>c$ilMyO1Vgi䡅4{(cE4h5nvI2Q-Z$&\ad:b>c-"n7i#2c5c6u* 7l:V\+CR6~o^ޑvs7-4OloInuuy)/5Xzͯ]X7(]sux CWh0%]A.¦B ao1ε/Jm]Js/S[Xn^¶|1؜ $NQgu^f=d=:+˼s<3Ͷ xk90!do'ȰdGDYa>Dw̥l/D $׬ +ELFf 3ZUg`+ !3O5͙Z,P[b)5FPJa.gx"ҍRpk%[JQ-lۢ\=Ap'k߬6cUT(wP>'ض4ӟռT8RV,kM2V|pZ)vQ`85xFD7D9Hԓ?*Jg1XAe~}QGiBF>V*V#K_C%tܝȰDf35o:u04cqd4 s)!>NS(Hy}D;0m2Mm_#||`vNe[p݈}>sx[n{4qiZƹ'+b^㶝u2Z&P04+{q,(%dQIj~ZgXͦpVʵ\{e$[{;ueXV+^l;rm }j {&=&dBk"OljSZ? Ź&%/;ˤ92=4?B$iLovlB&ds9F"'aZ15lOu(^慹e1zqpMi~Dv),8ca%* _`E T'M 0pE"w%+LūjK1N(Ʋo]޼ 8At]0nRtHۗ qׯϯޡS[.l7,]1 k5Yg';IqZԧ3x1%{*+]s/8!#3P>T^QNR| 3ècF7Lprkg7H8k5|B$¾,VUQTGy}k:'2U)*iln4I@w4̞a{\MNL=tSq;*;DN h}bHNuTgNeDxʛK"Ec .u.?|;n$D9!) ;N2(F+2NDb0j9 SԸgwɮ k]Mm/ލ@Ī욜sd(R U;?`,|TsCOJs'$*G6LJƃadŸF 1;t{\S[ZǘpIJY*$5);u@ 6&nv˱|8R54]+'8OTట:S|;Sh Jz eޞo/F흝fg$n5IKljN瞾4]ҭz@ XXX̖ҋ&metKmjgQz_rݐ)oIb'Ō0JLʋWj0|ȴL{{[nbm3b޹gS{(NlsKU(5p0nBЩUfk;ؽ _?ϯr~ue/',crX;xt{g|wcƿxJosT27x%(A0?i!#YĞxn?*¯dJ̥Py:֜!CP@E  J F/tj%X4yaFN:~d1=9YNͶf)$ U1wvl3esjk8f[Vnq4!aޖ[MRV1մŦ$,u@I?kS3'pz^)'uK"$pT",olc9zpد`D_Rb(lSŭ;t+ToWHm5OpNUǮR0dFΣ5>~X5z&_GЋP>@2M Zr/ s48SwVI~1.UAD2$v 3_&Le/$Ik˳ J^Po}LmdI{=_IE E/<_IF]Q^M,Z6ͅ@JrG(L^ۓ?"( $k(>2.l!Ψ߉o\p>$Q}'Mw v350r?VjcH4|ycT njJJ~ޮ?omR}/f# 9NltZI$;$ X&DFpdY٩2{gGnGy ꈙ.mEUԤ)NKxca3)wN{vl GBXe"H4d suZ,Łn~:}{G5JPvsWl]aIxY։r]CF%Q|PGIHE wu8Q!S Ofu\]52)UaLA <9S8 J<#ɭ(0  xp*("5m-avڞ"f9z6^[OAI1elÃXLXqS 8z *W= $<ɰ,'wdV9s ڹCk<$d=B13j7B M x(\ⱁz=#V˰ (ߴmφ$QFxDQ)@<,^2{$4os ZW,BVo'|KA__P#^&gΑ, ಞsfYAp(>76ȍOևqqj*c1fwN/$9!ĬF٫VpVRu@"Vm0܈C_5Dj$PAj&p1dcu^ Ń^ GUVOź*2CFb_c}DD)j 5(\;"0x,+|vZ?4(IpFb:cq;L1?dvWmoX}*l&vD?Tt.)hX[jк96~՘QER(Cee+(UG N[_@eL5kL(w,f&5(Q֍,+bFHpoTVa3a7fEsU~˫k׳7oԟ8?xu')?EQޥ-zߟWE~y?Ż/ޞkkY~Y^]]||/gG?] O˟.=o5n}suqg݃;p[kDr'n*!E;ċU/5.Yɪ|̋~q+"ɸi,&"9t2b-qϣ/J5-gQW.qjoY< h&4N,/qWsJFRST/KN@MS\?M|P&nhe>u>){s9LL2)#ʄ Sj&cXa$ 0PoQ\M5F;FJk ۑ&R9YTd.!3R3COR4!v'r7XbٟCF tPiU猒h3X> b)r9Yd"4gSe90V3ytVoi*DLɦȘ 6IFį)*,tM`Qk XTDxI -&$ v ~N%E4j6l;7vR{k#/چvmG ;vmڊ;7Ӈ O5#*s3ڪUwnH]1yfLLi)h};w9ȮIV߄h;aa3;-q6sKOh4H3d]!)PNO/}d֓R!`HVqBG΂Ý](yCt?}OMB* 鞖<\G=ft뮸;y9ȢֵN%-𰠁YjH)#ACB@/mCZW(RC=^OO܏>kYG;8񠿷 (Q qla0: "\!vU]Cj g3cN;UQqKzZm.ЗJɵ&Pt%uŅϻ0O Yׄ?T:!)Y$% bme Udp6jr7rhV'I5e&Ǚ@̛_s?淭mLʆ{^Q ]Kn/v+9rh+9grOfs;rImn!ۄI"=rHۊ̑NJQWhCz0~ס"vRQ~63A!a@_m{6ܣ~t~pYQ$'tb+E$/sXLƒE:?}3b,leSӏ74fa3POhy,dX{/ u_Jšn) OBpRoǿ4/fQV'YشmI$ "Gr$,@G,}fKC/`.u/G/=Md/j7r9 bpB53$i3s&$Ǵ/,-wrCWBFO+!c"oJu\=hW3XogȅzQmF>qB0hbu>kXVH=^q/~ҞðGkXmX{5ïnMZc}iן̡c,]!mHz{tIj[Vo}(! ~҄OBp`H?_/|l%TKaK=̳#JOsir2S&k#ņGF]<E}mAT+ IlRGдߐt}@;&D.W0EL2;+Z~w\iYeK*dNM@Wئpl m֏֒ȀVlb=~H%UA`UӃ/p)cPm(%d|gPwI%}z9#<*AuWZg,&]o*blVx!KxY#$\%6bE:<$:;;`&ߝmrBih&"IjJXe0R\M+ 3P1jq#_z_X49\Y~>vZ R[`{/]BA)FF1gU5ÕAjڽ 0j:Ke@>6k!Mc Jwz r@Ho(p^ؐG @=몶ĭ}lPRhФ8ܮ03$JW!`[|ؙ'v;m$Vޔ3OӺZPV[^OUJ"G!ˎƒp¹NI+f5BIΓ~9Jbޘ/EG(?^p8;gJưAϭJ 2T*~Fx'43ג(f` Iwy"oci?npG}+(1+*6 z]7 ^STü`NG=esjI$=LB ՖKt=h|@|VQSGƟXYGw❝s c`+.O_lm ::Y`^&W=|Dl[}0[UW|tAlxϭzU ݫ&!~P餔->ٷEz5Q8={to rdӽ$\٩PŴn <)NQ'đZuB0?U3 a4si:t0RUynn&n/$!*ɬ/|lVqt,KXW)#<`6(vTUxѫSeW$ڂKgV=>h募AfIٛ FCKPeUsl YZ)h%eś^Ψ(a(Z߭fz^y]+twœh5s{/~ҶVBk#Jl.=QY7DzYjr d!L|ԛMI/])g4'I8jZl٬pνϲm#|eyқe(³ *Ɯ"N ^",+[\I ޓpX+׫oZƷߌroFOJ\ a`'K4a"w۟ G$T7C@Evq,ą»KnBx}`psxuNjys1@jyG;mCy.14׾8^dq&}KlvÊ1~_^'c~}d$XԀc֯ɬԨJn<Sk{Dξ6 ׍fUtYX* j;>n]%XǮ`$qb 6XUHnn]qC`gb,6c:C&k-h`ݓ#_G9ElWm$Zm2=/τ4^0Yd)-o.z"Ig>ucLPI`pP)ݪV3źOZf"fM%Ic`4]mްLMo۷j 9Dʽ/4Bg{9E WtiyBpG5#|sj\ZfBPObW&ᘱۄGrf[D#bO g,sþƷEN v w͢j]7Ew&pPϷŷP߉nâ^@3a?]P큪k QYJsY ~i0T>ai_M%渢 W.uaS!7¦t^$4`y8L(@<9։<1c#9J[U6m1^!?EDLD60CG8ow*Ȥ~fI |nt8[x,&#:6ȭau{(N /uRt,򸹏DyIeHA _g7r=Xʪ-(ߚDI+sUBd+40D F&DD<ҦO;jPX) pXa&">D2Z3{H(pq +PxK: (I@|^Z̽"qiqj%$E@jsG5)l,q=r m~iMVwTi a *JN~r 1;T7q|M5B g XwHBt4Fn ,]CdڨXJ;)pdbuSūw]>X#l<偾xo~kNDk%7iXhTte0U`3q@ĝ@R% `?XM Sfx=7F  _ȏ DmMFe!rp8zhY $NoHjGK٪{: +a|@ ۪99"2?h;h~MGa`SuzFK1,\[Y0 'ۓzMYMu&qL%J?МQ@QR)gGcaɚ!x֐)|JzOBX5Z$z H#X7ў|/hEo.'%q$N4,6Lnҗu08:xG @D=(ɫ\xmZ^,{2`n}w܌ln:*L7Zy.=ύ3-WHzuI:pRMa^MaRTx_xvj 4!!WkN2ښ##5ZPxB_F]lGBϨCFd\n=S*NxGÌXʹ8i8d84O"F[C߽2;ϡ08HVy|5V-JԊSE e:/l5Uhkͅ0.ۇѤ;,g4$T% 1)2|mj^5gVDʜ fLHlB H$ E[]I8n,房:~<ˈW^H9i,?v0m}619[z!תb~@{gvf;c=y?TF8<8hjx}sy׫ӷ?_#c@׭Wb!:6˨ lmä:[[z6Fϓ0[/,{ȅ^x#3fRyע 6,/?cɠں4vj&۾.p|Nj:mQ臋]~:h~'Nn{>ޜKSRRE7Wg=x}~ \" m/Yq.;Sx.-Z憴"׋. Pvz]B @z:]Dœ.m`6)ju,lp]_ߠ8НhRk8Eph;q!-\:;fv ȧZlHAQ0@-)ye Z:SlhYXd6H¥;fjrz6vlx)#g^|cvCCAj,|vLRhfJu\=4sE8e4öޅ3j4Xfk XV\l[4Oզf }szCOeVhpȩH%f -!f }hgfXIlГJe& :1~=^~D7RjP>}cj][E^-]߮7b]kW^ok ˢB#Vkw1&h 6A6!1kZSTX ɴ2"p|HED)j6bnI{֝YD S64ifҨA±-#n _E5 жFogE l}0摈6B33d! npKT _6ًƧ9ps=iNz&MbaV5a׆;j 7VہaF`uص >ڎZ!vnU>Tݵ\wmFxfDI޵U0ܐ*ϻ6c͘R=( /)Q^ x\ouU7!D!O)XzFzta`™~8U dU RTwCyGbz/ l mq?-3e=\GyZkwQ6}%0rumi}ϗIxTYDK˿(\Bԅ@ "z8]=mQC;m4[PP}4T`6U ==hJI ^ o*Q=Bѫ4rb?Fmܔڗ=jbc4c4*i%3dߘC_4e컳ÐD_4H AZ:n5!F*aLtjtt=E 9Eۉ Cr\3s;>^:lvo@qO7)CuA!nQ&Q%)ӿzM!>\^_ZڦiIe_FDZڢzh`3(!`RKr\^0eI|+1 cω㮆eeI"%tԥeɟXBKU-!1ޞA! 7޾UÔAq Ph& f['>^ī*]VAv)1CBH4k+ĝA҆S3x,u@k%wX|i(Mk{{m/F9ʢ?oaŁ3WXv R9KնUb@E$ЯρY G>Cm+RV0*ݜxϧudƞ?lIw'|{}7q|T(d !Jy_ ^H'FQ3a'IC].cNK1;|4EzD MQ]$OPѽ(x7Xi0 n.'<' @2=ՖԚfB2?2eqDqoUϸao9`XlPО@: PQAt&N:4Ze>^;(e.taWYܮbtX*H\EjT?J[)h;P{Ue]0x:cQF18c=Y =3,[&i6˖MYh/,'$[ sXS}"g#rJ+|XgXkk_xfoXON11N鍷K~r> 3~U|B(rmm3zj&X_~0:`laOe;sP%`!.VwZFQ$=Ɔ'NŒ3ޣEsC0`{e_ɐ2mi=ƃlW7CEL*lϛm2ղcT{g~&:sϫf}(ƫb$ᅂѯZyVD 41:qQGT$|.k,$.zeMuiJ,HTM̄n>7j ;̰D0(:Ӻ2k*Ρ_vǎ4V1I SwW8ԲӺyY-bᘭ;OC7dŒ;˚K\ Exqcf+@ ~f4`.@ X@kO+qdg7jr-Ou%DcY#seVǝ~ƭwmB]@Lٍ ŚYp^3&#eSa?!tlO7.Ctk15(X%ތ#0ȓ:RY2u6]kA/|j;XU;{NN=*R]ʰM'&=(1q* c- dW6⮋ёu/80Eq>߂.A_ki .s75$qKWgg?Wxl{x<.v3cFi&ΠKT|)2uUB䡚~}W PD%pӮbI 5q8WEϻ-O:GHy[^Fyb/wՙc7D3+b+۽Xqo$B_x+\W?)+#mKqKS $I`ZHbl5z..|Y_)EH_K>bpq&]9EJi`~`&lQIE3&*;{Vw=XQxT(_B6b~˸:?}3M[wTuVjS疃`7N뀒ԍy cE><^!GAO4:f;k-k:h&ՙ1#f1bۆ)ufqjcƌ1ѠoƘj1&e38llcoՌ1޻lY5cScX1cL+fqjcj'1{fag1=ӌh܌1"R5Pc3FK;m5c0ٺ_ V5c 14f!RŌ!#ftfaZju'M10-Yhc3pe1H8jo7U3ienURR5 9A{^"˵ɣx,V˵.~Hcvi`TT 9 /h> ص{9 秦< _HiVxsG47w4qgN!PL/DfdxbXݝ q6½i*b BlidHoH.o![$,5O} !H 1Cjcx3.st wr px|inK`5ON5JI4n b'Cp<ϋ8Pu76X4> dyP  ͯe^Qs7sä"t.+s%0V(o &]5î~837rkP܀5iC'd--G4HP ")VEd1l!f@q]z*I\E29c^Z^1ah%~q̾p T^X. 2q Vѵur"\rt@&9886đyw$ٜWUc/po)?2siv9Qǜfc:5P4Z[1z򱈂M0Z#] 5:tPB<3M4ek#1 u+r…l1PLM?~*TH0!1<|kՠ+ ,#rJi @dAB`8|.q4S:m"p *p{D6Y?UNcDuzS_0l| ]J(ȊNf/ЉblK8&N4Wه+HNqV>R2@][^ko@Gxb3w AVdǐɢBKӝ@tC//|a[mفSp"FHq'4BAL[׋{_f!3i t,%@=ἑ`]c+X-GynMϼ4CY7p-Gq]>x|ˏz|N`..IZP8㸹q->>TT)Ȱ9.$?8x=PVauHXB$pQojkyMgƂX{.⵩Aokxe.fWG#%a`d/IY>( lZto BǫmW}  ˲Q"YQoV1[oS[~Vؔ} EYbӸ&j* Z{⦣H ESg?kR|FUlʺ.pO Mux("v۠Z#$Ĩ@h|^-x|^oT'U}g YU# to"-E?ʲn@$ZNH+eYx',.^˵œ\4#.&JwuW+lQFC&b^J~Vt0zfO_praYBִLU,q;gV- S> cu_C}jQA^bQ>b ?_tm7ճHm։rM!%I:$1km|L6W~ ]x;k*coV,> KTyxo cB,ޑӚ4fk}S=?%:CΡOԷ\u'#ʵGpc+//0К5v﨤%b_@L! XXe#  ZH*a X"%m\_;8C?_R@At_Nd#|[H G>īU~+ Lv?]|Xxw]q<["s*`įTln!,ke uۼ^~*RA9ȹ 6Idf@{ތoATa\L}McY%-5=bZ#[|]EڽEWq}t$ '3CN`ߑ]G 'V]Ҡ~\|80?Ɉ[_wuuBɛ"( ]25[y082 CV)^e݊T8wSHO4pd:f,fswsK E#h[>Kd(q Q+ʞ$"=~GE]o 7:|Artv{_mv1< -*ROEA-6'vp].C[N⯜`U5k7q?_k|@Y; Ҏ#fVw ".#:Fuoū.\Ef®OQmג()/W{DqQ}?gHy ֔YT-`eD) CJzP9%wo7z $#-u+%,ߚlP' 3} $jmY73ZkK0StZ9; e'$KmwZ2q,+DDD%| ul(8XEP w-yGc{*# 8ޛ!̒2 Peޜ 3G0k/~:}{GwXoF  ˦!^Ăn.4E 6M+MĦ*b8yi4L,Ħ~wDlک bXu"Clwa$Sa!2 4H0 `i §VqA3ka!0 R0L0L!bi9T$3s7˺PG >>)7,' *v\eT"5z'>ITH`E/(CEIIm3)'3M͍ '#y3e.j*sNIW[^s4%"mᷦ$ʈ!L*inAg EŋߥׯÑ[G!e)Džbno iYlKkaGuƩfjg((InXk=gכ4oBO~=Iە.wH9玟XY3ǎs[a𙮗v7T,. ӧz` ?48hlFYD8vS[8/Ml9R'`C=aPbvF̄) J|6]Y|b&+4k8G/LllX7e,Y}b+h A}ݽχfڶ#̇sc0X0E)|2hV,"AGD3A @eP˝qhWjlR49fB2ɯ6`rFXMŒx 5vQuOFxBbM Ll3ǚMhDt/?htO[*]K˥N;U~Y^]]|н_|Wg:hw/.w3-A;2th!CKy݇ӫxw>O\ Oy4OzsWxsq%lB2{t k)/?^_]h7_\]|~}}#Y?D4f\1 "9xG,o7$yVtUhsHq$ΤtBՇ]k|k>K˴=O+8Z:z@'H$8X0!߹qi:* >e`ֱӍe .nZ*: T(a:l &3BAv[x˕X-yrtZV O5(qh tݺDФ&ԶiiQQeˇ߿'+:5umWc?zcVK )qX#Xg,mOW+%.^_ )/>5؛<[[zʍm^G:(6GcHC*Ehd!:/.avY+?oګCS.M庐;+Do&֊\4GW}+jI#vmCXb5GS`p +7ibjoZ]zXF 3@Yfť=$zaI-hxxTJ{}9GUݫ;(Ƈ-9viDJbqxX:9R+x0Jt||[[(;<K3)zyRvf q+ߩD9ڳ=P~޸5o z">v( ƂPG+*g%D] 0Xӛ54}K/,OT:/ٱ, ݷɶXe}i.t# \y0Ǭ^cͤl.yh]55H)`m[V`n|U.Pjt{p 8C)uwh#z]&6W#.C F26^Pv%C :+yۏ︥Qh{.GCmtdDRn8-{Kt"! ^"|5RDS!&u4t[Id#:AfaWk@:.P3HFV4A"*Џ8g"L,ٿ5]r1WrDgn--(hѫK=Hc*M=]UǾ=Sm 5(o]ܜbl#0(鄉~Nthz"0:-a*@ܭߠ*yIz^7ŃL?&Å%-f8B 7a,=G3w]ٮ]9%wqѱdI\ˌ-K|T{kt5A>\Rqi9y3Pe%@j̜ΆĿgxKn,eC_^#ar= :xw=5e_MQx[kXo뗙JIص;Ϋ\n/&{6m;7yvEuտs; 2TڙG{کI۹Dofl?GH:߼I6J-D#,2 "7'4Iib!fG)]A;ZXшƩH(F9nM:p?BTotܑ/5B՛u/UB%uV P&mBu|eۼ|}yɲ^ـfq@7g/Y77 >/zYz_Gk:/NK N/89& uAj9Ց s`@ztwdV:?6Ґt͠uw1;w['ьUuNH'M;u sOk~nL;o !6f @7~ٍ;5_]$|"$1Vn_}rUb¸|lVj-QSfi\;%t>vԧ/ECzP)Zh X8m0-c PGYcBr[xc:'^pz_'?o6nqJ~-@`Mq%LWտD~ԣPrӸoX'ot|a$uk2d瑙VFJ<7S+PigfU$7yo7fY>g_=I+is<>pMʼ֫0XUˡTuP$b]3ظ`Ծ1v~+6$Ib&(UM]UI/ǘみyxMf8lgh:<*kJ.,8m#[VBmv|Ucd0K9"@,ii P(Z|)+?lhEAʗPiyM_c<"QH^jB+bwjy-l;`tvDGYJ/=Y'޺Djv"4ʅ?`N"3a7}g>k99#X&N p 6z "KQ:)P_>1~򯘰?@EG2Z ,lIaB+X Î} a0 !g&Rqrp?W 'kC G(#TbEGz~KJI'Y/Kgk@``N)#YnGn+V>fx ].%y@,V{4*Z*=֜uuhR Є4_0\'v 7$i F@-\߷vmj}`a J E:Y8{,vNY| $wˢJBelz*L::߁vEY\aL_xە;KM|wDZZ/Қ/_PUIduL^C<PWCT)7Ԯδj"X>MGq1Kt~W[Dмe PN>Jc.mOKR}Pppkx/mȆFW|;O|_]M|_Y`ѧ_,Y9/Fhngn^sv-/Uל/m%16%8WbفHEi\0JR]05i&WRB(1WtUg[}Z@/*?}"?Jシ J9\eys<4A5\vgwTW,3\4+cU ,+x 5Zϸ -sj nUf!5-t' Tri|O6B?Kjn7U)EfgF> ·DCR~"u+,sxpH̃*Rj9w"wM)rZ3TAc7qŚ  hH~܅$)GQ[}T^KT{>m^e^Iof %V. ƺ?wp` sKHB&i~ѕ6Kϋgґ{NAJn,tI'|v/ģiڐ(n(^&H(pD&nY<1j܉OE/Y7ˇ3d"aLq? $7:" ;p 79fiټLI ꙝ$ Xo0~x qAIôaD2 ؂<=O0@(E0C[\dfpc8qm- Z{z/ڽy,O|hA"GbCHeΠi$nv,!F,Gèc08px$ngHϐcؒ\b2[Iznt`a b2kBYpǵ+8;vq#YL#mCEGf[iv ׼V")x y^kӤ:AcVPWRV1BbB^Ӡ::I"-7RV4QU紑T結SgqR4Eoi3z:F8i3>ҨԭV$ޚD BӦ9 99Isn+V1][μbU9,h?0¾ϱ$0iRAOjx_44@G`V!C̨{F{ZklUȦrm,b.z>j 4k7&FŞLj]}Sb pi&{B9'be$OYdPY P8HOYzX!W6]wa27(}Y1Uk(D?&Nf @O=Էl1/5c^J"8u0x|j,S̤W.g|vK-=ppzTޅvpG@w86rG7̧BC*(;ĵ,aPZ9=YgwL<>>{Lsi|O}9tsi?|DݠD+= Usn 2, EvзWRaStm֠4 .a]+bNQ ]KC@n/Jv3(FܟC})ԁk)#Gv tp*O-׋5:QWZ  ?h7gg ` h( {6\ C/,̅f!-+Sc.Ğb.h#]V#ef|eָt?8!rISj_o?fUxJі7OYʌ!aoոӷ?m+@f}CҗiIⱎcJm9,~:}{G;v>^_btHP@Fp=J,ElC̥A3J0,Ϊ4͢nM(%bp BI,s(?U6Sr"fBb+!e,Z1ͤ,Z!DYL؝C˅9Lng^Ds(%w dap5me)GI}h[v4堉y$Xbi$1|jAh`y-&.4oPRU",ƺQ\MpL,mӋ$&¹ R+%~X2K K0g␖LuCig-`=ZRv46ޒ̛ V)vok\{gva&3^l;6L .R(XzbM흃q&\xpr LV;JeAvw#@jgQ&;,V~Cvw6Ôr3mq08QxbnfGaMK:ҐF2sM"3 7ͳiD6:Rs,d!xz3EIR/5)$ǿ\er ,nsHe E gxXx{x6{`dᴋceAE==~&olnYe59rz[E ) 4Tb%EcUB+Q7q]w|`_ TD^ R$B`ܨQvPa!6K}w*Bls(G lxy &9FJ\\?`wtOw?t-wt;x?zw0ӟݗw=3JwB69!ӫN޵N~A' 1BqBQԪbK);!F(e'a 1B:!&g8!zfo 1B:!FH{fZ 1J8!h' 1FZ Gh{NZ NFA\q'q  Nk #;aRu'=N;L=!wB4Ý0ҩΝ0J>a E@0`W;aaSz*F lxs #ߪWIiҦ>.֟z{aa6=[8fڲ@﷢ȌtO]}kE/86s^]d=I^56& Zy Z@zaEt ~/}Җ>iW^!ɧ}b#",~sۯ-j}_䵤9^h3~c]i' NN*vIg{u'.̙ꎈV2r*Z|葴=znu N9Pv(9 A-c2%[.+gAilA^ ~tH2sa)%hxf4JxzWd~WpCfzC>BΣ7@Z+Aྦ9n3>LQ\l(L J~y,}?H|5ܦ8p۶D$KZǷDbsl'}jOk֌AxxBgIblͯ͢>bf͈eZ w]T틉o/%>NɜO,΃IliA >> U{,M`"k>~ɷ~ImVĭk:7<0?s!ie si$&h9yoK>r!>вi(N|?x!{FKr[rV>o$qJ4&K0.!I! pJ/4]z ;K(00}vDweᲰp`aFn:}},Wp| r|7Czꩆz4 c'ց;}]4eZBAeu8aVGt5;;r|ǞrxBQScK\'p >:baoNc!,]IxiCuW?DӖJcGAj‚&lsbF/8u[cbJߑ"Og{mLb3 \ɏ%}ww]<`53nǴiI1]sL>,frvSdljB1r²; lx7ڨ7gΠQMȬs%q,Yv+<<\BDԒ_w1-Cp2kt `[zƂ`h$bz=s^:#+b@Rkd4Bqֳʚ^yʖlUi R~]*c Օ3u{ n$Θ!x3$sG udNJAf%~Ax_Q jyw[X{瘏b^s5)cK"?{trP]070voEC:A_,lqoFjDf(/`ajX.GA+|A]}꽳ɜ4Bc/¢$- ԹkD^?ǫynj !z 4)R=զ,!(qQu3jy$f[/rLbiAHҧXq(pbAL^chaڜ%Gw'U y(;A.y&᪢9h74t,f򃲜}3ϊPGif sAep,U>́w卑;n8]J!u@`D7ċ&(xU 4;"E6 yl:k/y0;Dvɝ q&lKQ'  y~lQtˌw&b;y  P3iΥ42 bA3C1>?!aji!bbH(\2{~kU<`w"rBiF[OA^h,qyuFә yE3<7mzҟn8}2=.qA)'zK@$KCUĨw!Eun:v Lp@s ; b>nXr Gn"Jg\Ҳ-wbiMb& JH4$l=>U OɜX&dFmVIlK]gN6Y~C?05-ٽx8 uKcCq(,XvnT&?\\],Ϯ.>hn˙pR_xִP?kZӚ}FmfԴ߯:Ŏ%'Fɟ_rb?KNKNHuE1ڷh:Aa@,z@ r6܎rSVKQ153RD+|Ϡ /0)%Oo?~wݫi Wvɣy:$[ Y{}Nxn-~9PO WTׂ04JXKwo%!J~UOD_XIgѼ`&v-< :FU+GGA=mtjZAGItTcXAǨVQj t :֭l#}+j#\V¾tX g[Ah+ :FDFh{VZTbXn+tz͡NӫoOr ZAGH+jXAaꁺʻtd:+HI_ :ֳo펔?һlt<)۳ #J]&)i,D4"BliL2t\ tD4^B&WTWZ~fVG'#$Q5TrNUIU:L#ŧzq_St,To[ΡuP^ ˞عt2;Vvإܴ9rӇ:d۾۹0DjV Dv=Aj#ֿ5zl;؏Rz`~O~=$7"ߌM[]VvnB]>̈́$й _ca\`32j 8=߼ u`1ёEi5i1 Z@R]((Wŗ1 'QxWXXDie|Ӳ>`{N"/ԓѤő 8pq$ݜ}Y7UQߌP^71#;7;)^o+' OX;hGek??r,{,C3v'1QG|l$^k|^6G?q!iQIƧA ((y.,yr=? ( M\pW(Z[&8XTr@=l_΂K]0C૕vh":RitY:r5*'Q(MXw(ږ9(xW˶P%ORCI QF*웱;y(DVad\"1f p)4n2Ku+r…,#cC5-,ʼW.Jo &˲d$Yv3a,#}}ϐkXzwAsW{gؾVc 0s)RV+Ti^'bB $R+V?ߎN9%+ q aB;IQ0V<..ذȡ@ 85dEnǩ kȅ#u֎ yd*{d/N,HEdm9 >~js*AsC(pt0HGsᒙV/*@{_۵(2b"E43ҭ g<($?O: 5+NON3ި}w:0ONb&[AJ>Zfk:V^oEgȖ.7ʷijM\vVv(We1sg"3enx,Ԇʨ}*.sItAӌKkTwq\Tc3"7!gI )Qm_> PmѬePxr(Jr"p0vQ ԪrR646FCK`Ic'G2<l}^/Y޸/⫠B86Ԓ!ZĎ2W` .=:bYMY`Et|\b ffj?_s|{N*_!gu-;ĥkn8~s_@ p8t,tm'sm6!7]ǎ׎=;MN):VenlE}v0 Q'I曹tt=՛ e!08%`V_o'dnWZiT 7-^շm߾u*%)HD3Iګ`_xdeBDG7ڠ>D+}PG8X*Xg+Y$)?0m'X#|u> *yGLߜ^p-fMQ+vwouGb̓ lYJrBQ#~?UӗHM}/s ғ$M&s s0>"RdxB|O}>V; r77W1'uF \V#|)>uWgŠÍ oq-cJ@.?L ,YHc cR>T%Tŷ] 7R'Ѥ kp8ru,jQC#߃s_ V>Kf5.C[ntȶ0܉i=-m?O46&_, >+x2lgJͰ mјq0c"5  "K@d@-1<5oІ̰$bğOa KX)_@*$2"^IPb3{DI/?~5` &ХY!Pvm,~&+ ş05˼Ѫ8jF )?J?qnGʅ霸ǍT3^qKaa6K1$K`،M\?s;܌}}U"Tƍ }hyM]wA<E"M ޕ57n+ !,:Ky<ei{"3#.Q)@p9Vݛ,2&t߷^7]x0i5;]Ud6ג0Sb'H JTxp}r)J~qGE|v֑8e~ĄCFJӥULr{gp U(, ;̋{K$S&)ޔh۔\f^|W| 2JcCF?&;\v[g].^7O8;qhiU%\eṖi ٤s!&~X]β³(>r )̘THۥ+`s}_;^,u(F=6n+3 2(0 x:xT~V:ʷ]Lm|u3wgyBqBrB|^\p˳h`oD~'6pFqGuH2NbCܑܒ^S0aiG>|O9^~zy]$Q.z0AZ,և?"`b=̉{1:7Sǥu{mQ@r>sC//m~Űr1T}Ahrk>|,5 c72q\qFH cZl[ʈhjx"'*yS8H XDZ{KܗIaoCKKӢ􃲴e5Y:"WVXN-"ˋZi)xZ\#*HKu}jqH-ޘJIZ\#ا:A5"FG-ŵNV׈Ju"ѰFG-e&Z|X~L9H-Djq@-5jqUIV|+PRgڢ:jqZ|@-$g@؍={А}t⎈"۱L3u@h_I\$|FyxCgreŞ[Ӿ216nv,z.@8 uqN/ BtE'WNn=Sg g9q$A=$ҥKu`?*ȯd{Փuim[yiY`]Nگa[ pxN~oiec }0qHv{>{e45&&uёefNv!t/K7iиS0p4 ;1:F|OEo%d%ttBx]YBUPYoK(h!6m/gTayqzZMrךڟ1M%`}}vv B}O3XCzqY1xX_^R]ZQ{RIt_dW>%:.5VAlbcZ[5)n#}b0m(,dbE2Bʚ. kaĠ1`=.6, NZYZC`^/QzAMlo%_X9.peέ׳黋 )ޜ9CuLӲ/Dw?қz7%<-zMuA; i)$oS̗ 5@+r4ސr|hYS.'؀QAǦo~ӷh oh>U@CV:!kAlV(=ߍ9]_&/uvK-0hRY=2%0%p0M*x!L(}ۃ %wBحtح~6 >ʘi8&E (@!1'Y8hi`w(<_p8 dJâ6ڱ:?9/_͑(\pߜ~{.\|Ż7uև/7(/"xQ%_񓐉0;ɟ]9Og?Om~\OP޿/WdEN/|AB ~@"~vpӷ零c$K{y$K//qiA\ .$K#- IbVxc*qi$ .`?K#q1PA\V7W 8 &ND۵ A\qd .&C:-4,qu < 'A\A\y8kN/_Eĥ傸&>1$@&)qiZqMܰGEl46qM\3)tA\'$(+k+sA\~4 .\ϗ=/K4B> cԡ`d 1H9#iY ^P( ZP}1$ZCrM1(Ga9lG !`H&_ލCb  *CBPCB:$G!)Jʤ})?`0GVL?@s̚]Q:b\֕Y#+mcT p5FUh]Q1f hQ1 51F~W!tȸZ*\/MꟌC:a:l*|QhUj`TGU ;jL|;UE,8dlՆ#1{bJtO&-Yjzf %W9峕6VRʂNȊ{UII0_*~vڳ*_<eJc ^k=9 z9S:Ă|AdURawneMqn caӖ՚K4'v,x)B&3QAgNY*-ZVL\\zcY\W$Ky(e*5ușYC-%CJȲ F~0F<[!* w4Svەn>jYϓV=Tq7@NNH5*Sf_R&ue~*ijQ +7eA"ߑ#FZM'ELڲXy>jLQOg Q|³ %s,/S({0? "U_l.㣃]h3 3֞incSWoĽdWmyqgY(qZw/8 L[V)o콕7yj'c?EóT@PCs!2LzA𕘮.I5mkH"i)]n]݀#ᄆ*~X:ҵXD^yST3RȀ AcSh3ZyZҭ٭ ]XW)3¶ڍ:rk=Zb\ ڬE̫c ,ЧsM!.87W^?wSdLmL$0!ɲ"j\>4 AwH*Bcyd-rR>ѱB"y؋c 3 YIN|1Bث)SiI>ḚOi y~0gw`My(f-/laŽϢ8u0d&Ż5NƧ*Aq26F`{q3;-s;y$<70e@]Rahq?.v1_T|y yKc] A^eꆐD }.b5;dlP52']#d,EV$"ifZ5c= NblVDny9B7&`"Z#ٶX P/nAR\)Fw_USGoʱǟq^ji G$UmK>SjMȺ 0p>8UR"%&Ɵɋ ;Pc뢴.JM"-kl[# P@$ɺEAh?r 6V?mm9ɎG6$&Ԁ1-DD]1Gp3d[{R{&VSmwd҄7'B[-ZT(Mvz<׏U8bHȜ`=M~Pz=m_#e<4+P@uL  >@-ZۿFlKmZKj|&.ǹx=v>d_6c3r@X0>Q΋^g L `ZZW`["ې$gp" !/d ; L[kik5 ߳<Ɋ2YEN$qL/|/'Q~,˕,-2/l1x0\t6sd#[ Ǐw5Rfo'1@Ign:ydк]ZV~FkʫBO},mI>_2g?UmA_LHf޽ I0|$Y_2&+tDzX[Ȕ_{Sa-;DM1xJ+0a?:XUww8v}Ͽb6;UJ븦P}mSd4lw-a~PWJ?w ^ԍ>KZͩIΥrƺ;L f4;Yg6U!a9բZa &ScIPjQsgg?ϭ77s uv6f@G'}GC"瑩Owմ?V[5I$?fD&=|иޙF?'x-O77{96t i d҅dt}e;'6רT[Kܚ$-T Ѱ$q<@ܠ( q,䡉h)82cAi}q^yy2Ht,9BVk`= yб#W} xű㧌292%aDGLnUM()Oa/;H2E[D!Hmn$vsmݫ=&NRD:'btos\5L)T: 6)%"ѳ@@䛀 %A "7qI GZI@=&gus ]_t>u"_tF_(r%"!#*L>犒LxFd/M|~%OǦ0&W?\dԄF"OQL4r}=g0gܴ!-X-Arvx- dװ y : +C ZݹPjK'ZJ[ X{؍J"`U %E؊aL kN`K'`pTc@E1~stơfLK~m(  {#RjdccU{h]sYҘك"q܉O?rrmGK-e_*C^`ATۢ'tGlm #4Lerh'^oɍOB$s๽e-ܫBn.Չh%ӝ𩮓2W&˞sv^TP0."hIi^;)pKH(bz&#0aj@Ү/cr4WL>$ZV<~/v^$ i܈ItMv&Kw]vr$'~:\v s$z#kw4/Jq؍O^:.. ?:r|'/$0 ٭=rV!ܖȋk7/Ek7sav*BwW6d(z,D nZmNڞXF$!@972$%.ɮ:.%qC sk˜j1iH@SOX9UŢHoe4)#BF@/. LV6d։}5 u؀\|gܞ_rnic;6,.r;Oxii;fi8~i#~%-n ƫSj5woq &oȩrrb!N`P1e~ G9@Ho2k"Qa،i&(q&RxlIEj0ĢM"`j몇]Y;AT[_&an h ? >M qR,%L,_`^IK2 f({$ 0BSWidi\=ɧҥREN>[ z\d|Lhir䕩/DkѐIץ:HF,j(,$(2b)3˘gUYJR%ޞ"syUkLP/Qm?#k{9"qa4dӻ(.GL3PͰ a(|j0D𚇈|W #Kj">Q rpGl˞p}NcCvDžNSzKeҠƘ=*h3*eJl id8ȇ%{Y 34P%4ߛ:ٌF#묐3Y|Bܴ$wPHV΁jO'Hmj^Wg~\hWۓ\k_]ߟuz&FŰ$)aGW{UI~T Y=7^9>~bof[gdj+AA-@};ӎJI7홖Zۼ>0Lu~#Gxm="Zv#B[W_GZJ͈?d&l@Fp>^ऴq5apL--@5qVXe C٩fZnrZ=߂ #%m+;6\7 w;;a ;YDeǼ|^`kmr2Gr8l0^Q 8IIW2R^Op{<-h#},Ɣ}8$;0= m(g!6"yW$s5HX%a{*?!wO'38.r2?Z$"8QP z,ْٙ6$gukt8z7{8ze_pN '|;#l8zA[4O5L]6>xסK儶'^ KEFFx軋'INBغp1qe$HP\|Vh"sC#A28teLM5&i;cܽ(K cpMfe<t%~>Fbbe$l&Wm=[iŎDIBf4 Rb-Qٺh #J<ò/2}F,֘tkmv c :7MzVKi͊E-Ys1' !+9G-(8Y}t *XbWT1^:'Ϥq;*p[=a|$k ];vyHrbd˥*VfR>Ž FhC7ȶ?`@2uzGXŠ\t.gKf<BA0PiT@Mo,U}GhwF,C bɱRW>vPiھppP1+c|!q*!}ދʴ%,])sMNmՑK[=)454LƓdqhl+p \DF MгZFD]㕚\Ghm1=v؎z[A~ YR,mW|EG'iAv~8`Fw첑f baf?*0 T6i7t 68jK56m)؂$uXyrs0:k~S.aV,Shq+"rHY]*` H+rfdzW] ,n|<ōIBr~\sI}@!sLGWqV~ˮd@A;")E߷@_ ݑOŇw7glѯB `$IG4#dQ >MgSq#oEdž%F g45fiB;"4S?1tEvt ˪_KTLb$Jr^ M8"ЭPY3mWg6]g?>_x]܄Ď"yf5.2[ʨ-QdӾ:!ݸX>hz7ݶkc7SS>/|2eRWŨV< %XyJvDq=`%vc*+Ҧ(ȊL'C*ac  a(NƷXpj.iZ1ȬHh:BKN ?\yd^?Mk29 Hʚᬭ : 1Trm|jՏNrqǴS7qz먤kàY%CUZbKOױc=YH (1Py_(eb9ZkhRw[]hFl!;북e71 'd5*zquA\pظ;-9Ʌ=EQ(fnH4E8HAvِwʺs돊7=#>!欄Gho Eu"/'DXQɺ0 0Y|Tfӑl볥nj'UEB(Er0Yi3hs_W8Ay'B\Wgu\ofںfۂQt\Gfpj<ݴ} i=+>L;F;-dK*|-I.I2૒#g?KtxVS9iZ]8,"(rL?>BeuWfgĎmkHӹ38'r = Lnn*fGn<}ubo&&{my ބP mGtWj-.tn%3|(0{1?Ut9W :@YX2q=ʣpPv.v+1 (W4N}Tl=vi=>[*H'dOCs2O6ܞ%73. ːi=!fiKY7'!#YHRwͨ'*?5}SͻOpxUNd`N4#6R$u6קYDG'dQf* isidQÉ25,T( 2w Y;<kj!Ic&[%\i ֿ/CGʡW)Dӏp<=e#$Iب[ ~='], g 1b;$y.n; 8wc!.R{7=ڙkp6G3Q$ mUͯ5myi,K b݌| WD94OhOhH. (7dN:~T&<%Z|hT(ޫ*r ~~~cة#P og+U?UCnd #k!*"7_pZoWC߾aևw,VF/ &Mv-`ۅMܕrUߢvlC弑F0-t˵4vayyA$CL𐼅?UeEhy1b[,=xPw #v[588U <3%J!k9V%aMڌ$4M״Ͱ1 hL qeMDyn嫓Hfx>Ee,w6|31uO7ehzL=a .Qw $a9*VjUK kS@n]N*gENt]KtrmTvŒbG7[Hq{ddk|֥XIډ^'߱+r݇򊏻4:bdgHEc@ n߿r^}U&]c fx / eEK0lELn)[32K=9*4LDsU>RAgYOH,g_詃V1aDVRn#丹? O O]g`P"kdgzc\iWUdJw5~t I(@H\pp^)B 2?>,m)-Fvuv|HHP gFH`3=iݲ=gD. /V.f# @&mqScԹ[Eƚ{FV2T# cOؿgxQ̜H$f[Ia>=@f05LO<̷L?L7'@ 7eTP27Iiq 7ЌUYVTm0Y0|8 7/ک:(yx3ӣJ{Q$sSynfD=VhA@5 \0s]jGQV.`i:ыjAmbg\@D߁-SwMnEhcZ.e$ޅe%uR MU387pQ$664;[%;vo2V4Vun,H\}2D[`ӮqJrj!DOJ,ou!3CV[GW=,NjoL ل{3;+87_8La(GFR!$^-0R[QRy' TaCd:+a/ V [!૫ ҇bBX5GDqI @|z1!v&~;毲a9ȠjMiR,v%@os [V%HUi<5 %QDܺ@׊bۥTa0t{PǦLU&*U#[}|0Q@uB 1هw A͊g|S-1z P-&%Rt<Pz}BqsRuDjT FW{׫/wWSoznz?^]o_[n<px*QsO.x0/cneH B ?&,7lΘʗ`\J?yY N3Ej&*Tͤ!LZlJ6eN>v$oh$(ތL DK+FFH %y\HBD[~G!ٻfOޭ9|SQ$J]mRPv:<0L$YlJ=s%4iWg'<"&]I[edM c"] p768TT^Wj_x,X dwMC ͺ*{{Dyrhˏ~au@_;?z| :C ~S׊$ ڡ.k!dkpdvP׼(AT>b{~ftBk6O5/f(ʺ[CTg.Y|=vV[ft+oˬN(HgFC O8bx oUg0״P͸D-M ߯W3i6GzuBeSU e;PA[p'_O0B{KhmTΞ葾<̗V\+:OzZVn$һ@hSTKST@6̣ynǞ-V/ٹ*5@)ۤsf>XR}=jC.pQoxx61sF_/~0?@oWq4foU_&E8לE/S,~Csyut'Z?c|(9O9EObJ0'l2'~[m*5K?%+' }S`?W:cl/HCqMƒpdCPq|O9 oՉKA~#xyE0| 9v"z֑Gە SKGZIݫ~N *Rpz*y([IהFQyV#} 0Z ގrw{bzA}9g_I$ 2g3>ƳєS,v" \%s65OZ,`WzД~ŽQyP{.a 1%4m/߬-㴤$G$L+u!g`΢d-S`+גU5[^u}*>#i%a G'0A--a^zw2?ÚɲfܧLL?x3dž=R?$"~:^'-D>Zӗ~p;olB1VS]g>62N@m𬬠@~cngCZkU )v:ޟE.&mp,C f᥏'\G:`4({xgݒcg&v03ܦ`g4Ẕ@pjp.gD9_(d](`C'PA}f#٢kC[+j兹''.,2wt\ŸGR+M BȒDx>nW=,",9 ";@\oPG-7U-A[_DP*!Mէ KBlX$J {Nb#'vr5/EhS,XE[x 2QΑ׷*V.^mfk#Pp:!^P Ҫ"آ~|<ŁB]|27[\sq8qF" &I3$hx9Wx{P5GҚGl8AGoakvi%Q_ndGjʰ`b ".1[aз 7(pݦzxHG!"Ȣ#&@4ۂNZDoZ+O@)8д~n%Qhځ#&ga6 /|S')ܷͬZ*' 3A M3jZz @k1$MdUrrDF>Oy[^g&z'rBVYCM9\Py!aF[5d &"C'5(V(.a3IX08&SGa60/T,0+)ggpE6XMj1T%.7؋,UD~f!>Mٛ6!RC!.XVȣU8#י" {Q{]KW1 1bJB/ yv9 OVzA@'kHc4=~/8XEZpU(-MK8=~P]k(osLM굿Vy :鎪`-őJ6n]9!x5`u{BXlToGr y,(6YOWM@0"qh506G6_3K)çaYQZHvwEl0+T>!-Ef,Ol-8zbC?̂j ac#w6/o`afzcU ?MZ$d0K9#^B4pj9nOĩ-;o aэKLo~g`{a ;>WIJr@̶]̉ڭD3]dEI}'H5a'\XY@L¶HS, E&x#}f ܩxe;A乑k'8;́O5z,< (m*&KL2aUg0,r?2zym8U컊b[SMnTٹeNM-1 ) rٞ6lS}tM*RbؐՉB?8^]/C٬hԋÓL퀂EڤubYM|8LfĢʒ遃.v.(Hr=;afM,[qȄa9 AҌspRF4D5t$gD|4_Uq7HmGV^5m#kMl-G6@>RC%O?FS7\Qb#Muju۔V[f+Tk jy4,` ]e"(/E66 zv0s~rة$x&ߎc>ɏf!f@,$D̂6 X**v-37XxVv~(`T5a⚤A r~j 84ykz* "A\AH,$ <yQmsXgxb 9Kh^V禮mEi&y&URY? ikঅG8ȒHi@;N6] ];lj±&<^ `EvaD'VYD׉ FFȳ(iz6oxBgsXVR1[aXpvq4(LPPs7/9 \i8Z$fFo0w5;(aoG길@ Z㣨#2A5׼aO ;87b#&v,UOݜg#ʻ7iTܾqVp{yu}IvR]Bq;*_;c"RA& + "-AF]ǎ+x\Q!jeQ,ϪWL8o'_ M(;wC8PTmܿ}w{wE4rJuMmд; ,$Ubá}Nq's,H4pezvx ::pZ AQQC^v$&h;;lg<"韑Yl5IiwfPlG̣IfaTr(8<'Z:^!yL>Rb$Ew8UcPd[pi^= PvGYޟ[_?MW/oonk07k~nt{K98n6]]tՐ75otQlj?+ $ qgW(S [/_;rq^Ѷw*;!Nonތ?Sc?ݾ狻?^tnvܽ_K2r}۩#AӼzw}_W ׿^]߽N/~LAZB!t36` o2Jw2[e ` ` n3J7p)\vHv>Vg ԙzLA=M0:DKl17zD5 p; SPSLA=M~OlSJ0O☚DaëamZ?&{zD?}M<#+^gQ W?XBbAuF"t}>-g!9;a9; |**wCBO Y`:$$wCr|9$w>##*3v=U5i 'f&=i.Jǵ󑭸lETӏ>0jQ }T#nn&GCG5QMV/ zǽm(V?MhuI~C㗴VIӆ|Tkg3hTG42kQG6hwx޽po !AwpFQv6@td)~/cwq4/ƛ̢G DFx53>ϲXb]F|=QEMRht|X@AwКȋbI.=pwH {ViP|xzhM% 3Fdv-2jﺹOl@~Rݒ.h)ޭݶŢylvvі Z0G"yϧc̬bLGlIL}ןqɬ3=]n\22nQlVTDkުsipK$+j>nmx /W.VL$(S_#G &AkmNX+K 흅ʛ:c,$b`9?s-T%AstOwO?˅?iD;Kaz|RPG w` !(&_P.;(\ ^"Z(2BFڕr5VhЧb[fQF\MJE<" Y\gSTVˡmhT:RW!y/U~(F2)!%@uelP[ [xEW2]OǏ-շ:@n8(!奄S]PX"2GbVn2u}΁>6 $ڥUG $nԠgV,jA<+MmQ0 ΨG^nj2s}?ʴy3}nj`mڗtCm9$KK4])*щI:7JVJhI%1MeC$]* V[8KBkpg5ZU,i֋٧[Ẅ UK\? eʺ_P.jwujH òO@}*0'u#,q'%@a/0z2^2?$lq'GDYl=屍"oK`bSB5UjKWGfoń}[Jso1-Qj?|\k5/cSk<PdSB3@WS!]u3~-?yHQ zk[M^Rs2c]7ʔy1.F-ZP5{6tWr 8piկlQU}T8U ʟcfop ?$p `@3SO~ iQoճ("*0eƅQudTGa8^qf*ֽP¶j%d_|x61/zŇ=ڼuGË?'J 3ue*ƨΥMC|OGgyL1,"Jb_"GEHcO7k&Q*Ү V\2њ)Oe幦EldyA0VW)RYglg,XI^Ŋx5Th90&ƽCu6oc5ٚLAQKM|DXq}{i{Oihk ؤbU$;Mi&cj xa vt VLΓW8f΢\f GhK\@?iVϘ}Ͱ+&M଼*8~"kn=bVX@w^;L?7/ApNxn`ϰ<שj٧{i4]L̞n:l[F=.oXnLcn?<ܥJW~b?0(/ ^^OF=06M>qyN3U`R٩ii|љ>4) g:l {`40}̕/L&J}D4IUՄx1K/HT4t8'5x s5~ oN~pbfLy's QYv*VxyN(깖Nϼ K[.$ZvWJh} s_(MU 56uTOM#aӘ&cPl 5-fs-.Oӱg#z% 5*Pޚ5:Tw'w/Bɮ j1DŽ.xW ()Dy0h,=^.GI8R ah1Q:dZ-Dy.r a`QF R~\ShbZdG[8G%R"%;>@/8pNLvЧi"L͙۶2֙1f'>b&7do}=<]+48#KAf{8 nX q87Nov㔒ֻqJ׹qZE!VW)M~Ta\cǍSZ7N$8=!e^U^XCrT>̻ ԋb9x$y(#A-BeÌT&~eGIsQ~l.aa?f‹bΕ3t; g\4Td'[ݶ?$P|#tS3=]YfnŨ#v+c)ZFCX4i4c_n+\gD?T\ sJ mL.*MUund9*6^dzBav7C̼ ;gh1"`^nb\o.2],i 35yyIfzĖHB`]|p$aal5ᒄ3oe50tOd^uCd#Vk`4QŠa][dˀ d)v(J+EQC,SF}]bfje^ )!}z}5Z5,n?bc-Ex#ǚdhsVhC_<fFFmCtav?w`mݸC4T.4MrIiFGyG $PrW0]3S܋hZ l娣LގۑQF(frF!G8g;p|~ ;abar14y7K|1-x}ws-bS_ߧgt<\pN& ZΣzBy,̧y J*j=y259y5a )hޠ+mz|ґn4ou~ƔbnyhPYsz25f 玾tsێd/-m`7O#K)*int ?ЈnZ'1 \4jxsѝVRԽWNRtt>$C" X0/`gY{ʼn̰{NexvKKN?1Iض'\$ jS*rȩ"g3R7&ݍ7vS{5osSG#Zb:Gw Hie-QɭTGstXnd \ŒYN|=XUXive,PDMd$rSsZy,' E gfc\4d/Kvj';ѣ' #ޏ >gjfoxa퉠M z6lz:Rԋ5DF)L/sW R;%TyM4bN`u)B`,{)/OWݓ2*yUL VyJluOd*š#\0ȺskkwB yK0f{G16.7=+n9:ɫIhx3VZ FІ9ߡ>C˸2_UMʭ߉'Dw5I~=F`Fp Crǝ _,p&\ ncbe) |-s{Vn9Y9*r2|LZL\y#bqM3godWEK뎂ȝbv4~CeN*i9B Ofe¢d.a(ujdž&n$G&6"h0p,~R{t7 W5<|.1zˀ? W}$cCł4,4`K\Z 8{2YM,֍oSnܣOjڦ発N){R ]6Di $lQ^3?78|Hy /dQKIz"K77^7eSuj,71F3\|$& 1gF<+p@"b6,QeJh}ڎ*Ь=r)<+?1H, |(O4c/,3]q6O3DxqD.Ǹ !5ˇVal{(+a}@8 d?cQ%n9l6݊g7$3lx?psWD)/x7ů'ꪰPǟ?q\}7|;~_ 5t2'FEm%77qj}?y;ӻt֝&U5On+ucUo_ s5p6pivSlol2Mn*y4n2鸴Ot:|/*<ū%ږ hc\\>b >Ad>Q"э`ֵ?Ew_-L^ 6)tg`u&َZ-JⰂZ؆!L0iJZXGҰYR^FRGFj]mHS61 H4 J-R zMdE VpOi\o>%j zxhąI;R4הзMRcBtnGcwK|8В$[ѩ'cZU7 @ôʙ!L=z%)h~ ׾`2HJ&:)Gmc da H&!ȘD :m(ld*JMD(O.4(kBD$O;MT\TQu:p 5E5r\=TFmSo] ܮꢺ|PDm>T,bBafP>jA}XE u q"L>ӵ+t$A~P)Ӯ3g%Qedv5e|P-Ti͇Aer [b*A%RUb2'E\7eVdE{UiFA/RK.$o<&Aj0$mÛdlˠN{W䶍Wpk? iz79Wys9V]]M+ۉǓ_h$9V%e l @σ%ĭ _ i}٫{0^UJy [w8RqX<|1X6 VpEdV pƽU2kWQF{E5[oNR_;*Nx?7\iJNK")j F(&|,vCվlA-NI[o)tZ?&g{p]˼6W#N췅b8 ]&[qm:M# 7}OkI蜯bKR~!^u ;eKrCu fg WYd*nZOO~vPqGr}Mo6\zM#[[mVm2_ܺf%Au%nmu@L# /!A8>41cx5ٴ%![U[{8Z誇]H9r>lmJvtV*x#< `Rg?]Ss[^O֤>tv'y\C9 ",,!5GetzvU4ORVᮁ ŕPpzob.KSLɡ$eeV]wZA+gFbI M!6I:3 H^n_3xXT.+ה{0Xsj?K,ڳM]:Fj uּuM>Zݝ?}$ss2`28ZEc6kvߋw-}qWo{y7ædhdwUYi]i.ߞig+vHIdרa#T$ώBBw%Uĥma'6 տSUaWצ]n`E޾NJ ˹6^q_u} mVSϯ$+$FwAнkm>4D>R!z^|X[ hsCar`U> 7`|fd6k)</h)EVplPYt@]&LJdST ;*{ |N `QzC;-eĽ|81` U|?u͌Ö:O>RX JZmY~>.5Vm8T=aRԌUapuSyu Z C7J?={vf󕃐<)}VB/jI0nc3I?[NhXsFU1̡L"Ke˒sp3^o@,Z >>Qȫ!)@82HW.+ or.Oܘ0)Z oN$nr8\`ڗy!buؤy 8ʶxdV;IƹA3loD|w GGS}tp)-ߊs4Q_nf&r|t tȜä뙱>8>@2׵e֫g@PIϬoY"/`{qEa.\r@[fJm6@$&so$$NI8i*'ݭKADXfulGFnMȋ,6>5N~9/6ڭh^^z[gU1W秛9^ŷl` #=lLSkP 2fڎ,#goF/ {a^5#mfdJ\~Ijj%~t Ӗ5{lr Y/ǾnVpj]"t)K6a}dg +=J/M&(>6&b'`+8__ex,{!wqxqA3w~/X_7`lW]`E5{_6]@= c~;=N鞜ͪT3}s\<(WwwuAĿ6 ̹} ~WN>ҝ@d`sX-8E[M t)WkEڠQX)ִ5[:{j] װ!aOc#a+Q& s6OX&n|fq^0KK v'X\ByIٸQ-B8tىm bW`+)''sė}2}yߕN^4|WiW 9#R BpJ('| ӿ?f f]J#E/O H*$[?aKaFTZp n 7b&.WQ|<nv\m&   ʛlaH-E}QjX (+S7|Kls16W|YN^Y_=^Y_<ʍ0U^9W+Gr0^9Wr+GʯM#x#xW^{?;vKч;7eeP)vZ\^K|" [seG*I9ۦ0<9SO3uS(I3ZD15z5sl&b6a4 sO"6ɞxy7{r*a9Ai+>ewph9s#,l*l\ltt_iO璭ԩ+FEzPyMRO7΋})]@j^ e)>8oT; ׺@e3.ۥiA-! ~oASg^8;mzP&J]@ m `!3j{vk0f$a#x+{l7Yo*0WGI|N v :z*H4?jjOW)iFK)bˊCpSXSOi:Bc!tX6AǰNbQR8Mp8ཐ;fo0 f#XRm 'L7u pALEg,sKE[zl&E'^9|~Rb$Z4/?+\P@ >} 6Ru glc=y볷;EHg=q.aEÔHKb-3$qyl Jɑ9N؉ EEH@cHSO,P=$#r#3t0r 8s%@je/^2p?b03wy]8sA^E/Q/wi(C$M¤ Hrپy~IRx 5񗪥A 5ĶySc\^ꮒkZNgy\ZUωD\|#sy~LȢP.f&i4$IO R'B%=bD` v^++JL{U`{6o@ K P"ԃRl$.;Y_AeCfJR(9{xȏdF8O=/9QY1޼yLHQ Ӽ:7zE3؄r[lc2]"FgB|e[!MnJ7 6F]9}FI#L3g35[T};gO>nz!.(y|.3O|3wrdf'ñM@/lֵA}Q;8*4_lmVIQ4C3h֥<<~R5X*H-6Mۿ6 O3u{dcj<䣌$*#6ll#6l #1ni~llB.q}G6Y8ɒ" kll_qqFď08#yV>7v!4%f)`7 >,wO02 U1TF0aSNYꩁIT{5JLN!( UzdI" ۢk6x'Mu 󖷏Sڽ5&/awu z;IU/)/qܩk97:-vaM2+,WQ8`B,Xѱ'a! ( &%bP) &XEUBY0%s ^,*) XEEڠ,bl`e]{"]m? ł9:HY0 E/ER,BzPzE&wX/6`0j٢,BNY *e5UX:eAծemڶEY0ۚW,h{hIk;tU$u BߞLhsUXz?68%}U0jrIUJeؑ=gs.|NHOcEXiO|: q)c n',k/Gj؞yBΑl[<ϳrڗiEix=|^j̉vpH}};a'PzfoN;쫩9=XUw&n;)|sOy;ݩaNSt=OP:FG/#5fԨiLaQ8EGs .Oմ ~s坾Q Q$QG3 ?7lq1@4j1@4 Dch "@l9‰́piۗp!r/W%9$΄_3 }kzp,я^2g:HF`O\qQ1J8F (%cpQ1J8F (a%:4ٽReId8ԽR|GVTaTf mG螽A7BRPAț!2a?NKHi5F/.-_'Kw'QMsh48 hEh[Ϻ! y.^YpG&TjCA~oœJž=U8Ci۪(a|Vjv94u`{fjH-$nͻ Kžg꼧W);mt:q/v \v_?ު"vlX Dj!omws4Kطy:u8lO4kdJ"~25j:H:'lI3": biw [O+*Zd8&Ɖ@<.3ci#~8ڲmG8ۋ֐J?X V1=X̥=Z J G]~߱\gƦH f CnզXq>0 pmk< "b4#8eT2kV?yr-Zxnഇʆ5m>P 47yO%mHDN$M̊EGM> |Egr ",۔H7f(ˑy"Tt:QGNAfiQ#AD NQ*;\&{Wg^v:ګpQ=A%x~3L(eu_d~#2gyO \ 0A2An:ܻ}& NZ3#csi-@DҴ`}Ml#B`uM+4 d?#Pc^ڨV:ԳƇ3f6MmtS]3 g.X?>Qb3/v^RC/^0s3^#"81JQEkIKlœ*u c|57.d?Au's=nҗF'ՒX rɆ* nl-Zla?<>bYao>+hoƯJX/. BH^|s>X&[C4x,`-ʾ.ivȦ9,Ak/2xoۭL VM)Š_12idk%v15ezG- C]CG?ZFOEkH4lC{)4AO1a=zYl. \)qy9qnŁQ:S{uVc$$K}vHDwK2at8siG8&کB#umyKɩ !,@»AJdCe @o*fn|?s煒}- 8 M]vSd&*#eGHRuBr0)#'qC -'QQA:GrHzSV%Xږ' ?DgNV9**×&|.%wcHt$ lv]l1k^M4;4TŸ]h+$aNhTbK#)/ X -5;(hNBpKNP1@WMyTj1`{vy$ XG~{eTn {xYT/E0:w%3p14h;!GBqYB9ȴgc`3;knt(2r_.l\+B;3]`Y.u67oc ۵m=GQe#Б癁^h [NeGx8?RDR͡wRj.j_AGuOɪ #|׵!i ݚZk Mfڌ3j*FS Kv]촷 LYvl80=)qTOpqM:~m#Lt#+L?n;1CK8)՗C<.s0Ej :|tm*u VK̫[HF^Dě#'BlPQZuiAgD֘`j$X1:r TA14$'maKSNMIQ86H{kׄ~EՑ^%/J>ȷYhvH`YPvsd:y^hE$pzâp3[xezqOac啁XLjn4*)oKVa I!+h#do&E9ksAfOṍuXp؀^ͅ <=Kw#[]+7~aAF}Sӟzgǖ G_ O3+qrQkKT0.hSX[$;$K(מ[/pN)h+]D&/O8[&kp&$ ~$=8lmOYr؝cQƃT}Yo#Vs.S7=74E7uNSRn@$ GOm"2UblsiJx6ڡzIv9"d)'"ԏ "Gt|)H8'~ih[)KƄ%gi={\830hzb˷ύ]%7z޿׏W׶_Ę 1!Uhn]RfTcCqK&â.D@`ݶohlCN; Wfmg 7s4aq`SG !"9:ƏAk"!pDj 4t#b0DTq/kC#sS6# ^ypUK>pT[avT q?}}\?~??zst KevOW׿\_|xSBWG͗p7H6$%WÛtoґ=I}5xFyKwxKyxO-&Txp`7mw9rxj0\AMm7iΛ4ZTM:VRM/&&n&yzv2Fi4|ǪՑ^ZF}U(FFGJFe;FQ?hJ Bih_}E{)-: ?ҨQt(kţ!#AWORI3McW-j#M q*lH::lHdìbz[a޵)JәdCޤ)G[ZV̜X3G_+@/v7`qn%K dVt$e-ͧ>mViPՓeN,ڒ s7uBv"^dQ,()%jE s^]A 9sq4Qb9vlL9N4eQRLHy?Z& r*5_h47Cb"6 `[ƃ?ZmE++R!ȘJQ.-rL-ޱhH;0r@7&%̝!E13.H#7M1z ]qle>̊f Z!W7(;o@1qvtxQʙhiiO*z;YJoUA/T1K44 Itn>(1m2/4.'e.^s?5Hf۝k(jfzs K~C9'M_szqb!\Di8SF`;{"vmB &#Lr6bST#D(Z<7>`\11b<7f.5N$([_0b횧R>^N$8(xMSsĸpBxq/DUrbw.[!h!ckjK5785@Vtڭt l-`MD!3;S5{'\$N6tQwçɄ {o{Ydë;t4x|'+tTp4o HG`~d텵I!%鮞]V+I]^EϚ*eJƴ]25 ?.d0x:6_ ޒ-90uzs H( I_މ*YhVn4CQEHv+í.@oZ3-<GTm_|EM7l6T#WYa\pq =@|I6ɯ75o8mAu1vEVn_5eg+>!p_6CP{DɆetKq4sBKOfxEE4]l؎iGThr\jH (~JlB2AG0O y_K<3roĵ }=uL('۴;~RqnOʖYK:8foiXm e=K+4]2mBuRHAIǟo,Dq]2ҕԲ8eqKÁ]L%0٨AQ+m 3|HPi /޽?`xQGZ>U]9&yq S8YHn qvFc2USÿ̯~vOQiHĽZuva-G tAhIG|2EYMA)q MxR*^ ZG-v`+mi3S~胲 êN:M54 I?۷7{qvkDaa; bfDm-&AK#+9( L <][3y~5(| 6G'Q#;h\}˦U0MxmҘ^(AG4?|\=˔sÞ1piKD%>_\hsUIF_hDǛBrϸ ;ufD&Kl=:&imÙjt8VY3}.`( _B~YTL p:,o7UYQhYfqT,=5l#>*Z΅f?2>˭zڧ`0ApvpLL=:~FJ;e/255 i#z  ތ.@`}7 A< o2.j ci=S]չDsRK#"힆-οѻ'!D0ﶘ _'$ZN` e(P3I\7&fS߄9klVן{O3u1 m ( Œ$ -5.lhapIp y֌r=*7+M(~X h \Cj;nb:vof߉ زu3 ąTwtqۉ><b'f0gg%w$ fsZ]S\N+mO,CzcHQYuy@ "Ķxَaot(@Sԏ c@,Ԉf7%Y c]fiUٛ#目bˀ6-ӣ=jfOA=pefA5jP7me>,o ۋ3  ' @AuE*BCUEV\ZxO!vHgy X61D6$&a-`=MQ鑏$J"9BKjx#FHzBKZ=iXm׋wwSo[XHͻˆ?ߜ{GC d{Z4w_3SoYzN+Vnn&7WT-_}W2 H'n޿~{uUG{K?6'XmS&ؖeo/oT>]^ݼ=_԰*p~f~` њDl~$Uz;TD9A)x-QB8i_ 'EA11rh.ܩoZ Wtw:EFݛ1O^6u-t|j3f2kkE4 ߖogAU+BuwE5/[G>*~oD\KeQJTu "lT3 Ү*)N"fay'Ԡ\W{2T ]mf']UzBN2WXa;SU EIEjMSut3<{rYak {p,XqE`AƜ$DLDBi6 }fØYkp>SK_qpZ k*p;"oj5)=Cǥ/pԩ[ǩ{7 ="!پ=z3˜ֈa5&Buck̶#w,^ǂB;#cӴC 9vnT 9Z[$\᭍;Ӛ4F5_.~9r~=[3I[#wooa 1c|G]L 4UAGxOA=Sei c86&$E.bó0Xʎx$u5ԅu Xa3vύ $d9'4CNV|aS="JMP,I,PȨ k&IFPEQvrMì,ݬ55< \@~7S-کm>IƚI\Jp4%SC/%Ղk)Z IJcFEp[hƟ) ʨw8E(=Ǖ zcAY],scºâ`GDT^j?w6m~_q.v+L@0=Z2HةIWsIKf)m<}SйISW@0I>8Fk$0W#}'bÕըo./ 9̶sS\H_P̖؊\Gs@ش=?ii6fOͼd 8u^&R 6ɯ2(lG6b&:7Sliۻ7UނT;C+D+E=C- }XnBN7Bi<ظ}Ԝ{(|- שo/m԰gGk%摹ޛ y7K+ϱ.3HȰ>i!ތtlHZrB@~7SS~O $YÉv2UM&l>+v*lw}Rc@B {CWqpݥkNHKwmbsCx9"TZ3]8MO77`)##gؓrx?RbDa#eaߛ91Ϣ&[9a2@ý. $hs]@"tcVIEI`ف%kҥ6'IUԨ! r3Т{$m^2B{,|up-_G# ly!1İOϔ,fӵiWV׶#J3yzX!i+CV՜2LD'bs'̩9I5; "64,O,5AA F7ÖXc &^d+. u²$);s?zԍ)wx@CZcU^]t~V;_3m&%/ONi_ęBT-i<`btA[ þN=T tayp^ w {BO/#R'=eaDsbL3&,藠-~}DMA&Œl9Ú|Ew$| cMAy9h5:Vsd%UcmB/5òWQى?mXF6 ۯ烺*T*乥w=o`=ZF9z"Pf5=ߐ9)!m 1Pb_>;!|*RΑWfl=k)dGh,>_B F_Z8ueb}Py6궺}$;aꦩG}NHJN2\ $|(nC\VKϿ?щCC<]b.1d[r̝ t#]㣉E\lMO~J8PnS*]q^$De`Ϭ`x'Av AGMDTЖWDTgŚA84ܩ R$|bU62gbE^s zz2 Y?Dtxbu1kUXN@rѿ9 O"0*Wj0"W 3|(\dGT]|u-sm_, |<ieǿ%+HfP6 /*)N#q뉛ihbja ”+aQsN٥W;NOΪ; ^#>!5~:HI!V Nq,%޲9C)Reh׉FFprbN ` Z+0 :oh_|7x#\,t9ϻ]@N\%1U%}.) 8 I7.$ nKz{@OI5]55fZ9fuanNWA$Rxw!dنtKlKu"KFX-WL$/h?CF[|3"-^ko_'z+H:Dzn!k$m]'}blDzZD"= vuvUD"=Mk2M-n"ֈDzn?Z}gԉD4DkK'HOӚx6HUe\F$ӲMFi,=D"V[VA$ң}8\>vI$򄬳WHgqHDM|"a'NH$M-ê{7Vsr}FBDaͯ>ѥT= 4rjz~#$]c6@;,]ɤP)'e6OS{c,d; !GUqr WfC b-!a)j S&d墅.{Hz5jQO*Y@h5KnT, %8ƞ㾶ՍxV +?FP"Hឧ-ƃZvѴ>ظkUnK5.#=K6[*ڞGqk\u3 ʞ;wB ,{n.qm GbY5foE:lOOtʑ5Yf)2VoZSt1Ћ /VƵiBt 1L7#5pfg2}Œt)DJ9x˽J %\AMsnv$bt3W9ΘGcfc1`2/"ihұ CFa}hA@ZN`E}=YهdںEۖ~%i_J멯sm$* Tcq`-?SppTUXE0x. ~u PHkznd*IܧRphFvU n `U|ͿY7xȴ7^݉EKc#[%bb[8XBp3>)~|UQ?P.,~To& fy^(qy\Ɗweحx V=a1`Vk95td z,T^G"3 ($^n6)3nVt U"K O?\]_0[B^ ?YYFC(_QRF<c-ܗ Il:x /Z NQM[3=-"`Xssd;v"y}&=z !e_WEy3اє]K_r C]7kɕBIUVM06TUVYnu={ZO*`:LdЛB!c 7yA / 5VyAΰp* +_Txwzke ھVAD`x! >Y@du0l d0|ͤkۍLk$^(9ծuų;{o:ZM+aqrW\MIja\ _E{;~Y8HRdXY>>* ugtI՗Q_xfCMDIJjFi7-itUFٴre DmYK,۵~U\. ڕErѽMѲsHX=J7}bp>f;2*u1b$ͿmC;qZkאx%v, VV`[a`13s&Wo1+"Y(',-kq+o\i $0a:c"!i,*ъ,od§gə=gr7 v}.BJ @r)0@ܐK˞a <83REOV;"ˑepAU)YUE!0  QjNqvxT_( %=2y*])>);!N//¢wd *B)Yvޞ9.Pڈ͗Cv+G&`}FW׸!LoɊu6[5+ִ$ fho>k@PoxH&xz˅O 3Bo13AG"西Z z'FfaŬM|@A> Z?DU 4v(w;~vI9թp"Ms\5op 1;y)/[`2tos^U8KgO~:{sz~ֈjݬB5Z3A99- A rJ4&K 7p}Wth:i6Xm҂ݺۭ³*Eh FWDhiTwUۍLivB<LJ7^c6CDtpI{z_ivO~)O}d */g<텒~a6+L~灪qe]CJyx$=1$ T Pj%l v6m:Q;h'D!mQȟs BY*MǓL54ϩ0<ն5CF-Q &|]" X䒓lI wxr4[OUIM>W˭[%_rkQ57F[]irFÞً]_s5Wxr * B@ׂFԇ!,}3vAtO'+ւd{E 7ʮˏ`aͥ\nO0nGYwKAuR[3YU$33f"eyU5*mR*~fkgoѸA ݨh#>@TyI`8ʚEWa$:垕w1\jU׺ f`l$w*yK^ .5(C,- H(Mcn01Aq8b DZBfȪ")b3Q ӂP'$04Z4|$i`CMe{:&1H4 5uܥP^;kjDL[?)_T7؇ŒƅGD>"&g~SGzcA3gEmjvHG}ISDr`e?ydYmҿݺj +d dϒPr. ٯִzhGZ3qVi$mM_.il{(44l^K >9Pp|<[EB<\}iy)[wԄ8=UCKUб$*mΧ)I62CZ&EU'HR2Ob75;8;u^:]:-+dZT@\ߦza^Z z&7\-=owN,fF-=|p3i¹\RLy<:Ӌ_W..1 4|5G aANH$igN ziA`5Qn1h!2J&Y yBR)/2W6}¥WC6^3m"1@;I+ D`: #GM1bBͩ//kȃAɂ!Wgw 3HYrk# U6r@>s'9sTaV )Wgc!qf (5#bEJz/`l&r}g+]3rZ "v!B+S}K =z/p/ 㨗_ޟzWÛj 7|ݷM!f}RD!C25<_@x2Q}'r813kΘT vhD~*st:5p3Qcblū!E`a?lݾC1 Qc,~k~Igm~OG5=3Y]bxTQ\&NjR^b:ɚe (!kt_(eDTV 潪'@էǗ!,V[5 gJvliF cŏ6 &0T+=]`-߲DQtfqk7S\YsCn{|')]iGV{-EX|YU@bܩE MˍKnjࠂɞ 0#;̄IV;[/Z!ؠP.a6u0P7;}'SŶ`@U\MȈE#j|3(-UrӔ/rAʬ&$lp%r4mp XEjK% %f)KleoO޶!qz'%sTtC{ (-}Cĥt[F-/@.pxa)nIm;Zq\fETهϿ||Γ?ӛ}xɸx0[;MẕG< .Bە$ G 1ذG0X81PD0'%M3>ķ-/ ܨ=wp*LZIB#f<+~I,cd>S^'ǷbH ކ"7jOz+YRE~>|~}Oy/rWW .J_}sKbaAIݱjuFJRN3_<NjaPLSY1Ha1~. J?Mxq:U'6rZUF]&Rs/D|*%I(ZÄwRW^CO7{q'2ODJ0WBHSot.tvLO$?A|=BƧ`՛d=J4/D̓;TrT+:!U?&5^5!>ѫ5 py{ZS{ PD2eU$+Dt sB:Nfq C~T,5aTc8uf"=vՒ>ХnW/:i'ԷۯkLxOkA:`ac3k#u'>AKB{*:5f-@9ֿ./~?~8v3Tc-fK ,#ĸ噟ĠaFC1*5,tʧ`2W,NlEQL*4`\N,>-4R<z)Y(3&[<WI;6&1Feᖝ3 (PefonRl\\2 wt*sxgvwt떈|mp8P}ChHLMf?d2=cMDv *LbNSV CۣOO@]qȧ==qUh @g1 7&Yq4_=vbzfض6b,VŖ%XR.qQbf)d:Q4cI{G {]=YFŠ`È:XaHLPu}7maӎ#} w+jWa a<uu!Fgl`wŌ%fxvNa=ϧQy=@="M5ϋ9yV G8:Hf[.[Pݹzn~F(Qu:=cS伫ɉG|;X4+Ar/T4]u6xiꕸ:cXyxX*sH-D/XWԺe)Oo 9s|"|wʟZB{I7iq #+4*9\|.H1{#BfʽwsRKtSQ?Qϖ#|Lba($S 0܎@6)<8QlB nuÖt$へ[7"8ǡ$t%ȷjYJ.z "ONtuJa=w- 'V龋^DM7GmS^[ta\ &Ui`:ۮoLǐ1;-' DwKvBS5UJ&|V&vw$v:{ϻJrؾ넪ޥLgT,ށ1(|jݦߦy=|sm$a_67rL<VYC|j0tcB7\e =Q>Gg#LEZ`FW&D&DPC\SY?+w`MqˮY!rTl&l8ٽ&M? 7{D$Hx'!TvjTX Wa`*YNm5<|H-pD91 c{ƁϘnxAWeJ:^-1Yߗ7 si (`>4W֙P9H2F 4c7<^oGSJrJ󣏽o$G,_q>sS/Rհ]3;[` ̠7 X"b '_DcE/`,_D$})2@-2N0^?k XV{_; SǡH":w֔Eygˎ4"Pq[i>XvxO꺭`?%nQdѧ-LVҦB$|.C #~z C?ptGtWبtɒ% R+"r!ao)rMK=}!ѽC,ԠG2D,4,]JG-/+M~YY. >@hJKyJHHwuM4T{uZ)&kZPIbS ťBF%Za[F*tVu~}>p}8a}u>t2n]!(ږi}X`+<};bnQz5nWQ}@3hl'ip9>yak՗Pc"s|s` . \6 ^AµGy!?}2NHF.''ibqgR5xxL7P~Qfǚc5e0X%Ns5jmEU>Н+s{oڜ xJb]4i5uW\Ǒu<8 Z7_$`E+]YojKu9Vw-ZZ^nx/I"6RѼ ] ZCM8OsCv rL: 55"H}Kt .lysʁxq(=}Q G(cIA0},b ¡+0hWKV1Fo7|:<@nE&qѓ"L^+qZzYXRpSR(d99b5Rm 'Nu ]F_mVLrm#9zP[}jٙ6,P5FcSquozk륀ɕ'8:F)I7 &HȣV*ca9nցRe:B^@lVF+r2˞{أ4' FG%4MbioD]GU,,@Q$RƮE#UXrmOs Td$@2n(rs}uiBW|RY(6G:M@O:SoO`Tv1W,i.kJqܰcۧw 41ѯÚʎA-WkxǩQ{O^j<|Uh .tT +xn͢D`S …fF!M>AXJmΞQ0YFBB'VB8}/ 3 $LB #֜ ( m8[3撅2[DAaڮ]XHe%+*TWQ5;RpB|݃Esm߽ ĩZ̰ۂ| ,))78d4P)|b`Y! Pb|0ĻX#+Xk丣L' F {}jtLe?0?X^'8-Rh?V BxzCcWnA-EluJX:^Jk?h58@>kT;hs z̶;.ʀ$x/Cs˶([J _"XWvXnkVvp$3&#XD>1 '+, 2d' Y(Ãnd#ٱmQGUA*cڎ<`Om߅'#߲Z|hYɩx~޽BI5Om*%}l]2+.(^ĭK fB$"\Dt-@-b1, ˾b68 0_b#ƇӞi0!CrV-Էpz?3amH݅f%ۀCYxR>Ar|v^]&Lx6Bں^C8^۰(ӒuvO˴ق}j!!XauϘ}>06K%0L9ۖl |,=.¬b1 l(ϩ&6LYvꚶޞ-?K.Pc_ɲy);K³_[Zn\؁ !y QRn3H | >aubl`YȣEy-I pJPWҜWH+i5l+xW < Ƽ`'SX(@,pYOg_~9g^93`5OmZaTb䁤Kdݗǚٸ{ܸr+p\j擝p#b}mMKY 9V0/.bUe3s'8Cܺ])st*GC!/Lk$J6?GbDv ADtnMOu|c\fQ}nhaVD 4"fqmxOɈv7lV{}Zc,4k[JNlN ?/{& +p JFUIIHf=uȄ 2:f%PuvHofvtq̍펣flY ;V;QAɯ;7u5l;)]ap@;kCڃ_T 2]ucxǘ dVHp _t-pݫ?Ϟg`᳍'}¸A~yӃB5aYw8kJnt!dSP  QwTs@O_pqYm8^oo_O?kK:v_)+ȽW1~(di"o"rg\ᛞ7IzmQoRћjx)8ߛŤzp3qf ν .pP9DNJ/;*|fT!7pUt =lj,N?nūw'( :ۥVtC:a0~,Τ"5Μ FR\Q_]ÆهU]`@WZx"4^¡Yi KJm[Af=Ypŧ7ЮK+`x,+hGyM#:@M CK G&̷]8_VUv˦IA4x(]4M2\`7ZJ[N.t`:q %{[fYB=Y:!T jnS4/7ۍ'S=D[~V*a;FQ2f2\ƪ"ZS ζƶKH`}͝!iW׵t*w]{*K>|ū7W6s`ig{߿/8.5׿}N:zMz徖Up7/./?͇萟~wn^|oË˗?,~x{Iӷo.?]y[Տo^YwĂ?~/o>C#*>@_|R8Z|Z[bRRuמGG RGws)J#r¯T>ү\|!V+*G)JcJRM^ړTJmzM+*(J#_W|cRR(yoSZLJ3[m1 ],L1"4i7B`aSD5Eל[t3j=S4ԙ"Jcς)"8i`n?EIʐ89'h@R +,t*6u>KK3ONe@5ޗTTsFL$\h\W sJw;6b 1חΪZ<~W0)K&5KBOJ8I1z꡾L׷7N=^"g$?JZc)fR9(߽K>vpcGjczt|1K3ĎyszVpEh;+ZK<Ts$vNt9?$=\/!UWH'7ȡe' `&fX٢xxCszs[JCzBJmM{rvu]Fz37=rͅXjiW{CjvU]!?uxQ^{+k=-T{K ȗ{uVOػ7ܣ~ /r\BV.X;>3 GN7ע@ a0N0btǍu; Ŧc||Տ'D7P`nh6*cXka&#zhQj-Zb7BAPkzc}(k+hne;BKh=^EO<=z#ZoXE덐*hQJ7Fx}ݰjz#z#=^vzz[4Zo)=^ ZoTBY[:L]7ҫ7B==DChJZO:FzgTcȻ{h2uu^lnz ,ZϝY->v?@ӝ#0@:>4b3gYтeCa-B%ϔ8ŏ ڙܦ/[ $O#z#+Muø2^,j8ui1YrƘ{ilNt K+ΜIT-$º%Y@7n`iR8X< k&Kڌ:H-%iA :GEXF fs!R3{9[~x}XP…fBOϛ U$yYvff Kt¥gkbyqqؔEEX7SH!/$|e(Xk̄NMY$ת VTHʪ}@x!+f6'<0q&3a`0ue-gV?YHHq,$?N|/V*p;C꺼"Pc-h$zJ^3(M&3p AܾÓ!OY!8wT7يWkH m2ɨWToI v3u㣲:TS&gEb0p10(Ƹ+ p20a x| ˚ēMۿk LMYwo;ɹ͆.eXݳyo ~C_M}.uUA~j3d a5')+ &}=n*:TU?']p+X m \≹9}mF3W Pgy$f y=7tAYr FMyc&O~JVOB=,-fC=y'Xf?s-Ƭɘv%ǽXS}/6p^l LĚ|߅% FdRSI0W$ !TXW 7 栥 ({RJ7`pR)z-yl{o–Zp js&L[[b )."ìMg3\7ͻ׼KQdf_v9A&>wh(}c/V5rЏ#t*fl aۭ _UEli`)G=TK@a%A'l*Qod? D6W7Sr~Y׷*׬O3lFm/=: j ޝcz*^X(]A &-y7塸w51bz:yեIN~)-ި~ Op]XAժt lc,#.ґj!= {2:t !;52uP,`.x8 qY`Gt`Q+XzP^G]9Ft놺L@"-t1yMG&@V !\n:x_8NmLwA޹!;*P6߸X&E^ '9z7mp~ 8jn8 t aTAKLslmΡjŋ1 +=Rh?GU"NUٜs% `n7"cxtޱ2(%Yɦ"FyrϠH֔3AFW+!;zM?u{kP !4^yYAeYCQeӾ1G.c)O9- 6F[txUť1R;Z~x82n(0-? ;o߸`]ǻ/S_|o` 6(EN_'˧LZc y3G#_$Ă=xQRԮ\C)p0OhtpH)YW~wKujg%\Rn#kQ{gEu{C]ZSYf&n_zҹħu (DY$$eCP&*.pq gb)a]0 m5s;k6tUc`K <}3Y5KT f+:Бf7~c!'`FgRHVET{U ")78)Cy^l]8χ;2N, 7qNx4R=mq߁i ոoT@EoK7άe| $F7FѾ:F/kn@7"5r:gSP2{/sG P-S4GQbfMep3nn~3{` ~xqyp=bnFT0kL種1lNB8A\\NhDBKH$0".!atyw)j+~3?,̵͝7I-;̜Բ\?OۼylB壶͓}]Bhi3r[Pcŵݕ|K ތvJ/D8uoKGE] t,g$%LS\ |xt^E+W̮,y!#?yxr{6[b,W1w؇& v^ k ]xP`1`_t' $ciHex&D=,F^231:Q v%8( WBiLOa٥:?tNզ'I G\h*&xFs:#^B":%ꂪb3BSN(dP&˒7 L%GC҆},(4-lM\|rB`lD>,}^iR/ JeũMRy+3)ֶR'qC϶ m>2ō=*5NU+!0KeSG:5WVZͮbjԄjOz9'*#'p' G\pb*,w+YVX7ŇX 7(sQsLE ˲g:,j s?21%pLLx&O0@ʓ<)z1^}BFzD{= 5[8K֎&!r\&n`3'#YmQmTFE 4FI=@9ֽ6FF:.: x=re~C rjSŢ឴}SpTr0s)*Eոh"Ԟg~yIoUMjpRjs {ĽQo'T<\V%%\S%Hu C c[%ډk(l_]78lJo:Xa(t*ָ3闻iϘ w?+Ѽe3lrƨaus{'yX!> [-k4^UEf(y0UX4~S! l6ωG<͌dy7L+SH7*5W >X然"0I[w+AՊXndh=!:og#Fo') ؂%av'H\D~|E]J>0ųpUB "<٣Ѐј00C/X. Xhū`NE¶ ZMoStNPt]h)p?A֕ lOҵeڧ(]sPM '(-!30xC5sz4/l@jӭdZ~tlM2VѨpi϶(BW9R3SN5GuDf ۻ'--_X))jQ;5!6N,d} QSo-s#5*zTdOF[ !u0fms}BdI1eaaŔj+w[#r4M#>];X Pv5};.ɏw&,/Npw|tu ȹ»M׶fNnDSlX*4 =8:l.Ռ>5J"-ˇ_\88;,U޼|m}])Q'[R^l Rߧ(t,OTOX8%?ѕ$6XfȊGdt S!cEҬPMښFSЗΨ:hknsY Wu-+ gGzc*iQ+ч4/:Rgpi3.4*Wz}. }Ƴ0 ˅Hվ'DS8*w\C!W8ݕ ` ~a.iM.3D9eKѱ}%01CЖl;&/vtyfd?RHIdy8?@3ۊ l \ h7'Nn/B{^H^[b0C6s o>5&TvF4aU9ekI|y5LRnV+N*qzBdr-/`-EF6 # ,t3dtkK?硸JOo⒨6 <^ȍbO-.cs+"ydGms2.ۄHn9{'x@'y⹩ e7yxn7\!z33*<| ӭ4B]gFjwj.-)v2hD;j -<`V8y:Iki_L,Jؘg60Y3#ݥ0W!!|1E #G@nX6+;c&EDc#ZƎr/8E,liu8pY`If$ 4iveL-9κuPC*iޥ[[BalI5p@ ~ၒ:,0| JcTJr9/Y7!&JvJ1 %+\:~Myօ"+P n.dkJ[OgE 8oWe(2ģ; vb Eێ @&E+d@Xѝ r鏆 We-P;cr| e=:($xƱ !.1yUd}e{/iMwxD tE+ Y]9Td cK qs\Nv c 0D)XINvxV$HǃYkы}]FEKΦ?.(\mdTo8s:H)ATjm>//Sw~nɕɼtq͚:aʏR4!=nlrPKq~~fKj.dwsڗ!4@wZ3D aY&B $+),]U/@{G`*kk1 egmϦ/*|ѦIk2ߑ&S(rf_)PhjBf&Q=ԄJS=s׷0 2iS4rG8 !ۉ.T3P 0:aE ߩ>P2+pl RsxR~9iS,2yq,jz<8|!Au>^sm3Qb3` np,wO;&[~|4% Ud](hGU ,x6,97M"(]8:Z#9w`čnCcş+ UBHN@Og'*VnSYg2h:N"7f q>Gog LJ]\*m}Č *1Q&(gXȊAc^X:;W%ZsDjbvej86Fr1n},51 aIxc{O~"CƔ !ct(w =Qp"[S.B`[B0Vہ* FnlZXQ?}Sx=+ Bu:>]" }R~,YpFZ㺂,͙.8Ro]mW5V[:!,9@d1 קnJLi`ț1,8L} N [6.-6Ei Άi~(_xk f @ԦT}$,o>`C ]Oo__h|xq|/,TEj/x>4>Qjmm}T0d&mJ"Ndi@"$"II7(KT_;j k-6vқg&adڹۜ~hy`$ q؂}&:םhό U Fa \$qh=:qe,r'Fy`~,1[曽?4Rp?WHi)b[@RvWfM)%{йZeeX 5b'BNa*Oĵί@&Qv6-Y`*gv6zApb IDس˜pGB^+4K51R!*O0{f n4^_,2̶Cq <:;eoٞ*WzҙY=_蘀B33)/W$:*R.d9]ā2UA4|3**7ƿY_3`P|O⒂zGOzw߸3-j5M.8v'Ӭp+Z~9qb_gUw\=٦Z4-Tixuv~pc^?I+.ObV)srdomGXe/ww<::˼<4,we@ak%{tgBHE>w2?ұaIYڄ Np7H9  ݧƙK?=2܊;BplGhe¡RD-:CQьݦ|2g-n-nkˁ-XR7?xxhqkQ5>wrqdO,vf,nk,n I. }[ܐ {|n(_"ͲI`y]W40]zՙM!*sf_#>C,8{}{q.뼏B5U9<h=J ѧvW>6i+4|D+/\[RźoѺO4K2?Z(nLj9$ͥ-ke5xzuf3n)*q3)QG,Үj͢H]y6irE) G ^A;_ip5NhqWeAp 9p=i GM{mJ>qWepĬYFfDiŦQۡ륍JEA)N/F tٕOLIsI'-] F86Znш3X~$ )>fdn+g._9{7Vϻ @9ԉXH-; L-=@$9Zϫ}[M \CHHHw+CDhuC؝͜#+lVJ4ܑuYE/OrW\씧ݱ?>u"+b?>攷t|yr'=Nbk_ {Y3^mQVlY3($atGFe0QSHTIfG! @K? % n`GBٟfÙj*'<I,nR!I9l-f+_?va; SᄰB, //:e2 JpNϒ$v~KLPf]ttoԷ `怊s1Q >5tJ*G(TܗNiq,XD0 ,l8'%8n;lMQRGaVb!6̷|7:U=|c{L.)Q#N$[Z̸l @ vF*WT0)V\Lx+U2*1$SYPa͘?qWa]H B&\̋MƪѽhK|~נ a1Q t Y2b.^z[d7ޏ7AM.V&ޱO~V}B ;E?fn>'&Hxcx]S.xc ʍ < \r6i{4V}7T ";v=3~&=QHEw=oɍL]TK oy^O5'}}ۡgo )Γ.x-Y.Ju?6t;},D/YVVv&in+xMDB}{c._T/姏]U)ZynlmtRȋo\;2tCWky{rq,?{ӻŇ}G]R޾y{ϞL;'^u7>]87_-~|}q{ Z-8~gQJBS$pitUJv|5;yTR/b4=Hc{/ @a6(4ԤjŅpazN!Y>mSM&(ւɲieڃZzPNp=o"в>L_G Y.Sjb}; Xdo~)VnHP2LGY{̘ZwSMxw@q 0!*`9V=w}O*OwP zC/ʘ^LjJy{ o ;F u&wRYlۤץu#iSCe9DS=mvdnƺ}TCHpH/-X?zx_9J=1]?C(Dz}_+(.z5%)=iaZj5i-_DZ1TvaR~k 'V?q2zPߣSQX? j#. c 9xN"s.~WE5[=sO9ϻz/]N㯫 7&#2H7f Е2^-tr|K_&94:1 5$-JyУ76C+G̠~q<)7ՇL W+5:h^]KWȦ=k"T?SPs?퇶0j _z?䴮Qo2*koz}h{{|}>{S|}>{|wWl"wݱ+ҬxW>wmiJ]\fwjXMJ?3#^4}c_d(f\V-t { k+] 3/O;&<:^Ϝ[(XoWE~gTe+:Ybdf' +i[%d3W݊lTOx1yXuJ%T`^|#HocO{R4oWe2XP_x?w6B]D;\Y8BCӁ‡4{U\=RooK`7@L]:]MW ƶbj~ -]Q倛2aJ8G]J_- }iP^%Y".)T))WD ١V2}^ &Ҿh3#X1*6~ :;bL(=>X'^G*spA؎4P= Xڽyz/$]Y[/i;6s(߱rŽ`CU^!?^32b~ iȏ7IZ/p$\vx:~%]N"lNS JkvrA5ܑa-Px)o4kX 9k[)!ggdIp'"& CxeI|!So]l55_`WMX-7OtWJi$ \Jq==;>Qzئ5}jVTU| ;P;zZ>ΞHپ 툾 URx´V5mE{BVXb^X)k<~>UgGUeDpqlV *3nM1 TNk>(ݻ4ݥ/=^+LM~S όPV䖗mW`.V5Oެ~C<]]\w7<JBYL:5v!rUj w_#1˷Px|uFI`E]:|q7NbS\?d(_+ fx CBU,Vyp~Lcʻg ,mi8y"0C*}wĆn6/';qW}|`R7CI8vp)`qnhw/q BzIe<뉾A oF] fls)=Ji_wŒ X@2t19=wPKxdQ"3fi]Zp,9~ ѪnM[\i~6<h}љY ^8֥i/-wi{o|dL[^sP;W\sm-]qx[x!\0o/@.]GqCHW@o {t$n6f2SBg1(ӥFXB.hi"*x_1wI4 Oʞs*v(f9YY:$9`:5gv[YF<}͙Ϫg,q=n|*itiiB{i)3@)3P+S2H-i\^i3H+f2ӄͰ~f3]gδA"026$ņOu0 | 9t'F|xQTs] t˲Oww+jD└hيxz{T*N~{ۆ_ -Iv[6>̄;mԜ ;M4 wՄ;9sFH5sF5sƨsF9)ZG,dVΟ3B)!!y9g6[0p˟3ҫ?gTɟ3JU猐jPiGj<54O/8 !URQqFh5qF8gRg\N@C:TfzboJ9gR 6=G"d9rM~IO3L$9}:9??؟&xdm~hi ̵'i,}!?0S4g24g38!?M889?ki4 h0hT7aJ9#-+Qnaoi ŝl \E+^Ҳvρ:,-`&dJc.@6ȃ`W@cF P$Wø68eH+7hg#(k=V+.U:͒=v81Ҙ>= E$]ah@ҹ37WMBb椫4۴fnc5Sht Lf¥nv/ra flxϺfqYn2u,AyiF 8={r8]ZҤt3e$:mTHHR'DW8K}̣7Ae ^kpMD&pE~׵}JD.Yn1XL BwθGBj8|'[!='jFwBVS >JvŊxL h4&d:J2UVO#Rɭ)'$Oy ۔OQf!w t_rSb\]x~d NC.u` o&U>a޷S:}ķ(="A׮"a 9>o!0CB7 :FsBrxjBNJ׏'0I/mgƥbQÌ-ov•tUя ?c5/83巎yi%V̥;~W XYIvԏۃh/v *6r S̼}\|{q&j )ˎβt2ƚ;O $ YLqEU~ rN&2MMr&*7( {d <,Gn+u4PQnFV7J(H . ѓV}?)Qa(Ly64 ƘuO۾t[~8NO{HD }`O@?i'g:${1l01 m,ԥ#!~ Ub:^~DDVqՓ9Mz /p΍Y8iȍ=AKEԊQ`JpdJ24͠|pNOAd{!ˌppԉ,;U3ɵM1e7mb[W{fgVd;qDb/Odzܱ?6"%<B(D&lD٠I wi$vnĊ_:#my"H+mbFP&(8 B9-#( 0F݇rRPQ\J9B)C9GP7P q!V# sRr*PR sA9@M _4v9dv9B.G`#j`#v9B]`c-&}R ̯/ttB<3J%@Lta4V߱,X7[_dс %ɢ)GrB Rs Oһ`7Y;}kf|pDU5{ҮPe̎BKeU?3wUxJGOկx0#SVP~aٶK ?Hw.؄ye8gkpO7k%ҬSw"3uQV?79@*ew|ɺ< c/HJі vm+#}o޾^|ͫJ 5-wo,۵r`BviM*?tnaJ#m1MM!8KǮfl6s`߆3:[νib Ri5iB;n!YZ#J E =hGzȲHmN1I1,tH2. `#5slhtRq'u8m~苵(stjR)tyWVt,O9.y*7)/"qB6rx f0S'n|3u&b@S'ԍt:2][1M莥O6a_UϔhQ়v~9.\SFp.TOP_h<8b)ȴY=Z'W^EF|زG$qbp($3^ koM9)0C&T޾љrOʵmj9WFwڇC x`b=+o}-GH90 zbY v30^w+W}V''ͬ6!uUCZ@piK=3y8_g.YfZv2wsyx+֭mJ%<:`"Vez1<Ϋ:Χ;C輳((Tށ;8"X ~8BϊI ߉82"3ȧg/=2'Ĉ(HuXAݪԷT46{c Ayhc*\svzV痙IYY"/N޼TyZ"nVIP~'3|s!pc$w;*HQ覂YXVyh,Ǥ)prEzZYQ3`(߃kvM׀zB>3# $ "7 gEϐgT*h)F,8bG'U֐|+XmY4+GSP3p`|σ:ZTcKuQ -< 7@ ,>>W/9| D/C;ނվxev@[LZ1y«PbŘIGFôm,BGi||ټe>-G/6f' !a&-5yҹGכ *43^QlIIEOB`>M6,Kذ[aIMLEr"Q}8jӄA2U`|# f;t Q3:Cdj<3kӭķB&ymN/qf%:*_R=£+H?m8|uyoDͶqmxtnu6vax 46 N&~S-ZA1'=Ta c6nWsZSۂ F*FW0fRX|f_bʂ4E|5<*mu T7up0QĀ^pbe6i;"-Ɗ/'*"ֈ/H/ݎ =N| yU/Wn\+Y}1,t2i|rmhz.qt~;V䛲B/wEU[:%>{u<ҕ&.aD]l:toHۥML]@7+F,HZlI7LE1:TZ%Ank Ia .32;Ѳfb*'ki`PՠMI v38uyf Qj)Zrjɰ6qJWsQ$ s^Z_ly"XM}64V-x$S7L "lO^dYe#5DRHY@t +4F_"%Z%Vf$;4,LM3™μ;OȔ&%@,ު,3߂3Ph܎,bJ0 ^CoLɾ.o@Xٸ꺦o;5@IYnԊM+ L tA~w}?TdH-USA~bB)H:+ 3*=:B[Q4gu.{5󧊩fYnxz"\^PEK9:!~$'t6BFN`Н-@(XbF9W@G@`d"GR.8:0O+]Yv@(1CP&@~U\mq !_>YקQ0 %^,:|vͶr|;\W֬f+Oo ?= 7E܏7عSZim#zYUYR5Oѿދ}}{}/ 씾9i(,YFA:uÀtc;= zce*„ S0 KƱݕ7PFyQ\.ݥ|:m}7DƖo&QiCBN IS3DQBY3:$Nl澓=R`8k{\ d0TpJX6dK/\:KbfAN"nbޠXM`a-R4HI1IZd|'~hE q7Ѩd~f`W&9-f[3cBuG_h"זx.A8#xʟrNX| ?aGXKǺqMVf2JwCΚgYZyZeAZ!,'#])ˌ#O.3/iI%fh9=M`/DIfb جX{ϊ-tl8N _YJ a\6l/EE̜ʹ{zuܐv o3=KwNi.PE2fZXQcXm\fS6.f'|}σjHgW_6gr6g;<X,7DB/( ᐎ>y!i.Jblqt} wu4ڸ}{~_rBpv tIP>H/6n `\{]<~3UeeE6kPp(聭HB@mH2R_eқ =W;˄)Š*2ǙcIo>ŗR[^qr;5&3I+~Mu .e'. B?<> қ[s[dbs|Qlgb-BL?sGC^n A验3^BHr C^rkTj]<֟Q5-<kk vyۛftK7Z4j:K_F[,6P !Cs\cßf$@(o1rLT`6Y4NUPEM? #KP*ҴV zyQ##Io|7Q+.J+-cyzg13a=H&$(.|3y%#(7P.ґja^l&AwEvTctG;Mv:8 HZ:Wϝ&u9W:kL96~R2}!kp\P-e+fHfI57R!L[;Td44=Ǧ%Iăp\| !=CV%=kzdɲ`> S|#QLUѴ*}_WyZ†a{ rR9T OV_F-3%ҳR-ÙiJc:Rzi[zu;_Q7hԬL\J8=ճ%REYM%YĬ,R=wy{iϢ敡gc]CZvN^yRy)VvGdYmM0eO'ͳ^<lg6GRMӵX1N1pV~;QK[ͶݶH_he\g~ Դg|S= T[#HeZ@v`rNsɶ]oX=F'Ӗz2)s!H _YK_>~ ! 1BF<-?tjzfx˽ͳyT>"ҴdrO?C tI_lMra}sc$t<$ nbɖ!j*LLI%HEtw|6z)խn +80!QNDb׭Eo-wRNۦd驃lYh+d~>460G(M;TN "tSG'UW4t0k iKN NX-H ߒPnzwd{fopYq{-qi 5 $A* Y:5 5X=GsvVGgWQmj.ȓt1zv51qJԭBڮqWMd,U4ʸmL`~߾ĝO( gmD]8y|<\ ݋G78J l,rL eĬ?{ӻŇ}G|sc,l>zÒ}x޾y gt^\b.s/_'O^\t=ckS/^HMwK&h?]t 7_-~|}qk]o0?*x#Af%pBO% >?~xOo^YQ*mඤ^ylO~lG"Mgz8pYSx037Lƅz<>?m$l}:Zesm"-< FH54sflRo;x61udȗ<aWϧ39 xpL GZ#sUq>lG|ԝ7tA2SgRjd9 N3iNtM5hNqe5gt ooQ׌,D)udCT{jO@մAgU-s^uZ3/ΫvΙ܉վ)#?2YꎟD ux2DuCZyi}|Ϳ2'0Xt6+N]d$w3> % AY9ԛU(N2a9 {Kjݖ2{R_4ܞ*3ȺRʄ;n)t2}3[&C3XA@" !28֘FgV0/V+\-t-.#t`8I  z> 3Ks $G H?AI Rz S I*Č|'T@cFi<4HaW"H5Hr|$$}R$I_wddj; 4ah1P% R]MEYV_YF8$ bO ZDՖ3(2Fk~ӯ%1>=s鷒gI'm_M8B*Ts~hzfiTAvM"ghzfk-Дf҉kָig߼#-q:Kz}G&}GM'UB5JB' W zŠ$IWAyK~][R8Gl{ϙPiY;`,oؐac,aO?⺼\+)[G"c(y4Y3;0qMh7Kc}Թc ^<4=/<x4fLHG F`'jâ=f`/YM 0{p`M. .Mftԧ81IfHzayo*[;0ۊw lq`L ;5ۄ`\Xc4#4ӆmS7?k^Ȃ Q^g4aU|'Duj\O #Ӫkac (l3 mǪ 鐙jv=hi#ՙv雁V\[wXKp&ҧ{$M˽9_H$̌2P0 Q\l[Z1QIР`Z$۶=mМ/Wjjk&Ih~گYw Ɇ"w;Gv~&ԮK 2Zɼm@[ܜ9eF>Hywd %[>Ag0i24HxSVмعG~7|ѬRr?Ц3id*I8d;JyΞҌخUI??j))ќG1Ec͕]vGE94 Em^3!z?|64lkWKob0 :x_8T#Y@!7܃%jƚsB![d.* Jb3)RYTS~Yn#abwv쒊 F K BDQ@Q6T[7yVX2?-m] Vb.kCL:nʇ::C"? `XX]Z3di~Sc=R_@Ni/H<(/= y<ݶ]!T͛ d)LGtjԂg{nAc__,v808yܷ пϴ5Ӫjfqp̔1* xAV:FЍ08*32-)C9E4IG2i1%;AGhtZb#ǽ8?řn#{qM3#sfRuΌ3 u .NX:xj!@7Is } m?IS+-qPCZ9w 1v\bi6ם7]ό* (Ƣ˾\`zT[ hk@ay\&_uY`>Uu < \V-:ntP`YdQU{n,|r/Weܰ 6NtabnI|3.A/a2ѓlY*y.؉(rS Un8< _LWf9΍+Fͦ][0 qYۈ._RJvtRїJ5xj'qyMI(+\vw%{*c/jb+8S<^QBހ 6'nꛖ뙖K^:e@,/7dZ2h]_nuW.,sZ\*j՟Qү)NS (Gk:80}'֩Mzj )fg.y ǔ6Wl J眷RΖQbp qDttjtPN]ϲ9yJgyt0ehs~’mIb(H%UlņP78zErGl֕p/<d_ОviWndE*u2Т.ȷt4ƻ T61` on{B/d-MnJWo'{oO67Ůܰ2qGal}tIl/3'sKw~U)~vM0XMA`+' .QnH \dd7eu'WYNl匑("4f䧓K '?=6O?@Ü'0~Ny}*0wWLSPnݷ,hSM|B>Tgqw3ۂ9DM7,==di5r3ɬa)eވ;[fZ+#a}L;#3%#\5Ssf&x][[k?VjFo\&Ք,W]Ք/¡i"hZm= MSzm˜c%l\rtmtJ[E~(2׆JC)UejC~*ScҁjFՆup [{03@&Ԟ{+m +j9 ¼Lk$s8ʳ$ r˕9Aʀpt_h//.\*f0]ЈO4O2jn(>͸M"$6 jn ,tS-m"We9˭_lM!q cPyG UAj9w._;,-qO4 l$PˌOOs~ď"3nj>oEKBDʠ Is%6m-p `2ä<_:ke4471dM4?rMwY:ҵ᪯rg;WP~Jhu+tl\^F1}# !iA!vx} b|xËKd! IEڅ#X55}Κ00X,]o\V/({_1cGs:!ލxĦ#ӂ<JckToh#a?gnt\lMgeg5v]xSVWtL÷]ݝwQmBEtu= ݎN-#ƙEy7:\:x~K<0(vbCe"3Nx?x)P5T_#KV'KS=^Ef[`au3u P& ~GG4S9~b6g,A\yPrrDN4vi#Q%h3vKK  Rc7TQ P1Lx!o.AJQW?4)|k5-eOۊh]T*C#ɻTFYڑoqn̻H%Iz\XÄ%q=qxľ"E溤}j[Vjg+&m曑|#gr! ?Z$!zZz?uBٹ^*J2c)êo&񀙙ۡ8.|E4粙̲Y&Iqj $oY@|Ma )ffhrMlkNgf{3n5i5/Wk$sL$y6t9ze&!hD| ~@b- @eVy!7#iQc2CQFb2s-LCW4tyaUfe>>﹜8D;nܥ5? L3 |rUR}Ul>կo~ Oc7QYzKwR܏L'#kMR6IF3AQC! ̱!MN5Ш{!y`Xh6&4eQG2(4JIeIPp5P'>9Whg+Q2`DKZp`lW| |BMA;} 'ч]0c ƴ9Ҥ+QN(1] h\Ҧ5>a Gsb5Uf SƫyIJMI570ǶwlCGQ[}"u‘*9e븄$g5:5fLEg\t'˒*?O'5?RE&{tOen["# ܁̲1Ŝa9pD8g9Fˮ9m5L*6s"Ą f*vH%5XÚci?KC34TZ '<ޛ⛣zq]O^&֕'Łq$ʂfX6>0G?$7+b]ߔ=6ZBY?+!XrHfev`ɷ oi(FQ$[<ŕ`A쐫0ֶ#*\e @>6K46m3pZ%̻Kۢ\݅0}c'duEwt^N4h^+.n% +@ \s)ee8ahe]c}(2W]$g SUȹ/^DaY2S9N/,lֵd .: >: N,ܝGاV̳2o⚨*6 AL=NAخaA:ZNU| NA@ _kLoF)qkYoݲƋN5qgLf;oRKD4Vw\C(+RYF'nf M< me< ;ܹWqeN~2ykǎTuaGMm&pbByhwS/[U})@?q,B $)*rb?sbUO!x=/^ٔEERP6"O+aMItAb;v_jhY瑕fuЖXmUZlw e );4#zt|ҊhOAͤ|.6EGN7HFNZfX l>R|29n|l[D,m/d}3s?B7s_>:K?N8}'܆tǺ4ҭ 8^bLRCm\ԋj*܁Jm @Հ )p+&љ&In4 zLvPdnqc%!;7^|ㇷ/._vn$u(;|w" '*YQ,]b~ lG} l6ѤGgc_y\0ptTL<ͬ: b}NuI8c⺹ ^ZὙ۷?^_\*< 5gG`K6Os扨o@|޾W]OW/1)yuŽwzNm} ˧nfdIIq<';]:oYa;OHnן6t:Xg7?^\ȫ:r3dN!iMbjHT-WGX´T\Гx|5tMSyA ӯ+MQImx]dW{w/_~ ?3n *T7b{1e5@. ϋpA M4 lb`i;"5e ۔ZXCG h+iʠ]يg4Q0}8]~ƶ:f6Ń=x|(3ݝq>ͦ&-ժ1DH=3Z1c(Y.riVbm|3%*}-A ʚutXf^[2L+B ӎJ&&=ɱQaby$|meљzAyET&,ls&usS"$4l{ !6?,ՖG]$ ϲDxq93t쓰䧱XfYK-fC2af<:3$ط͎zXMB)(LGN7Ml3,E[9S7Nc/=`Y0 0WE|s-=#X 6~`Q1qZz94$>#y(8XeX%#?=F~x*#yŨLE^՚E::&{D良7"~Z61(+.?x}g`gtNs Z۟Z-·$LWHWE4H냥9FçoxN.`ũ㚮\plm O \`nj1gFi69 !wܴ=vh.3yQZ3NR+uŮj,qnJ.S!(< x˦TjM'Dvl^{CQOQCqN9KmQ=9rY8#o}7W_?o?0+I0n@c*+M9erۉ.B߳C⸾`v]\Hrwus?2$2<΢ &&B$uly[P^9(RN5TwwJ׋q| aLU=f>@<snJX1߄pW'17e|nj@5łyʽ WW` %Eo1=sVǫQP1$1?dlwE}*ܪFQM_t(#Wb'kW6Ú`ۭznFAjyft|omeՁ] ų"Ȋ7x6+Wdwx,r̰Ե(UfҞ}ޗf?dS1E&|xL QIӨ >W7 5"_v~Be[aW+B?W_)Pfzdr{X[̷Hf(r _LjT)RDm%{WZ YCHsiZnuwmV&a rV?]i)Nel$J"hRœWay$x_ fۭN$تN.^Xn@UzKmY~ᑻp[< \t#٬b%@: 9'a H#ʊV~e H?/~o1ىQ@f\t{Pm։)sPήnj1T- ymԯT9T ,VN ?B, |M厞$vWW8QyHJ M0o j555!u̸MnjTOaܲ܅t8n.!!Z~]ЅEdk7A8YMs?ʳrYÌEw\a$I&.ks`_5ې[LO٣6uBvV㴛,8.Xkq:LcGĊ͐gqf+V]nﰺ_8)VMRm`/>]rw]{e^PO*!+BD2a5ίە_?S3X|XuRsJ.cH 0p7/./?KG#]+Bj~|`/>^7t?n*Vt7\QCkЩdj->R PC쓤Z}*M->R PCЯ'Rk(Z}]-@=PS p_ pP8@'esA_ PO'ӨTJ-!"R PO'ԓjɔZ}BW O׫'_h RHxjB-Mm5FHQ蚥=IW4.I2h貞"z.)\SDi`EOR"j[STl5k *58p >/ju{ FkFdiSu7D`;B 4dx_uYVgqLy5c|9 0(yTeKe3}^W1AKw ' *[;fzZa>HmI9:6u zy;:ލu)&D1GES^Z~]Jt=djŬf^`Ț5;6x\pqxC,nE3jVfϙFսs'3i?@O=Էdn[O=osK ,>R@M%qj"8ZQ\O7i遃δp!Y;A5ıY[nO m"\!v4K2ԡxN;KY7s쎑xnÜS^K6ѭ%!mK3-RMu(+k-ݾG f\k >#e =-v폡FѶMRD9)t-ݯtoϭ{r_{^Q o&T33`)ޜl}GvV;GۢI#߿S"mi]:dTJP- ƃ T@=<Dnh@6zwe"{.Ux/yw|geQL92 b^P4m+I\2Ж_&k^,hWiMGQYJ\ϓxѧқ /sZq+xc8I-- aLnsXY!uRec 4vތo1QXcҥOdfHj*`dx LX`V:Xf+NͅG;$*/6pBB:f Dk8Q๱ 5r#>l7Qd8tRe_[$pZ{uxg(ʷB.h:Tl%^lE3W2@M@M)Eʷ@' U`ꀁ0PO 20pH: 8F'GZ`0M=߆d-0pDu18Bd`0 ؒc8[ ڃ,aGj50/6zwdΠ";Z>A?譶AL_xYSg)T>RSzc=V4BB__Fo3 ynKuY FpG>}Wlry2ȐDzY]*6j0TC;aK5j|zYGH% \omh(#Z#Ysq<{~ FH"n7tÙ蟝E>:Z^ngO*|b4#zsϮǣN}8V #ӕD آ22D\I&ˎ y%M6,Kkvp]Yט꿀{zAĆ0SĉcCfJY\Wg]%,E>=˝lqRNT),"D~o.BspAjqŰAwJ (l<1<󾪍|b1C=ʡ?# KutΩ,qL'x0BwZ}xzϛIgqt^^`Znh2/,(  EJΡDf0}~%ww[I>Ӈ gAygbne$;H8*I='9{t%@h8{N}%t syW*X@]S"1X &+2[m1YqB Go;Ycu@`L'5+8IòY8Øf+rmC٪W |',<3-:> I\,r AF3^цZ$an&wi4׍[dS]B//I 40x)F>h{l)U:rpVL՛{(i^ō"bj$^33{F;8ͩ%Y`9kBS63]_=^ԣmz4VfCmvk P`TYxqTckfJg7أyTRӖFfH;U ѳ_l1q4^X+bPFBѳ_KL.DЪYuu$\Tl:'E2l*7o[\q座 rXq p#`e[U#y xcUO5LI1,,~~V.܍$R2FP,h8͙5'_?Ո'aJm}fCWeP!;A6nˌbᚑHauǍ@'5o)GY8<6-hxMzm<+1!8ypEU}x 夫&:@1تHjyn*YډFEYuQ)}#\ =$cݤm ù?cӵ0,K.YOdcqmbdkFK'Z;xd6xG =6.m3G U/bIȩ>s6L8R NxE 氬UG?<렷8w=Ss_ZS1Tk(7Et45"2lĿ͖y RZyY A-]g }$[~ K\Z ~W1| AlvO"h;H)$Ks[73Nj1=q~},"߻:~@L""_<b٤3:7S'ro/mq~,v J}Նaf;jw~t3a]9\6(>eaWxnO^AV=hKpsY[en8a.f ٳlfቌgX$aSaRW kWag| tjMkrf3g=|;~y2u+M ;| <ߏӜNi3+p`h}[6/e ]K50* 9bQm"Էl>OȊrͪΣչ~U?Ju|S躨/U=ŸY6^1b۳3Js,Ca͗Sv" R?MmZl^ȖȾ`.%w*o}|6ٱcK* (=a?1F>=, - Џ-(<_G Z-PѹqyMUp̨`KPu# yf$Z|G[*Kp{ ̈W%GE+kucҰ32^S$$ukq,<@¾bȫlg߭ e#ap?z}U0{𮁷`%r[Ly-BM~1ж[:٩dn*ˬ*~B]M c_=7_*vt_~39v"NP./UTUG= ڎJH| ÅJMϙ%>2H7ܥ|eintoGs^{kLj57J I49D#W;M^i (Ȩ,tDlz|݈&"[]v6|O%ߐEN)^un*Xf/c?,ڄ~Ğ`05`_#2} U-Y nܩ LCalb>WYiHnJg7uQLicmdgTa쇝D  nA DEGg"Us7TO9(d@;!\gIR+eBg3"Q=L*"SjfIDTsΫS^pwdA옾lC4`~l/DĵTج8q/%kY$^arK;nO$o7DDНD=W:;1qbAac0}FWpq 11Rf:m]V\ ^oh)Ca`93F]|ee3~bSWUӽ(o70yScEOKۜ}!,[<$~j#Rnb ꕞ=GgZV7Ō O9dǡx=__B.Y\m сY^ݙx8r۶`~vף >P[DS%1vE+b42GDvD8f.Tݐ1]/TIT,?=lkh ac>DqӋ3W`Y |Z >27OHm';`k~I0mz*mK|2ja׎0 qa"k,&"&s>#a0#&@ڹOr Ce0T' Pӫg[MUER+kphͮ$߱VD |-' "8{6!i슴qJKx"+*?@% +NМkfqG~8cdC07c2hLweVvwc_ dX*y@`pijO5s)k΂b>pIWG Ob˱]PsԌUQ+b\S~$Tv*Ͼ *](K~2H9NqX `ڊDh1իJ1,b@'tDCs=yK~zEF@5nA7XEJX,YmTI"?sC/mP.PJ3TrX iG]1f,KɀzIZ~'Y9ކd="MlH.Qt̷i*"Р_@CdSGwH5]h#M34:8]VnNY*w[>Wh6in  S: $qaI f1}9/ 'a{πT2 ke& 6%kQIUɤ⪧ɇŐin&CpÜo`-td-˛Zc iL, 3(|bM\;RT"6 $*#c!W)D+x=/sE>nN4<5#O+*=y+;;BLCCw$v|;~ƖIٌ 8 2 ( !ZQ/f1 Mgƨ[i;VIɢwX N'+{isN*M8(!G*Z)nؑ~7(ca=S7?!Lvd.];^t]YoJ/z`uX֡jk/Tu+#2mf e^=>c t1\3؉./;XR:q}n:_䗪L|w~|Z+uDZǡ߯g"c (3gw[]ؚ4?;yL|` {CVzA҆˽kX};c{ċܵBZkQCwN`i jͼf"+~ZI7{,u*@4>zfu->~nnUb3#$[ZKbmwߞ7n\ e}}-cJV@^ ~kQb(sIdPF€Ѩ3hX㳐nğ9p|OÛFedؚK3^Ʒ VTú'YC`Efq9 R>g z,:{w4`DYc3+S'L}@;V>RU#mߔ%ͥ\(ADNiXvm).h v@7W֤\}!//Ա$MrK?HHӺ˞ Nk檂! f$YwbkҭXTL'Ze\8]B`oO +}\kiHQ<-n03S,cf:wTO X jQ`TMph'”-@^$65u>0UK;86ȑ&ɀW龎)Z+\- }S(+Ï̴زmi6Uz#[6pLa﫲abmJCo5= őmNE}STi\RM-w<"*>;=d8%Q"%W{(['8c汗+Z 9r l_}!FY%{ukqkS:# q(@f JRAƁ *} ekfVֱ& R?|ռܞ>F!b2l* dO]фC=Qdr^_8,'8@XPiE+23d @ͮ 1}q8 F`SsdwEM +Kp T$$-j΀UPk©u"# #sV%B ;%BIJbڔ$=Mj"~݅kc)OJ$[=~ t 4gi n27m'+fR$ 6[ė,5($g@pVshťEj:?6Zlf~'aQ^-B]=X Iv赺nX&蹩da)j֥Yr׷@L V @(*d3qp) %T<#@6 y(TɾXgu5^t _}"n8պha_zFC%`(8 ȩc^珬l՞8)hM/Vl~4{ꮼ.u0I*r*mm+$@[\%˟f 9Lj.󢾻>IO_ua\z :ߣC=rT2{=j+kL&d{)IK3y&"za?$2)$sHd4⺡3sT;~+Ps;M%9<߾ ROw'MOtSĝ^Wվ1.'4G0v%՟W~럮_{;Nn >I+Y240V=z@gzzKRGI/sW>(uez_cVq*m ^\U/~)pۤ{4oa]}u=pjS(iEҜhM].:Sv}C}~~xfw|~x_"~77>|x]o^h'ctH?΋ݓ& aJ@+O ד29) k^ry rNsR* 7'%59A͉qoJ '%G?BnXBK4'$&М?r8}f왓gN ΜLY9sBbɱy3'ŦMG e-a,/'vH0/ZI@TTy+0,Yj5Qzs~,=$ĜWW?&PfLsتCW+IK^suҥ?<>p m`" *jr~iN޾`yN޿`N?L'o_3-8sCzFMHM?Tx$秹nނ Ŗf~*%\#\$f(c,-ʼnB-6ʉ-_֓/X֓/X.痵zP%&>=":4$&ŠFA]h )3ފ)8xL7ilDu$wwvr:?[ǺLԟK)7X%?۠iR ̵(6ϳrF f0󦚬9??‚?*i`~-h LCi$37!9~|TR[f&"9/ld<\|~LSrhOn$]7I"758oAFL vڊ_i#sd܎iWj>reih_&[$9.oݖ'%TL~~O`KD"/=U*CQڝ4b_si-X–.thf]Z_8(-h6"ߖ>KD2RcloV7(,e]$c9 yfbi  YS?J!"FXD4WB`ۭ#34z l<&˚5;K9,9T]yV 56&/}ZVBųyF-/i\CYߕ{OƧL5e$!x_K+S%:3:P>1 ]oZɎI5)x[`S!;UڵcSc IvԹur:ɵ4֥K[ CV 8wQc/ikMkNM<>e 4-}3 ݁!h]RZͰb&y㹛Y="g9tI_2T XJN]ģ 56N 7hnTnX]?;owq_r|W;|b|R~BC|9s<͹eRGd0wid^r>mx?ÒNj7)|eHg2D%9U/{jd02YIC6{G7>V(SeeMW7yNUPXRS?y3|eKFec v$`؎N3,+H)#nmqjKWԺaqMr#f>vIer-ڡtܗsOSڑ5ȼ:in{m$#Q ޕg.$]^lPc3RR M3\IEu-kP!d[ü:9_ދo9=>Bqg]RkPjٌ~!L)PʤXyw D\ f۾Z#ѵ倢E{fQëtݬޑQ:'SFhNٶ!Mܝ=W{26"MaZi-> 1S+2=´a#+03*-CK8!+: %"Z8elmqPSKs+3쓜Ah07źf`dO G ڙ:P>zGyO܍@{2+k(DZ@C)EϸYS=mB6O|\B\zeMf0SŒC}o?ݒ-+1fޔ6ދqR7-׋ޕ gw\7J1#*Иk!{e>3fږv=i!񖪓8%ti~YwO!`mqkY;qaPIsJ+\\)j1rʶ"ixB=pmF6J?jzoLʧmA;!-8,O1̦#mS4T. 4)+ǦbӘ#[?qIK]F6+>}>u[#D~a씚OnbkΣ.w+~M5bh{[C o4^or 3} jX3.Vӵ-/cl9[,.ߐ|q{[f?@ٳȀdSjU?<U:%ۂfEk4DgbYlERÙޢR)P]3qB䏾c){h8`v BPЧfd&/.R$dB^v5ڭ]זLp5߷/6JSv(8+Cn_IUép?, {>aOm3'ax[ ^9Ρ's\-Wyz>:πv[90C6qW7c^}Ξ "M^rIaNc @A**8.w`EI`ށV0R(k~_M0Dz ( @[iGqP^=H(]\8r4O2ߏrּ+D%K'] UL Y=`b0!K-EGm}畧"n}\zW)7kZl;\HRW:QfSg%[$x$y9fwɤ;,1 g!>ckq8l U♱庼'ӯic~2t_,t3#+ 5\ȜÎa?tS]Y2yI2_p_})>%bM*ͥky*yԮD ]A*@2ˊpF+~+{sWQ١Pߗ#g<@l'W=}|uC6ТB:Y> 0d >3NK阘~Ҽ\9$(y/OqNW/.5߸{F_ \9sa1~N)Y!8b_COuCf"_o^B W=qsgݸ"܎ NQ[㮂k\%{I͵>!17Z~o{8_o!}MܪԹ0,mhm,EKޓmnnpsr -WU_~{{+%@ uӪf9ڸ#,#Žӷn.!XL[leԩ_ЎXaؒ(_>Ojxq<6+0O$8Xd $n&(<1 lyj{hXj}/=N%0EnoW搌GnN ],t8~řZo-L&5lJͭpŻgBI7XMfb۩Mε^y^2pX55nEфrW&?^c꙱߮Y-J wJ(,e"D^:-c֬WGv [t[lBϡk6{En`cV2p ] H|aEr^8*5 KӂPesw_sv!ݦ~$*-[F[Hܿ%Mm=w)b( 0W1pxiKqVUe?Y {;%| }!LiU_:kV ^*cM" q !K\Y͏x߼Ҵ_8dJ}a3\];59V>LJW.ybyVDLBkj5HկS>Pв:m^7?U; ˽L`jC-݈$bYWJrCmx^qԍ%lbyY}`"->UbO@fb'D.\?\ z} Sjzzl#ӏOM<+/4+ﱦ7mGL1L QhTUc-!8ZjTʍdD궯:\?@pp"@y`e$,EiLH}pXD@дYvO'2"afnUN. 8;G_\6yGt \ k~Бلλ$f¤(:ݨ$z*Σi&NZy 0)A=T]+:F*)}_~?t}箠: ȧ]~R|*!׾w?]|ݗ¯]}5$?"\WtY .~Շ_8?}Տ}ë7C/kW~|~xvm|q~~?Wׯ~n߿~WӇW oVo^}ƥ'FgJ#)}ujz6ޣ=)lR \3 nJLp/ӯ#ipQ Dw]JCݜ`<'(0hYˬ\f2")yN3hfd8{_oD.H 1F7:'ĈAĶ~vqq \sBb]ɩYr|UᢞkzN.9肞<'4@2Q<'$ؚ%?++~@jƪu>tMa` 9x̘f:tnP<9`jE$ ׯ ͑-.豠s Z"]Ϟ$Y{C׫qadWgнv/1xI݌Rӽ 7}$jBoz7p=UOh ٤ftXimԴ:Պj8&[jG2CQ-t3WjWd qea,nGhXHMX{M' eŇ7^F{XL᜕%5z 笴IDäGd$FΧAyw-X$;+bI5묞x?-rKe~Z%|^RE, g<d,KF-PNevr~݃X:1Q2c`(w`\H/ ًzbiRϧx0Sgƫ'cziYr1#JmMk9gFRkUWpثJIe$_Òco>e0#5s~*N,FЮ%CHgJE Vی#ǃm K bMc^w[jջ6`g|]ނ}RP9ccp }5ASǚ:2 BI:>"k A%l:Ʀ?(j{OF.j?3z_"xb+Eg2ӟ (mrs?dA?է esĨG,NE!aCϱ,V᜸mm8g#a j+Ut a #[A6PW%' TSJUcA&0U!ig!+ًӘ9 !%`kvyVkWsDZ%^f 屒26Pcx]0#UB r󋏣^C^E3PXsP\ͳ9Ƨ167Q.q$MҎe{ME"!֬5ه3/.{ޥrhzuCOgt (` ~%H.B~7`* &9=M*U<2 GE.oTX0Km?2em1ȇ(AdJnn㶔qN:yTot+L6":O9D1Eov28*&KDŽ etуV1l ]ʮK&HGrcG9ڶ9L`sI[򹹑`x$h/W߹~h<~3`eT$A֢>#]N\ AZɚ+EU'MA<}uLҲ|;|bmX2+L$0R1' ,m#K?. zz#\xqXƠfNi ʅ}Kf~Pb׺\ 냚#wRu;Po ֛Ա_>z<8icc/ ψM}tXx=WnGgpF޲r0' z)g8QkXG$Kik$:0:yp9r2e Av~AR,r[dNc@s' Wv! [aB?8gM ƮOHc9I(O',tzi^9X TY+ yF kq7%Fs?mFw´YJ(6i`C' S;H5Wa{dT`dLwH(Spݸ1pP7\nV|2ce;k?n0TGvlmcR[~iEe0@Qb+ӣ%ԢpkdiUDHo@3@-cw0,IUuux}\=QʼnBJMnq 8N{;cz+ֶytG45M;#N3ӺrC'  IS-\>TѡstZHG)AmhQ}\6.,?:ѤDSޣ9ڷ ⬢.sbX\N2-.:9`˃E/qZ$Z2lQ[@e~m}xGlJڳ֨od?q:#$JNGXP|螚UmLzYOd 7\"HSɞA;(oO',A3KȲi2>O!ف#T+q ɴ1h"029%J~ 6S5 x!5NdbЦԿz\H7ѨJhR^I @=ryuYn*9]b,mEYY~f_t浼UmGCe?EtORzLRjEWVH46{yG:*>2d*8:eUgmE8}Zz{ү-ʱ p$և,FxF Y 8Gw8z!Y\>9v ,3-X 稒/=MR i@A3ww13z:%gwnh:_ G8}JL_ ܾvG%9֠v'ݘL ;KM^Ĵ0 3#Vc+ ܃vfך%p[]΄4H (&!U)#aƵr0u[1}ٗA~`fQx) z'dJ' Ƨ{g΢13x;xAC}<_vCщM bDL=:2 c[6E}yu&}ZA)5I\ d"F :!lxN5\{rxM6 x6 uQ? sO\_-P7OyGi0-YxȈ`^ly-?;o")F7e^ѭ&dDZ?xʞPT)fH  C tWV쌺l;R%x|gԤ& Ip=#-.BN*L^\udid~d8a*HxOG$ʵ#Vq8JtXPK*Q.-ign?XpNd@d#ָxHA׷`Mb7EH&.9.!SЉ uGʠ  m ,'#Ҝ'DI'ڜS5u Esm0lr@o٬2j#!M`ݨp-dUJPߠTa%BPԗѼ?x  3J: o:;,'fgxlSN,uLFۙ^GrH&b8 V+ng@e~{<1cqd.JKL@ObYVw3mt!Zh@3v\JcU,ImϦ}\cS V /{:tYP|̀QhDG$hr!d$]p"2׉AӅXsH;pd@IQkrdG)adUє- Dɶvu̥&$JwjMFOQóCN*if5>\Hܐ~js+qd1TcREiG+19e'Qy^t xPl.鲠Bl4&l -+uzrs?|hAxgj"2Ňۦ6|o10>Tl[.4?L4)W(%e97H4L3a0ܭPc$OQ30x}b>8769jG{pX=-pƘs7lJn #jf* A@.YCYÉμe;R}RסF436CO98Ew `DN*Ufjz6`a}e¼6>[nErcPndYwpOXMdiL_o^\p`Yz=+3joyl>p^ٵ2E Y41uE\5N !& "Ku]ṴESҼޯ _S dS3O`t@} f$ 1et[{5YV[#5VkLIê,g5ߘCtUnK$Hf5:-J+`Y  ^ \t;y|/Zp1ﴱYkx\Xv^"p4EN͒{yY( ΅;"Pl;Y=eAIQ|hna wKW;]%aA޳ځ[߾MCîOoQ"7ಾaDUUkU^U۹%!"dβ ^T]+_1*eE fIW;k)7seVڐMUV>L˫3xaC%N-7SX"7UJ$$ޝA}A3*oV0iQ|c0ۅ7Pކw!j2l= sͤ71kf,_r+.K[C%h_ȣd',z0ımG*00:CHA6үUtSwwjY^*W ӆCsx{'8:O&6ͮN|sjtx}{WӵEzJW@BF| |!vڸ˅~w7Vg\TlV:R"M2Z{ ⑪0@te@LMum{*UEaSi^RgX9D'vorI"|"8ŅYR!LP][ PlnT6nAl &v9svV^d%HE#R 8A3f9@yES Ԙ[B殠?|{vxn\)lA7G2z" :Ln|'?M)lGkipހq̎K0q*)|wsWtpPAn3=~!Xn#uNYnKX7ωCW aYIB rfǚ}uƷE qv?eY uuݐ>kz̝؋rI yŞy ,!fo5ڋ $P(RjpcAZ~da cDyq7 DTQU]h">YLo:0}hCWrW?3X5QK6Ep"u-TT: 33I}e:lо4 LbKN=^D\3xÏ;㘓w0:Kۡ.5C;w?]kKçq;rߴq<^IA>C w\1;R]E "%x5r i57UF -V?@Qf@=2ܮjH?V |,@#ʇ<܏Q#`>$4UZL_SƊ6|FϽ %s‹kRNjf`uп .|mБs_AR*;0nC8d&R*+_ƺH* I⤻9Z^~ ?{+W Ci0⑰GJ}*rznbxm >JYŸXO DGK^z*o;si@gC ]$n "O~+]VXӛ29|Qϓ{_*PC_9Lx|}8%][d[ȏlkP4uߢt12eO+ޗ޴nFM$<ZBBdZ׍92}(mG5b)!B'n\B9-zݔZ" _ʶjO4b;CMũ7e3~A8~m@-aeܱ]+L~j2uy˲Ѹ*)K^7W 3 t0ꎁu|ZH.f]^c zMUaciAkEsYǟ&5{x̘Һ`կ1-80Pȏ,IcY#BjkX%O@7t\#)KM6|G(%P. P8|-ܽ 7 yWMHMo&ZڐM{jKLQ&1FX}RfnVj閘P; t}Sm' ~kPkq5veId3t8H PstJ 8/`!sf*yrcI?7M,`XzIr`\NKOq]Ǵ|풿xqwg!QhX[ܵ=dE6LUnOF5%|kpPxIw8KEB Җ{/͇Z}X.]Nӯ{Pl6é@IF) oGNUN6VOġFZQRG+J oƩii[žDM)ڿʍ7ҭ;֪Τ2%7'=9Uj;{!?K9,cRm1ϦH{]q?m+FH&b;yJE6ȊM1´11l9൑F .@ص )hƮt]KkȼY=~r}?t4֣ܘ:{YT4Soiz>M:P҉TEsWye8gi R0.*#ޠ|R+islp0=>ό5ѮL ޝar/C$5OJ*7^?`+@SꉒaO剸N3W1Cu[,yiNЌliRk/BRbZӺ6~`.V'ƙx{clKrt/`H` Jh`d8B48w-LSE ,K|)J oaNd@X?%#1EXv@} 9^\xkb 33ıD+Ec,`a¢ZL-_*lݳ[˅]˻>Qz'c-2zsː1+Z~$>K;OJ8.G4۴a徼Vx{ݩ黦GuP5[g\=ihQ֩wIc:˜P]_].,ZMNj# "b~p8@"_UDpJwRbOKr7#KB`< O;|;4S~K-@]zu-`qEW_s)=[b5VA]'Bsc b|ܖĊ37#')ɣQ^p$O}I"D%$ri1/)ėQ/)/)sK/)̗/)_R̿K~I1bs/)ʵ/) wiFI\xMVni뇯zpxʂoRֽK ڈ,,/Xj'=~J&=:fteGW1wډo 툓  V"5L ieKǼexA.맢a`wv}E-~trz.٭\&9墭UxfzP9I銘Ԭxg]Y(W n)V''HA2U ָT bwM!~-q:S׳osO,S=H ^ ٺ+:Eɂ$ %֠%[jӭhtic11<\[HtY4-2m pEHDz'~" 06bȳ}FSXG[Iể$1AmWW.D&my +!a ՟#c&KSa{ '5A+SG:[ }?ͤ̈́yXI++_ M-U踉kAI;ӻ aU+$>AP! H?=ٔ#T^c\=Xu^zۙU \՚4P+Zp?Z(-hU/ *YO\w/H0h-%J9݊c̚'3esbi2e]0<8gҢ}p z<7߼|yK;9Ҏyn#7f#鉶% P=q2"'[RU`|{FSY9ݾA9VGmv7Ѝܠ7eK;t3ӛw߼p7rşW,LU怸yf}@\Hg uyKwjʩ&Tl )B HZm rBʺcQ?lvtk+]i>̤;(Nte^@^A wF5JkS%5f-Yv{aԩt,p=P yםco-kި )/0є.OsX r*s(͡%|3΍sd / =u.O*w5-m6?i.VVT? kfFhNs.gY)lm;prFӯ W՞Le:anq~.rfqGpxR1R6*~@_*H՝"/HJTz~ iOw+%3hO ~S ~@BKn$H9sv(3[zJbc;C迧؂͂4%6)LouP'2wV<=!hBdMUt(%-6 16* Π?WeIiw \hFuQOEyJ@%8$)| oZX!a d.#[ W}ssߖe* Sƿ{#W3F%i;pt-a)w=R,эzjПx&Ŏ;xb;|b_AKU3&Tkf@'hs^}6޼\j+իQ#Nt R$ah@Xq~4K#dՐTJ 9ȶ0lA$uOvE ʷ؋BnwC?NCOr}J'stȴ8tԦkn.\8`GtTa .}YִCm*X b!gՑpDܳ3 -jCz2ޗ k1~֩"+R ԍzZQllWdS6+w`GEV,Oj+ mS>e|FjnnJO gc^3䪍ಐ͔@Y[` ar@$;eO˦=*Vk'7:QdenuNSx5SӴ=9nH5Q˯ q9m6v%hJn)$]b:zfyG} ߽yfo]|]eptE )P^5{Vrdx ̷dcCw 8j|6d|IDyX>\x!4G: n#ڽh[{G8Yu謁*Oې@%Z'wqa xx$B2 Q> /^`_>ё@LQϋn~/\S|31cܩiܢ6/f- oïKm8L~Gf  |Bq;WV\].V]A+ nI/VNsÓ߳'8Uwĸ+okc{0ZOc!2!Ew{f 'l e).V9,3Y 5"j:PX輥 Q ̴1]|]@ /EEK LZ*/m\e6Mbesظέs oE 5:x <yiUu WiiIp#Z6?a(9lH*`h;/[Y~/цZO>ЪDy +/Íis⎦WUF+zj@19 =k*܇ԅxt4]Mt=i.ZdtB3I+ܒ-bd:~#z|;M$| 4CPW4r !(P Ah'fX^^m QEOߗ{UI<<.sB#T9*Rn,@' ?Z&7es3&d %~<+LÊA|n4m3 ,@LWGֹK ̗ј>׶I;Z;OE-I M1P /޾yu+d߮"9:U ݬ]LQ妳PUFk?Yν!ay 8Ƿg&eiEbP g9 8rGX b\;cp'a)ɿBP <ϲ"֬1ISwFE8n~a>35nui&zgW+l*CʉUI`V^shpkVSlu u#ύ9 {%&.uQ=>i "3CxzXB@ԅj/J ˙]rPꦩt3Tʋ5]/Uљx P"3 Dr<ӺpݠKb{.l:`Ѱ@LQژ¨YFez/t{_}L#m dX2pa u=''_rg蚞KzN91Dp= rI3R[sRgepH  ZHr|kCZZeƴ֡tW:osgXG~tϯ-3o#Sd2&ya@0/us=O\CO TwnPͮ^hc &$ rl'4҆lT3\bz46ts2S4UJzMJM(v2C(ȾG(zClkؕ{a5~Lt/ 2d 3!쯱DʮX{3mBYa !w2 BCECVrYa.>A%*/_)H5[tzrԊVdlTʌuZ%|^- gd,DKF-_͇PїK%" l"rlqvTTf@7H\5JOġђ'쯥rct|xCx8\ ¯A~F2ѷrFle 8WfbE^ 9撞kOmʶ=ݥpwTyj^4,xQ[aR:lCL/=_{I6rߨ@]ȼ d;<%ZEcW1Ghx-ݺz`adE6RuW,c0$\حZҚ:pѾR9վ ,Ao:mސ}i-cqJ"P61ףuc[E Lw7hOk3EG{t͐=ڈxik#!;EӬ򂭻qG8N7ՃHr,\/1,4; X4}*bM[ONWčh\qnXhvlgjfhn*]slZxFb f%M (](jv%m~&BӍ}URf:xFF&x|x 5TǏm Eۏrl3D/+'b2HBԋtlxar )~/3RILpCߕ p|U1d M3u>p,8k_֌?+%;!s>Xz-h;"ҙT0LP;Q04&ݨ #' uc-1D~ue9luڽXUQAx)G ȼ= ZNH{ߕ^%e9Hт.0lA޷Wu捥tFNx${حFFn̳[kFPZfS9fu8 6 j.ۛN=#)AAZ~DjW"7 G? XA!ŀ}w\ '?AnN[/SB.qO,Ӎz?=""`h%t6qE'Z@p:n BЦG#,U'`6-3TDfV,̟3DQ4a#DeǾ:Wzb>kl"aH'v$SktNTY|ۚ{j]eP{Ԁ IM|}tm 49?4Kz3xXlf'!XM=aWt`y ;E3Ǖ,C2C/+^g0Àg,E"&R3r@ViAYI~[ǹ.>%XSA5,Ncsḯ~`E5(Ȍo|lp`ת\ ٙJK,̜X^9UHe:*LIqT~00'>R1˶A=z%J`b1dpSpJ+ p=_QG|Ԣ;rA6rp(O qQj7ҶwKцwf|moо6/y3n didkw1PbgEc5c}Mp/mt/ o_K6d!Jh [_gm.HTyd{Vɲ18q$:0$8P tdkhWcŐʷ]bQ\Ly1ps =FQt)8C\Պ+Ը:J7IftwʑZmN2Ay nIEE4U~/ MM,_IΕ]13iQ! ==0wthj2y;KDC'MYn^jTù6UϙDӪ~ſ"_WDŽIȇ`2 q ⼧9%VD25]^pdxD |# !&ţsdO˜h s@wH1n:33-Ǣ !dY}@O =wJdైK!"@wA Ɯ(-z$Vȧ/q|"7Xuhrltᨋ-F.G3/}84 Xm;F5=EV!56fBcϼTx ud#۟Ł yfX,Hr[8csyT喀_𾄌x />x}A'nF0L_bՌ&UE8̽ӟ~0Fm~r ɺ51oQ)'mO1gջ&p&z`(0Ӵai+J騑BG{OM >id58tτQIeH My~ƪ-A`_ʏ]Ǟ= p~%^`{^b& .Y]K\*F5!( P+%.2cCf{ލ<%VeAd[D/, WaC=yȣfxN*#] H&}>boɟQa[7l޼zNxe #3#fkpb뀕IzWY W^ JP7dCOVx :.2p׆ 2%5pi~IjtAHÚ5;Azcm܉F/-/ feE9.k'@tl\i@t7PZ19V @tY;~ffZ@tax) .̓/" Bz :=l~@oBlT@ 4 ]N{HFD6@>I9 dcP܍c P4YH2u8Ink++瘼Fͽ@FnOȉ|y4qoi~G>x1eIZ/` څnHe>LYˈ+>uj%tuCG@=N#0wz2w޷xFt%{^YMrmLp]Y |pqBzS75c/Beo{FIM9I`ۻu@ h1_eU[9Uvz̓Ǐ{\866-Zn}XV;ytM=3{6H;;Ṫo <#k l.؅!ΆpH~ J.(t|[.$߼f ݔ 1 LL)#)ѡ.\PL-NVy+wORy0wvW}|~e~7z޽~5O]~KSލ&F`6.)Ls,$*xGoIsjMpCMl 'OHZ*b_&f-2$JXۺEg,jr\ӃuD`ӴYK'd$ϼ0ˬ;@}\ 7`[b>a rIw kPFƭWw$-.ic!\p-=r{ўY ցRg1:ux;mKp}a[nfL7oǕ(we`P?Qu?RN(k.Dud+zY$le௰)i"]6|D͆<,dɫw KWq$JJ1vw 5Q=Ns̋ue-0ҼXX!?[ABiĦ(kP^YDŽy$wfW[YFmXN:>SA@# hS9̱o5t4{?7Iw{rB:B}΀u@_,B ^/AYm$ yt0,:FDٖ-]}9 #tu #rN%61I+*G2-,'23}ǝ>=+<ΑiuNd ;PWW|놞?C|WovMFqGu&LG?̏T ~KaB;) 9=:i uӟ.("yNQo㌷ w,`ȯ~-9e:gZ7_#-Zri`y@:'b Z`W88/}EB%t"m!1+q9l/[EZ E˖Lf-rt5IsP64^lH#^Bk(!@Z!4P6^,|KHƐie˭2/k-.XuJM'RXp[b-p 1<2H40.oݖ" ^C9LQK.?lJvL}GC1]sDY7_D,'w:)nleY1E;*fs1NK_RG=)ԶK(Oto$q&{ ֑3u)\'}P̫|z+>55=_ؒ&nkS&{r;N.}gLX]ȑ:X2OԮgsdl\I.hڇC| 2wW@TkKZ5VHMs- =yVI#Ef1xzQ~ep3u^w$l*mdYt!B4 t?ֺ _~zKZY%_9aޯia8ai}IwQ[m;#&ȎٝĤkKȭ&^5-y!SӚnGaC+.Y}oc߹=юqz=⫁@fEM> 3 \ޠ@oQާzD!>N>& C`PZE 1 U XeLQHy,;63ؙ>bIcFL0fS#XD/!qgҾFZstFĵ7ē~k(iP&ۛPUʆS8D3 QV"c~l&6r.FY譞_A脑:EĞ}Fnhe&L\?lF[pW SӀ c5,̴  ڌgAP;˞kg[rOE$;ܾIyfiE.{zlV*,KIPYj+qZ߈ݥ( U9r[g h`42HQCO3/<tټYxfzr_AE$pGi/^Ն߶0iJ\'֞ ia( l߈i`:' _b ! nTldc蹎>z>ȮO5Aϕa |J=\%AϧV 1ˍz>ї*#[a0zh=@4D罽|\,;SOڜR?;\I4`7mL8'W;] ^z(J<6vW /(~r/1qw_&gŮE99{..)376| [~)lK^ш4ZZ׏7ɟ]Zj7ڜ/PQ}4.؄NsAD~儤 +<QWcb%]b XܖFNb&" u! UrrOiiX/8 &*#MpHY$w Ch$4ױzr/v@󶉁x 6y}]r,٩yy>R֕ bNݭH^ؕ/6,0uޮY 9I/i@yȣϢl2mvWc'unC?#):.4E*jy|j^ ?T3HD}{&ʺwS,$._KmMhIKuLĞtw]Wqxse<3FPi)yI(Ye몫~ e1>t~ڍTFr&_I=zǎDiwz.[`8XV*g&LwR7KiV8~W3Z0@8NUSrxˮ";楫 t:X}1"rw,: x>^~|zg:֕sTflkOD,[G95*0{flZIKd,z˅SXbӌ2'zsn-`fh\8iUq\u4N8k"+1ǵ˶"my̾ i ]SKO1el@z{i҃iȌs !(]HgrVlk P}qz{Sm_YqqI6@sQ-*-}hƿ[-Z@HT''6Ns&)1ce4k)ݾY׳AJěChJ8bxU^L(`4~f'Ie'EuyOE LT (W麾E=Qq%LuhbjK4VyP, PwK0kBѼ4zW VL"A{WVzѥ)az落w05R8z 2,N7*zZ(./Ge`ԙzxmv:k۹rz_*'ܮؕG=rWӶ"<3H^~SWU9~05nygYf{@Hs-h> pvj[+]XGY~fAd2JܝGa5`)7I$2mK G r@Wu=V( ~=ټq%95ľYtQH0ol!POqs p,_+`sz-G_WG? |)H6L0p ޥojpt}o ^I?7@7Fr.c3Q٤-js2L gIZ58`{~e$ ~ ~ CM7n}76Q"J>虒4>jϕ>oLHՔpƇ\3㻂1^ҍ<`JNsM 1#+om0 ?")^V] &K =nRpT*vP _+[ - (9 אs_yfܓs vP=5{ᣏGm躰㹦htouv $e`t7T`[LqM k0-w(;4-hjΟk̈ɿ.CSkk=+B_mx%KmV2Jw[M K!.DHiyO-e+PZ-{fSݰƭ+Yj$;ų72ġ'9NpGݓDsnI"<'AeD|rh.7D\wv`e`{{`T1ܕ;i4gVH,9oA~1$UD%U0 !ksNE>v'3s`'RԦU;(; _iSTё bwtݎNͣ MZY!O viFag- OJJ(̝(ϑK&{jKOK2=Muz"r3,}b+3sI֩`gߞ뽘iB,WI.mUsj*Л&qPQhU έH43PN%4$Zps.F5ÆT+UC 0ȶpzȜ{:/mNDž eNtJ%Nal+Sʹ`t #o[#3fz2s3@T-M>[>yzzw;tcgAMIvD9܁eC8|%3v#XԢmv26j1ݽݭӠL9R+u]C&]1a@=$r V9C`DQ-~Sڱ|>@qyF>0Fw6KHJJXa LU[=,Z@5p&$q:ٓ; V_o{dΞvTChE 2.PM֤ tzhU]-֕v҂YL|aOm@TJApݤ՚/2/nl!Y4BMc~n >zۥ gr3_Jxg̝ %Oe40 B 1~Wތ9ȥY$I<<#OA mZFP:dD@n>L?8N9fj@4WB2L쭍  Vo '~gàWS"{@s&Y}b;.r[Qh?o3H)66H\W+A=1 J KPQ͓X*B{voX~?Zo_Tʱ&X4~Mxw /~<`'o_pZw?]?z3t]/jt?hBkkj\ëN߬޼w/)rLz8,ɚlFG&.l*BJ{RTٴԾ+閒x;#S"CNԾ"Q:d@yӎ|>5՗M 9#%[9> Clf.lxGHex{Fa 1fNH 1 ='Ā1Ķ~04#A]y:\qhn1d,<6ⵥ[`˚rR̻~7: JA0)LI I1IA0.#)E($H`8@RLɴR`JH S)ƥZ ! Xgkq^v&1)Kz05jo:dzC4TH7e#^'3@8Іc_VeJ/411 60H`HhC6a.1F=sDT;M)f7R+5V9L 3VN< CϘ*MF ؕruv4FGAC:0 5ʍ's4qPV|{a鱶5 aaeml201-H~4=ɧؘph#o߇Ȏ`?F[ԤAv6 Un BB,~"=rPx٪0#cw5,d.eo,<}m>òv=BQ2c`($:.Tߎy튩RC[×?t/(Z&7h,`[7 ȳq891#5=ʍ7'9֪N_U~^vqc>(Ca[FyH *Ìh[-_ sĮWmitDW45fj烗1}njzcB#F5Zȡ5@S6<^@Pf̻f׺dLW&<m#X^\j#'P7x# @41S'u=PzUu9[QRCm*43+ a^+ 8hOb;GBGyXNs.vnj.t.|.j[fP-.ʓy¯AjFF s2*+8YU8Wj),3T gQ[+YwƝ"9pa3<Zb9a r:kg,/5}0(y27 W]x@,Y|.lW+uMk`.TqʝmiN؞!SeK)y:*b~YF73$%?w2F\x&+6Ŗn1|3=zّ{idJ9$c9~6.{I5@y*բc\]7:Ίe(uΐܻv"8$^ /a5l[?`v1ٶ'R=EID+n!0,z<(4wGeQFIOd`13r)-4 dDv{x}%3L-6 ,CCW3[oz3^` l~ij^{v%1x[4W}ww|77Va\/K{<%+! ; )^s\8 fL]\a]k)Їe^EyAl_?gа$oJdpzQt 23q`oz\[)vkbJ m>1hj xP r@: [҅?ږG^#MkZc}7v^:7T着YzR84=5-@e>qa fcgikkE`bUt+\*E`V OR1 e\HޛX3O2~àg}=vb ,F 7cm<[ƫ?<3@=yF*@a4@$mMC ͗}*v!R'iIg;U]d1*}&|WtqI`<*ӡ;՞Ƀ&$'<u eu Um 3jm!zɈ T'@JX몃0{v'Qe!Fp N>#0sӎmylMzďީXIj֩wde-aspzUhLj*=ͮ;~x3mdiI\uN# H~0H;bϺ9>:|vJ@/~%_m /Dw7<| ߵsĜK (? aSr@tP9SK$z%g0 ݅33x`4A ?f~ئw-}>5mӂ,p#YӬ zxפ.tؾ5; 2\.Tu}E&(tbǀupx5ǣq$^Yvt7L̥!C5})ػ]'+"%3 7ՆvFT ω3bZLT7N>!զ[Ot91<#(-i9-YwZTI93bmR&xKlʢ8vDb޷ȀvQw`B,@6{nPQXޥ&P\N1HyM7вҌk Fln 87]Iթ㦙gy! oecv8M=3 +uFrHi!V3e2G%8#56 vŐOCg~[9YsERxp\<_ZߠsUK[WA`,=YÃ]. 2+dikõ;' ǓFkq娯C]3._NߗG_9tpHLH[9txV=ST#{jk_ *i(8 7 p٣wcoe0HR3x6^*3ϑ!c_3^ϗぼ#.9j|C5!#ly,!7]lH;8yAgZ,=l@O$4wEݔUA4cveMgsj{ʔtf0 E`shB4D`3[?[4o_+(_ e>?3/aCs|nA/_049v4AY[4Ai w` ^zM4iK߷A__yЗtY"พ~ŋeم10"ʾa>)(+jϼ#d")onǎ9 A'a"t/oH"bc;p$Gu=y||~˃k?|2V[jbPeaН{ *RFV$dsb뮬"YsZVN*1;?]P;F`P*oo-M\?cڜw{⩠;s;.y>/j 9F2 +7KPw;Ĵ}3#3YZ2o݃Z.u3ƴr;1}70}ī< x~9dMN(,1$3@OcfKF UT,h\,+?C*"5T)]! sKOGMr埩mVHC\Hd-L|^+Bh\_$cDKNSD M+"?Z9&𛸡uo|O8}034tE.5G!=/C93:Sdro@V`nUqa|kY.,V.0kVbJ;~׈Rupj g:M(]r@*֯ain&vtxDB |Q{w驉OWf֧#:Fq4]c7a3FG: sGrԊ{]][kJ :xޣ7j%Su(Ũ`LR 3JyVv-5+J>vrv4NٹpX1z > Y>mVKk~v#(c;{+( 1q8KdӬ{rM !L u u\);_:qm0KwpB+J>tZq<2~@~'ÌOtĽ9aTc7_[> qlܒr~0wlHmi(IN*X|i7eo&8Nt@$O2B' sp X#5vbf桛Fq)곉udDG&RV龮1Or8,ӆ6w\xO 2j8F*4fI ?&q_87nK+!.`Y[j[׃L:ޞ+ eL K&s5;vaW,vz, c'3.;H =Xk."ew[{ Awp^ ќI<ޒyW:1 du,X0z"]E-}/P: #f`Ҧ؀*ui!Kv36q660k|uE  )s_7OEviVgzJ 2?X\X}nK=s] 9? tL貆]E}n'L?CMԟ%4p1곫,c |Xa*lc[ +UWNpaJm҈mSJn'> Au, ¡퇏3eh&l{ҏI AtFV4dU+LZ%3r&[!ըCL"3lP;EvSDR~כ&b6bfY,;+'h]%S zr㐴uz(Ҍ6RΒ)3}*{^]h/X|ZQdVwkt9)6Rxk܄k-$ӸM@"pQ+,yjC kZ8ܻ5UtE ]TmG%ڗW贓m~pS> 8/aϋVyS%'{dq<}vuh3OӴZibǽ,ǠgodGsw$$E&]B_ʻh<>E.;0nMkk +9r ҁbm"%_+~ r{:`+|3 {3%ea(s A# ӌ԰_6PiWR۬.{7K#!H!Cm8~$:pec:-dԚ5ڿ-5:%v"w=L9p܎8Ow~ Ta1A+e38\%Pzeh7SWtWeq/dZ wTy-YA6xǻLvFGg"(ĮcaEh[B:]b&V N&{*wUD;`)d74=nM Ɍ\I|S:HHQQEzX9c钼+F)={&(ᄄ'c%c)2<<;#a&A){>N5XP9/~ WXXNo`/%HkɁ gx]z.^8a8 G#E:Q3fG>Fjg~g.yTV /Xә5dHBRπLaxJ{HWGK蠠vR/>U'QZcD#Q=Pf{gylcTF_AM&/q BDy  ǒ\w§'Ft$( wcn tRW|O:ـ{s1ډǎqΘ/#-@4w =GW\Rj6#S 9N|S1)vi]HS$I aJs$ *2l:W@9 1A^eLG2/{ N$ڞR^ّD=3.*9zꦊd4tYG + IJNWx|Bx5í qbQ[4c*r0T% E 8ޱb~q+6a mQS#^겝pўn*1cHX.{LS{4n0I3#K t ^"^!V:2kpNŞzu4>@|v0! zвskWmSNȏ,M˺ٖzZ*8AM8*c>kf##Q6& kdGa9h &'a ϙ#W} ]{HŽvfZP6SY(K #qlت> 8?~Vik6P->\Ó0wIY%:z_HӎAGd̗~dʚR 0]҅瞄='-/3czk+(v-wX2[t8 l7x@>>~ǘضݎBЎ97_,…lNLHW _X2xp.OV^b_y!]mGe#?,g3W(C3:~3-ʳɵ~\P@k9xbQce Hk぀pF92ojƸq&.ǭ: N $bw-vGuͪ G#/n*r;cXC:V7ɬ-iECenkSu?dF OYmv uߥs:qXMqSSmZ=oL婍̨v*KʛB ;m kGvֶ?TDĘUy1HP+HV<,`m'a&u"X4񜎭3H𭶂iec%#OG [ 6Ra-r1UKop'c1TSDw=$j Xhq̈́4]E)[L_ n݈W8Rn!W==2˯vtE+ *fϧJt0ZP[ֆ-qEÒ#A/Ć-Oh) 'afN؉==e#9˺VOƐ~ZFN{:>`l"[RNAc&mBW4uY%V*9_ HW6/l)'KJ )ֱx! L끟PLxN'^yz2& C 񀡯DW+dՖfiFc=wq[4uYo<.aXkdOiA3޴¸u f9NNfZΆASj<ЧiEQ-Ô4N"ݖ{pR:g}Xn=#Ww'`2; 1> +=J庡Q/A7frIJ$44.;d喝z!Dh=c ª' CQFR!m90#=僡r*&($xA%v~Ij8jM]V%P,V2î&̓lgA/.Zye6ΦΔ<3;'S ;41P[puu2 ڶvambo0[ݜx0?߾xOt~}W.{>|s)/5㯗Wo\ܜEG pV7}O<_ez5Sߋ6^--#h`&|DgXFXCw{PZgHiKX>r]uzMNwWo.oIlcKmPV=bBNДIQr]X%P³%LMhDB#qrDRN_6` $MioCo'ƶIFbٌXnvlk=2\ ;|Hn$ab;]mGM:S?Y:y t湿ힾ_W`Lfȴ@25v[yŦBOܐM?FY;*Cg]Qtʜ%]B)͸:Lgew o̤ "6a œKN?rsϭSM\eη}NMszk ږ @*Yz9/YW^(QH+@USVܚpmJ ΈeIs]Hgܒↇ~\_S//b[SʋPOucR;7;a. 2*܇0ϗgY9W9S`! ^i6$ }'=.4 jE2%쌲_9|xwG1,^k勢̋66dGjd.$aNlG MZ;XߛKgn9~Y$>ZH%L| ֜IB@)QO4澟4TCJ rq.*5{֋gpw5P/:,?Pv^РdP7f6$N > 0ev3HewJ~9 UO>aBsMH p,,I4k0S+/*|Qf(u3X>)Gٕ Q+Lwc_³uUB{"{~+,OK42m U"zTzܑmDV[7DJ9FÃoח>^T l$AXY"|l}N{̨Cc' b.xKkGwtyN)9\27au7ͧQxш/ݍ6*TBՖ4[R*XY|>g3L, mLU'1xLsi> /`JVS;cZ/ ]ǦaN:UxhVpZh3.fvtX7hy3'px:@ɘ:i;1ϡbG{wItZ *bMMiz~?aTZU>Ѫ>s.ޝ[x/âÉ+bmvl-6I ?AJLd-:p]vE/b~WW"VALJe PBxǕ%bEBRJr ob,/_oUy. Ov(+<: M0LU<^j$݆McWwWXC6I#[["W 0 ;&D|]pby=,Q>мhV$Vbu=D$̾=HoE%6rA]o>բm@5#M"\6 wI|ۛ}}5u^V`G"0 Tx)1ق y njY$$:Am=s0:# bZa R+La6L!\aAmbl?MOYr4S\:,H(jpK@K]Vѵwcc6sycQNL3wD/86 C-8F*Z8n~{sqqw7ݔ0fh*%KM[!e; ֌sd4B%12[brɦD`5QLi$GښX*'4!|fkt.㹬*#HYݡmǢqn8K"Ӹzlb_@ؚ4Y7SJ!ME6 >|F؋3fZ/ k@ܲ5{j(}v 61< G,OVtl!.S8".^a WZPHWփss2FJ);u)YS Ҿ5LL)ZM~t:.fE܀)YR TLDHbcUZ5 &C'kES.)M:tv&iŃG#x? ~$T!$V8˺! zȶhl&)IIӖ6xBh,Ww*zBzGm-,i~Zm픀 ©fjK)SM|gwnޖj_e7$p[iV5=omV_ttpv\Om=$/[εoGmOVuwǝ\fK7Y25ܶVgw0511L%\XCM ӥ;#\n8EOdmzT-Rjd1g7Ճ.팸Ȑ{&ޮDt(Yv_ܥ%}OIWakh{Y(o]?,g3lᤁ=pNi# 7$:;3;$smjZ8HD2SCX|hk,mLea1μ7%F~Ny,Լgׯ3U>I k@1-*=Qb GmƔK7 K-tMACxR5O)^8|'>m5*%1hD9b8A]UExL'&#٦Xv|n\O1=%$#" 8J<魨2}4sA(Mȯ:ʋo?R@\z "~ iVddH? G /Kvj>ߕ˻rx, 2,Me> $ =$(|Cb%kĺ|5P՜MjK|AmnD:d[mx.|Z@ 5pf[c`SUEx,|CvcH^3VkIs7Nu4/ţ~քw'u x{W2Po#_MOP!STAt񌽆E" lOn_ 2gr _S“x*)%Ʒ3ߖ٫ c။T+I8QY]yE!qaY`'j>BIrRqmt _T_3$z׫ٳaXlqa8k$N߆8!X!7[T\RUMN}tvynt1 &'nfMq b+(cXA&8İ]gw85V =Ԑ Vh.pAH[UTjy5 FAqUjW.b83p1B\N]T'ȌlUgK_JFڕ׿97q2 ra"PO1eW%e[S=y}2,݄AT٤{&ﭶy-▐Ʀ}q7h<(FCK!gt >C0t-q4ʔfG;/1wh7ՏĒ0FIBw_mɞ,5Ήccݳ'S`R(STWW>o_\HuvK.E]]p|Ӗ[]8AQl "xnjEّ~eVϥ'7 fAž\ۈA>2s]Ɇ#GA/ EchXD׃о'0q7S2֎ȳHw[׌sGDj麯/&"}f&EvH3<qQT||[sèpKDI"Pc A)D>yCPNI =`(\m/6Q_HVGY}ן њYIZ{o؂mns,0Cd]g%^y+e4ӂ]]_st?' 8kRZ^a؇*:%a13x cSV&B}[l0q AX5zN擛#sv́\ߗUMDh{Q"Rp.x܅nVyB=.R>VE`nRzc;S!D2H)<,7[.…]Z7dRPqxhYQ:RQ N`ڂنS6^M 3+!M')uOE";/q%NHy?̹ƏU6)' .ZE*5Nu&қAm:eFe!:U GW>Q D| Q/lq̭?'&OGkpJJT1_Hj^tł*S͆nWB3`MwEmr#_\RNE@7CZ\Sa7 W-)y ;ۗq㐊-ZT,_O 4L /w ?4yj RMʹ2G9K,)ӟg'Y: {\Z8F~GfF8c%u>j X . Q] \ *vKnp-̟o\*4i5ɵB+` >[9\~(qn!̸aQ( [W9gՐjNqvRbDE3)6,P]?C\5j:aCI\,7󕝟\`(8oЕ% A 7\jY{ȵ=>QXQм|6=}D8GQV-k);ȋTS5܋AuF ѨIOja0mlݖ].qDIh uqz΂ǵeFΫK[i~הФ@J H$v>c }%>*IX~a@ŀIqM CE,"Ɨk*4ntLSP-)tfCSunϬdа ^\MP"7o:wb1@ƌH`R#9Zà dmS0 ~+;3cFT SgE%sR *>ux,֖J43_FWx˘( I_e~X 'nY CjvKwWv2h 2/abk[C_?lSZc`P ۫/';7%@A &\1h5JSכnyT{N7hEErXMRj]LE2AUdmokOUGmqYkm:n?w*yCF.UWmadE6t (7n͖Oo!9uM#k?9OEHv`ޥb7k`IJCվaU'3aؓd;E̢1r fwC#Y˴ )a#^h~(FUdґo~`ْK(| lOwHt+[hڊlU~?')jL"462)13Ң'<_{簒ϵ)WM/y7/ Gu-a9DTƮo<7FA!dfnw(-;Q*{ז?_:&f߳_w~^o̅K"{YbӌL&[|wݼ}w?~vUVh{+vL/*x*RAÆaU1آu qUm=m#8t0[ciWeT=iSW9l{}FۈeԸO{'ZԔ_ypp釮6FA1LɗBxqbio/i'A=8_N*F wZ| 2_^ -=ߴ?&+ee;H*؞jBVs*2u^;lOI(K7ܵd)O:әs]kz!M0dO܍sZW#~] .Ӥ钑M妼%nR*Sp(fBYHesW͝|yLr$5@ nBӌ"'ThQ }FacDIyT VK/%(.N3lRxpOƼz@.NWdoGN6\ޕ# ,ȋ΃fDI˺yw04x09"X+P[^)nv6C\]u-2wuȖun){Jpar5Uy-Pfʉy-C3;FچnHz%C4X:R 5b;N1VB"q\w}yU\8X]j % ?~6߱dVFKb,ƋaHuمz,&![E  /YC`@&xv8Ac)!3a6xbCmX9P —E>;X&N|R,=MF<߮B Je.($7yDNz Fb;ll|ϳE(V>D(~Q´KEz]x {a?|k]P9V5#\qض3MMR2OvA0-6Zm T[Pa`[0qjGN fhvnZf{Ь:;Ь9{_H%l';QǚxorՃ|"p}׷qżtܒO w K/9kmHtr2Բ ĒYg:l}Tl2|*-9 @=܁/P$*. R[^~D_OPx"QE+l([rGh{jQH, / 07Ҁz jUa 6jymk<*67v WNq܉%r`lp50pX0HQwVx@^ &~%nSы be hx3d<#=6AN\c#}/3S@:.i2a)eX =lX#.)Hxޱᤏ53m#N{(02;/] Ws.RZCBGrpgy~g҂0&.p3ꎖUS-,lOtkȮ֓z7Sa?z{p?pGOkỿh-ءur jB"-@L|#v,qQVJu2_d3O9gZ9i_ ӱsh29++.sx_=ٔ;q| :2iР"0Dy8NbSEzJ/KZbqaxOs.Vq`m@4-}{ 2 ܨgFt.?b[΁u@r`!)?_IaN+-ƒVs,!癪,@3erOaPl^P28 A1~{5K & <::p)o e>.$\H zqE+8R J o bW2Dn̦Hҵ?qɦw\yⅭE){@| 5׻*{A;/#\~%t`*vGqWOރr\zAPg˘)zAqeK(e-Y(E-#[-I; kYWN5[mڭ7yP&zD~2;'.NAA|]v qr*Ġ˪ݩML Ն<[0wV;=l< ;$ yۑqSm;i.!w.½/3,'{ JrQ.B<}|Y$WΛlT!R\8TDNJ :Fks`նxZxz 94'k֘KFQhS{XEg}%>N^4zzC_I,'ETQ֥x"E hhs#%U=%|tԎ}I 4Ɔm\E7=j8U@_)eRd؞;Jy T_0@kr3Q6m|5M'&M}0ȡ &b$j7froFUT%Q!Ȓ@^¡`7hN(yVFc`~s.E½Syʄn Iq/6$_oo)DؒZƢ5n~v$Gp^Wt:|i 3 ILɎ/B*A}U#?u>ZQGJ眞w~x?_ts6 L??{oZ~۝~yǟ{OW_?zk{XY dy 94;֣d_*☶a k>ӓ774{*zVǚwx5i9oTGSp '0NZp _,avcb\Ct{qJy5s>:_c'Db9mL:[vS }[ȱ.G8c՗}iM 44uO\I-gq}M qKcjgIS7nj_Зkd3izһ>R>Ԡ;n諭jVscm -rB3=5ǝl7c)sJ`-&p[ ta,ǑGH1Ҹwc)Ƨt63gHt{mKDJ^ܠ?nwkJr_ wX; 62Rp ͒8/g$Q"ᠴC\MO?37q^DU1$}|-@Ord'[fx%w@brU@-@ig˴7BnGgo9āCrMw}e:IS[ɊPɻ"v+1?pLG| V%h (_ḿI5Ah۟xi85$F`齹_g/_nd;# ,0s@q]?E)".}@ Ϯ%B[Ȓyr zZ-T)'Ar=mPܔ vHsK'?(0)p.1(N/{gw֊dͶQǔ36l2;RX(tS{.}t=p]:{rMRfsgUg-NG4=  ˸6&M .h-$, iB¢a $|.p >J=xkb- (C/%2z(耇y-\Ix ;G$|5wH8[ֺ0/ܽS02Ys?6Tזb(mvaϵy |vwX|j|G% M Cә҇k"mYؓ]/(LSr)H`h_%OT-"9$Ves-/j!+Pi Mr%u^41I]ˌnWdZ(Fa'lrC AEgD0f< 񂔡 ;qvm$JJ8=dJ|_AlM &lx=NP\̲t9P_Ҫڱ,YQ;eg!֪^v{GY 9*M|/!iE]G)3r U3qtdEE^X~{^P&BUFc7Q&vRɭGIk1 C17{6~̓2Z]ƽSy^yի^i  IfiUjs#j*2DwYɵҌC%mߠ$-~o5B 4=+/4$c]5x\@+x1-P4?SPAdk~}m}boqfҨoV[nush=ޑ\VaOu6ݯgtS鏣?STݕ? !T|_;hk;j ,̌17ׇ}soZل]9ʨ5lcN\|MzӵvOo g1Цh.RWg r[ Z ![=qh'/egp&IZ]|l⦫IvܬΖs#m 5z8.>,ྍoync SiFwRiSrܬ ֳ}^l =g+q!KnJ+ @p?BJBjx^뢠e<0ײ~ `.cCkϷ[oɦE6уmnY 4"fԉGGKuI=Qy0-"L$3e>"IpM>PЭTxeiXΫ"f9;^qG^UHcyiychq$G$;(Xw$O$Ln{榲kX_pdhz^.^)isW)TjqQ':P% UC\mq&>M; 46sc8…h 0"̵wMJ ZqmR?gj T$![gӮM݈ ٹݖo]܉ǐ%]%̞  :r~'F~H}̓p9]yԋ@6~;c,aV] (6{}Q$CrS֏i&[2\XC:*6䁇* qQjA@i&=&ԟ\JV~)^f;O!9J,~m)OEQ0 $<$Xeu.r$sMrTh>f]_>jrM3[$J4\u-<sז|, Pda>k^qC^s>lQ(_DPYFAdr㥇B%Ӎ;+W D%v sOXލ{n;*ƽP5bʜOIK4(Ͻ3 mY89<ϗiW=s4@)Z=#r),$eܕB:8/LgcߌIEA4߾T;eqNydNy<UTŠZ /c4XofT<;DFew-XO?!&5@v e(&˺\Pf̵N %ŚS(c1 H3#ExEfk?8qt9q.r#it;qr&d-|ʝ [=26Gx ϰ; c7Bn!5Y4<(Nfn9ԑ7 }X׀K/ʨp1t4ilt0|B [ ndnA( Fre9 [hW>g:=4Ѣj.\C*{bϹ:Hf}ʦc,vzI~kty8ɦ57>]ͦLʝQMe:]8 "f&I!.#fjDuF#6Fkǧ0+OR8^+Z9ovBL(pBwfdOL?sń-lKΪh2.l24ƚ}pG̉^ vlmC9J(@[?(ZgShi,;ւ,5ZkXsZ<O7 XCI/i&$bs3ơg>6mE13ܕr1m fSZx]M K\T1o6@e?VpL4 ؞Xt嶬]SP8ր)'-z w8zHZ` C57G`B ^w%'q8Cf3%iFIе`٣8%;1r!G&,@qT{I^==әaO/%f`{|1[/a/Bot(J݁w -XΒ'/dp[-^]]`8QvO3^SaHe([Da"LZAQ& ۽wFCY١o=Ffp7;2i8]>FQ/ww_Z;[y=nH+ WႱn8 c9~Rq:(u|K`b=ƀ,ZcӰ(H+}0!Au+GAl!. H鲭G3\ ]]6}_(R][gr$X-uH)힚? C"RX-AxuqLc"}Jh/!3CL렌;Z?Fp;ԛ6[إWRsNy8* YBǤ>* #373>| VsH, G +-O~RyY)#_GԄvD^^eTG5gZԔǒx5}ݥ%Bν{ L5ZwXhz{KanpE^,I٣]Vb,oPͫ/xOD4kN:G{=3\ϓyYgDtx؄33<>'2O±F8!kؓGV[)];?qj]BqKxQl$̖')+6Q-|\Xm*VG5߬e;;$K9:b{%mj }pya'bųȡXbD~WjW?fI+ u vgH@En#3]`u ӵ_ޯߙʄGhr;uafX|iŪU*Yރ#2b.MWp8;j;Vl(*CJQ(iUJq.<( 4uVxurjg >JyJڻ5`U3AJ&By돡d@%TkCm|i._/I=\@aL_瞽^L%,íIcY;W CiU Fn^y(E_eAaj[&V/2xI9kcQ9GMDgޥ_<{2˲qVO:Ti'h 2V>jVإ⁥0 Xmv[i~5<%&3N&0ǀQlVYL|#{1v#vbW)Ad7$"֜0fNN>=/.f}"|!~TEO߼sQNb9'*YOQ}ZssԿ'AuVJ!N^n-m7Xo!RZ#ќA g,PxN :wo#/?#s8`Ԕȫw7o_Xt֤M%Pdrhea] 8!$me֎Tn0nttp@v2ԭ2{$ߗ -Ljukl^/P ËC:/<  %S?Uo9PR 2T/|!![1ppp<ON2_"&tc=#8`Qrz٤CxGu.mQB9kR޵;;X8IݒCUbG*bUUFєѹvMR_tTp{]v*=ǂ'p=8i'إ, GmMOzۊHy!yyI.>!PI8r;=?A~NalX0Bh:͂G,SReeʍB]3Z-D0:iN[N#iJ" PZF(2腸d[Zt0\2Q6 =I<3'@? /7Ә~R&'1'Ldl>rtDr7À6|FP M }濋쁭?WtCmyT,YCq0 $ a-2ֻ} Iqb z m)I~ۃKٛOn q1X >`q*~*I$37|VZK^S@$ Vޱ{#>`La̸;! Q%V +7GSl' ~+3M7؇tR R0Չsn-fyW3cZ+]|F QQ2&{v?@9#+, 섐ɔb[slU^fIۤW^D*nfe/UgDq]v-HOf f_?>yo9)1 |sPXE|ToZFЮ3}FyR1}%cb?)¥eeTBN)q|`p[G,Ln3UYs8I]-Jʚ8}7gwN8j%܎F^@? );ּ9Yn((i_{SzHa', DX*5(^bEpŽT76"*괹Bf/ΌM&od6BOAknf8{ C73(h$' +jH)v%gw2!iT QDmX~۴eVDgIn;^ipz1 J|y U(I%n`Y =!_nETYqIz:+S;X [6t ~5aqMQ]jGN[ېDki z%vQ~#{Tp2-P|+˘P5.'2H2{(H̋^grElE!~@3`#aL( Jw7H`kET,{9`To! d*өEq݀,bc: l{2ne,Jmr dxmk{vqonw6KIGn;9m#Wf唻in?kUsqҏ5VA^5}89B/8ָzaJ;:E8{~&\V !Y\ 6XmG . (=9sZ5*++W~]rEJlM8X #{X>W$6IUljZŮ@6]dAhj,b6PG{ K;S9M3a s 1pprp[_ FQ9Eԓ@lOnJ,`@#$_J'AY;^<(aP k`][:5sotO$!I2Fa9 q+) 瀲lgߡ yV%6:dqr¢ pDBN"Z~+pNH2=AF25EACVY:8+ɰk ֠^XަlZ?{ɏqڦ O:li\E0N ӱ4 _g?,\}HWD E' s/cbZ: "Vzr!4a6 @f]IL@[:K`c\!,ۍn#uhc4 )K2ov!ި9yge.< BI

5{b )T'(va5=A[W%4F Zu_U6eEG.0^R{ ij}twyZ/v'VeFHoe-V52 [EiM6k.rd_>ՐHIZڳvIQmXNᾲKĦہE3$l@k0;&gXCjnSP}~'+6_G'=<@]ݫ7QO(Q%/o~a9,b §|o~~{<*؊uRI%f.%Λ ;eW# );D6UgDZF&ͳխlkЇ -E\-:7,\Q꣈B1DAt}ˠ%ڐWzY^l('n[sdL`vT}Z#" 1Ѩ%ߟG̥W럦Y Q5nAlˊ1:/-`-Y7[c&H) f8KB%ծ1j9Iljr+=W !\19s"J2k;w@L͈Gc(EZge4Ml|#cPnl ɘ @kg*_=o>j rFfN%Zǭa㧭 & R Ażc>VԑF{U.L8#pN6"矘+r ;H !Ir=O59Y/09 -Þ¼\dqptcB%'m̸&{=AS1کB0zOmSjXQ FՉbfoF-* nh{,7H@U$f &ymsr'83ڜ0vMMMyឈ7FӨ/yi^}Y(۬^R< b (N]3<&aWo؃^즙'V:H(M͵cLd(>޶{ton@w(NH\{}A1M*ZĎt/m}zט}@ k|'n,ee^ސ@v.]{)^5Ra^Og& eHsf;!I=b%" %$a˿tB_fwPފ%!7(oX=E'/3,`l"_2H ,WUTED,{$eU<+ *=l͞5@nbWaDm`j*ž_( 7A{xč0߯uxXFFD5t ]3uRʃ=Iv$Kv' Z!bܱ*QCܛ+yQVOX03 Nނd[+ӎ-"jJzuSRTQ@JQ[@JQ٣}A;`JQc)E*mA-`JQ V6N)*dBRQ]);vRPh#;ؗ75ss->!Q=ejw݌/1 zp7ՃN_;Gm#^mq7v?Ul'دQm]P=~l=ӵɡ|}mp_A<&g89]#Q)I@$ k:\`L!5D&_3/^MW5 $V&ۀDSRz?6Y:n]@[)#_6Oo(uijX3W'E]ЖbgKM- ʮ@K.C9ԧ }ksƕwybŇxa[#m[1pxg A¬J7D"!̈ns;x]x!'ڲF hmr8Sd{ڔg&ɞ~5UNn[Շ&h`[֛1m 0y4<êKh@$rtdon$촥#]ՀzWApA*jcԯf!80S!?3߃EΏ}cࡂDbujW7G2bs&hjc=%F5⼖TNr us/5O/[C[I!/>iFgyRb1;5rC5 hM}.t^0NIFa.gH(62L'qM4վm6,<3tQz~FcX{ҦG=ffr\Gy}m4w|D}>/>VS`*/n >IDF4]&~e|>CeXk[J-^mnx^v-E/_q :"U!"M՟~)MioŒs&q'Ns#@|1KOkP,KWw GJ Iä 褱󁸷o"iNl(LDW"Mb#%J8CHhch6Nc37AsVL@/H°kE$؀w2o;]j7De1u<1  IIYF?j|-ø.E'7M&Դ$@hEٮBq(?YH%P͚BU+PȫqBO}Ǐ;htp=TT3OGm*w)Hq,${s<PD+Fą#1@*hVב՜>15m}n<&vĴ'@chb݊x8!$z_ܻu{ߵX pNmӖZꐵ%覒Dpw+y;W[]_|*-P3%E}} ˭7ݖpw3z0nvDfRܘu 99u-ʈzieV7#.fw:m EVňN܋-sᾊ5-Q́|;'PF| Ziu.[baJY3`shyʜUG«\$)տwߞхؑ^My$p'΢,a17Jࠣ4sMRe)WsVt/uNK;q];49Ò=6!Nަ#m&oFC/q<;15ģצkJ;Ptl:x2'aXq?IwEڝ!v8** KOG{a/1&3: i[u[QB4"/K mKXO,/ݎts+Vtxd7޲plAڦ$yr< fq':u_UY_!lȎfWM u΍gS7k '=;D(oT~^qK9ϺB{1Z4Аư+$fa?<4,R7t^]n~qy_AAȭS(?GT%߳vw:{ZuZoA \%HP5?>pa~M̰$lvyPDg|[+ɜLkÀ :Hj4eo}on7S6~5aT=54 $S,)?5t!5,8Yix(ԣ'@ENDr8^o LM@?8in KOܞ=`%Vj,a%aa{r'gLc[{ ܀:q֝e(-n 6jTfsxeC G+E 5@G{)3p:┢C0)4 HJU@ ȃH/:P"{e$9u-];Rv/4ka PltsFJ.6LƳQL1ӏ YaMQ.,bFK Z Qe( i(KpoI{:y컔/>1NF!4M;^Pd%MVn`R1h>];{c}`;Pf[_}/z`]72qD}L& :FGcґPaڠGw&y\֕1>-(^wu:~*6^a n1fWCWظt1AS ĉmg4esGt3( c#ؑޟ#̣Lޡ/V^Y>:,lO@`HA}oMղ9]2$d@UCW>c#?3qkJA s `\r;e*|D0ä FR! 7K_0T!fR$Re`M ~81[1Rj.ea Յ0Ѣj-0E,:xKQ&o=<߈gClY~M*YN`8IxAt+r_~_7O@m恐j9I0z?֐Dz_IeNk%D޼l15BL>{5xYa^DcIoV3B&osH*])U | -Z=k0q[$cO (YAC?cc&bJ6HHTt0X"-j(dwBc,K]h~7aH?O\o?й"Ā[Oh Gkwr+#OJN/ƐM =? > V'qjP:<'+96pLV?/+9/G՗/)G P2#5YECcTdYrȨdw ^-z[-I/7;NGy~3#g2iJg{89Nեůzoմme!$0ՔmXe,zt; ʻe;R݃RHg3O?Txy?mX kP|q4[夔j$t|w㫹RhZ^e&%(C!{䑱"RXlv!+}39^,*\=|}6n'YdI'Myaozw욀2 Ъ΀>j2ڵ~N.CQ  pIdaY߼ t"rٞ?40JZe,6L^~'_4$u\{yKekbyEvÏ.r|&|lIC] g%;u^VRUաK"JNVٜd6'h!`a(ɯ1 S@7Tm!p҈j`aFK%MXCl8n_8 ?oTzQrAOڲm{z1zrƈg܎EyȧL=OinIgM~3c{);Z9hE}s &|IoHsvژxaq|I &/!o?6^89ny]wc {pjpٓ;ӣ[e0. vIDzѣ&G×3vP{j'mpOj1SI<3G}ْ8ȱgbJ0r jYhM0CHWI SѱZ$X&l.2g6 Ӽa*'zPX~FcOZ(54Z##X%Uŧ%ufmiP~Qytu {cƬD2'$!r[/f4iJޱ)h֫뾸rsԴC.˙~ 9 ' ]<}4ÙobghNޤkh",C¼;r&CS h\_[U2>ؐ䘇x/hz=UKD8-?χhN]9iH4PSp1FTɭ>^찻$>"dI9C^u4.vdljgS'ZNh񕌷ԴF#cx"eytniƬD@O|x>6;Lϐ<xa/3zqdM$iwFA2lnne-wZc^2 sqD]{\AwiVV'44?2qoaEAQƩrц72I]\:Ԧ`9}9eayx}ʦY.6f.ȷ.xOf_ׄGGXS,L7 Kv>< 5=q?=ĉcsuSJHD3; f~pxL믬$BQؿOޅzz}9a)۴%O3}}a|.:| 9(0Ȓ^x!~44N.JK&q%9鐜 蒜 G uz{uHN#$'C#81۳8G.`( NLvdx+0ﴭ Ǧs1 B |UF u,ʍU'^'4k}y8Undg|M'`j{eV+lPɨvWfhwihGr Վ7PckFZ0g@/U KƺppV<fӈ$w #uÎ~zCkwuX gnJM `|$ n~ARy9P7]>VEʮ](VX{V3{a9]ƙv*I $, ͹o}1l=֤)Hw++þHp֑3[kcצCHgzɉ%8n,^|^ЧFb\?n=nw]k60\Ӑ,MI93 ]t١7%/ +`V(\\ύΔy0<0։N{{N?oŞj%]ZznWN|,bg$drFb#+Ϭg:6I"Pak˗n2l—ju^zk ċc6  Ѹq:k2(S0Irw/=s ggGkĂbSGY,;__ohƯF$Q_O&Ovc "y(st"e8L" szYs^49CUZow#vA1%!)-@wbxYH-'瀢~̈́MM&u& w' >Y}CG"/I˳24Ӗm e|):F:jA(!/$)5i`(+4R;o"'(Z_GA6z:'ٹֹ)ydƀ, 6{cM&)yg&ܮ8c%AO1\We2MPǕ&Czlr[t͓4Yqs^t!eW0'+,QKOEf9$02{d1ohRz,L;ʷ -*B ]g$B>+p5I2~>n2E Nv>:J5v{8BgߵGj"D"(a8iPmU9$ފ B20@QstT+w>1rbsJp g\{edh'6(3u-7:~9:3!jq7΢Zx톾G{< $UƲgvP[܎{M8[}"ZTDŝ#:\Q̲T$,kZ@{h25O^IX:S UsQ1BX+XNdZ/Ln0'w<]w񟛒~GzSv-`y6R(]Vz`b~g$W7鴢~( RB=j`zY.>_u>4M,XfBe,$⭿̷cd=2\^ƱT|/V+]| |*K6sNMX)O:,]ŠN YO`>szK'F e7p+&hPJלı=NDsFtYT½+X qs}`[(fk|~e1?5;;^"nw#(o?npSќmmuN&.̉㘦"94g"P˖/t̜\3%Nsvd{]s^ hr~NoAU[ՋAsnt/=Kˇ95$=nl,8BXNxI[uvPv/쾽-7_9Id'N;נ؟5<U0$F3Pqw,Iibߣ!un0'tӋ-jA-FpZGnv4/ܩ¹=,nWi]2?]K[/3pDvX{S[ 7ir^|[ v3ҐNpg} y#݌0`+~|c0SwKLsކ<5;`(.'|kNqHH{kAύ Os_N{*- KDt ;oUwRAG΄bM= T ?(B>nCSBPN:p!8 Y `>O%,L7(p7ϣ@醿}F8MoT E쒿-)*x)#fC%uxVj{<2Vt#2 )^E~x yIP۱ P%o >e`J e2!!TQ2:ypN8Gֹ'qƪ3:PVq;gDZ8Med@;ǻ0kY#=Z`͸J4}HOEgԍ.{bf*茶nhtr.knp5~*yD75wuܬy]a%N;-%H8u2t4xA4 T$,/1n-w>ˮjُwn#Tr'1BI/{gR4s /Hd&zD& pco2~Np]B; $~1ӻ7;FIq X$/m;z;w L60a /|ԓRÕ|m1_a$'cG6{r< {rG!A }32*qs [VucvD#qv8YTQ]2m"p qc;ݢlUO5U؊NAa׊7L:x@ -so_ftQMf94.^y&̓M _nUJ*n/ٻTZnwjC(#_$aDʔIϡ3&QשlM ƖhptG_RmϰRI+8ɖz=!K29B`u*|pXlrkWЋN]KD(ɓ[wqlY#LaqA쵴E,41>qY)p_mnK9[*JM tvSõۛWmj/b\_E`D?{a'ÿ-9GlN!97]=⇩O|7@<2o{ia5ȀF`y϶S;qo c%ڀJae#>ɍ։Sw"K9Ur%ZY:pv)9P5^Q Ry5QTX+ySڵ=pMa Xm(Ѩ{9DAm#;!Rm>6ڗoFZBH{g熕:lg翅h;QXA<Cu'#h9r=C,AvL?ƶl|gDY@3@%X q(W0πtta5twDE w  H*~ %Esӱ"&@mU17侗Akn0qz3dNs3#] V̟PD;w=ɭ=k~`\5D–?&CL+oGvm~D5Jlo5,2 ]' qA,2  8}ϵuI_~wﻔFP c^_;hڔ;%nZK_4~Q6QꝔi%?O\o^FMr 4ɏ쪱m5ܞ(N{b>в}wISx(nkR${G6fG %;1*15DéQmU 4V[;M}ǨgPqXqUc ?5F;%!޴%҉Q%{ܣ˶lr uǦfKKvIHw/hGSQ*)%)((Ԭ ~Fwbas(2~8ڟz@(dA+g(1vu" Ա-Y˗ZΚ:q9v@xh|/nX=-9^.̳( gf[/\j?43ԙ[񠯾u"l^/{J~&o؆6. tݘꪾ${Q-a#ͺ3,~}JdoY6'vȚX+I[deVvUb4w Rcvls+wH?Qj(yd7噇\ ͣO{c^ .ֵ X@H@Ѐ[Dh}AiP/T{juNVQNS[٢! :wJ)zh84DSF[;:Oi qH! shaQjz$'QXzH,^<'wN鴛Ypew c庇uU'bw 33[O%\`8O$m[CYIFKH2 O3ڠNf27?7Adq!|jhaQ'U) F1GDin?H H l)%qLMd|Рɀ@G+>n@}dPsPS ck?J t~LdIRd5Z&9ʲIހ0G.Ҫ! S4 %^;Xpgc@/eϘޭjxȃ#((&AaGvu0]Zm~"ipe˼?/@B($AvBPgƁ Ϧ,ذ3rJ5IC|j-.r簘~35RJ7Y!'6S##ҶJY:m'/0fVkJAvE r&EGlИu yï:N,M+B1 e?sK3 _pFzB8wT-,54†/aٶJi\cE'R1w旿[\F˂ӍV h,\VZR X 1"*z1Y?y(;g?;BW4?nn'0_l,FCϔOB=Q/x\CUX1ӮYh;0,sQSKjy+%KB{  C3Zq:p-fwZG-SţvHZɃ H#b7i_3#@t1M+0c7r!ge_}qGw^ɝ 0 =J6,+Ȑ-ۙVi7Zo=sBdgT l%1!/ndn8X[NK {AUz/`J{M F;mJjoVjjk6"3e%(sv#Mo;X/3~f}sʺy,DŽcBZ_iGqIkDx@j/1:TrG1Q%NmkT($ XZf^x*e ޕnO UEb|6_ܐA/ϐgN"CR>UYP. LεWY!XXM7];Խ'!~A!G@C!/n(a,ځpS,J]RUM{k*($ ֆ8g~g9`g# ъσ5530<..3J3? ȳ ozc}y_!մs|u9t # g<0 jUZŁQZ-{[_asE p@])q{8Ya|Vk[WLfzfUk!cybg ˲ F'6(3t@I4\v;B`z|Fˤ% 0fJn'Qzv`Euw<^ y :R7Օ.EN)֤zCҤ }Pmo}:Uve(n2ګ0 I֢޺SB~BL|Nz I՝[➹f4eF7]6w(#,_GpEIR0ab y?Oh mܽzM)tX' &'MpK1N rjwyq]Bh \frongFOGzʲ 3%c{ZdɉxA)aI4~0*?e+(W  |ٯԋ]W.š N Y!x{V?(%*)`%T iZ.w,I<ât%-%CNuM/_קWgujնek8WY4g~ vU@ǣ9.KO6:y di+:FjSͤ>sm<'K,z@Rɞ;cd]Fp1VxYeyy+ې~> 8 ;јRQ㾼I\'NlAc@'~6NCӞ34U[v(R49tr6Ǻ?bKxC\aʤQrㅆN%l=(^7L%^ejn\&T wDSc_qBG﫧/,(1Ak-ڌxB(a ?fs/`!_Haw Ǧi:*=a2=(0o]9 pqώhipMS͍v2;$u5l<2" ϯt\Cf%,a,l6e7oǶ~נkS*eIQIP_/n/β0#~9ubN84Y0b@<'ibp ^`Pϧ|GqxqŶfUBeܵp帡5wDjIA8Ŕ3׶FԊ wպɇn<޲W|To:Ga}#E d|kƮ+;2Hv#\]:^{rzOzgMM~¤; ۴&j5v%MGl3v )}ΜpxDVn?̯"j8 )̠Hnb$3BA (ݳ{Rǝ3x5V}V65T-<ʽ]xr Z,z!3pyk6֖WQS%N7;q9rʬ @!8!E#@z=Ϣ#:ԑYZ1ZHb33Tϸ`x[Lĕ.>μw-y@@eݠ\:. ßm  AHh"Q}DQ#k)!'-YU:gFO_}And;\vt(zM^Y(KﶫŏY/հwA~]ip 8ʼzU۪QDRxŹ$FW*OAiE=8$&2P̬l8En8S(`<1Ev! hڙ}=ϗ{faF(s&`/O#`/Q&1ʴd-`^gxD',P$T4g" * _`b|yh'ߓ t=8h3q>WܚUKy/j/7q1忄]=Ǒiʷ塠WVZޜykFi@0`P ydRp\_ U}-7~<ޱ9ǭ3dz5-x$>CquAW@=ڊk?iYl"^Rקná%8PPs K[4|xAdqMDMՃo7p<m0i.<SU"D{ wg}Zm]Ac CQ~S-Bf20jhĨ}=4#,$rȟzlOo~€OTkṃ4yx-<@B{ɍ> | פ: 9U Q,mUmגy,.4D S;nh_%Aķ<6eV5Tqc)% )u?laEr g>JoFDoDunD0 q"Q̙N(f^zs.)i]TЅRvM#B~{1cO(xy(aV6{((ϯZ=p PN r. >?tWw2N3+ſzg$ (FD肶ͅB6t[2XYT:-MTRJQ&m.ywS3dHZEܓ+R!axvUmO<`f^ȢUҁ)r5!Ko$N݂X\k.DF;Yr">njx)2L|iDrCX<͒ῥiv=JSyΑ]10Lc0Wp5\ f)i ,K2? m݌{[ꓳ-%2gj25|)e] 3z۽I0] ެR!N34 w ö ,'ahRƒX]  ؗm9xطV~_ʥ@{AH3Bt?gP>za9`Uܐ^{iZtV)4 rBuy]ښ'l˄eC@Vuv3LB'AC]1&7"}t ZQ Bb(M$sBp:c -;!=_G t:@6Am2\95H@0ȯaEB/9SŸ= !UML6W01, 8J='':6:1U#鮕neWI$j Mȯabu#l5,E.ҜF\z>6DD Ee{ݟ >/E h '|QS%ASf4ґ?`4?v[Fe5 k-+G ŃlZ-n~{޹Lr3ڣ#,Fp/#cdoYVХ qi4bIA<" JhދFkQm=J2M \}R)(I&8 pNI#B(Vﴪz+}u1aK4Y?  4iIW[R]kWK\7GMb"!u*nz8HPgzYyE/ܕк{ e3n;ot+9eصzpU\ȪnʊAlu')CjťЫ8gѳ"/R*)n ufj^#Q ͽL 4n6?J?;2A}ae?,CؐCdib[~/$5[+Itcv 5H?LSC\ċ^৴jt\<;޳d[ 4"~ f.x÷5Zvқw|Lˏ7ng$#a_@n|AǾy\x^1+CŕZ3~eD(&!@bvSu#qw3qe9qC`R'CYsꏝ/赐cwWE~D#*"ǭ#8*}݂ۚ5 57:h<c5Lǩj[ШIӔJ@E 8u8`=\.J\&p8h^A2P ؎nHm6L~6~lЭ+wEB C}:uBNkk?>f4 {0AqCⳜXX̹/Lk"pcNUrv YYܞ,òɲqa',A?>K,ƔeOyYD,Йf;сW7<2HV,y䔁gcb+wIg:`Zn36o4)'=7MDF8syR ͇wz d7y$d*,@=z%.32 u.` ?eÀy9EyP)VsFaOӊg o%oT鈢`0'[a:>Oo_k.B`} a>k<꧕Lֆ!ՃV W*S5IEA9O^ miNv̩.O6 IUgtՆb㙠vYݚ Yahji]b19.ڱ]tzL3bU/g7ꈊ˱׹n8f]ҊQF=L|_}ȍ㖽Kb 1 BCK{+`yVlKOti-`Yx`hn(|[FA{a>بȍcx/[c8gۄ;0Td'gn&PEey$UVnsIn#P/O1c2o,$Nl{Zfi=6hdeۀ+F=(Y!j5[G 1cؕ*78U4WZߊhY*rlMBX-9Xtc#×o돏<-xi|{Y[rQf2rpjCÞ4HZ -+JB3*eWm5j1gB##,ƫ01e-%hȇ}<^7P~`ډv$\(qSh}f[,;~끎XyЪ4_n}{H;;-l^şͷwJ'sL/s]UN2 LIqCH, ʉ.C< $ayom=,  < ,#I(R!6C ǢUyNk7ع8 ,&! tg[f$s^iac j\`> P4qCmEޞ>2ӢQWpVZ3WQq4XP\~ufbj6FCZ0zwxDg#g/.yB4E: ѕXt<2x7p#bX5 H6C0cU`506+a]-sWEmGDi^&v I(Ub\$>=e&~r[JZ&::oah$qyѥoPc&e!Zz`T.uv?5GqGq yze+~'FI 34{ ޥ)ץׯ>IE8%sOGrJv{XU] j΃V]_bK8]CbủZUih߼woǿ,'r~ FwbBTˋBed-?lmOƔY  ibR)ZgsiuSMrLk]+7Mk->dϜ!0Q"\ //9n,6X]7L\8 oЪLTo/OŁͭiJՎu ݵ57ncڇn)HIm6LVM;/[$h+Iv~ &<8nAup;|t ibهJ[r:=E400\*D h{RU?x/2- à-= g*a)=uoZ|=#uW*oGtuI~Z0%s8x6 :c3P7<2>ca@vFK~48 ]v|}.AC{R fJ^Fưh8JRl1] Uԯ99TVH=y f! M 2cP ޢ  P5S"rU! ZX SncJfemfFL! D3 bcO߉x~¾0Ric+TeXyx 9t{{߇$ˋڽ )(DHAMl!qĦֻZ"7ϔA{ku4 {bJ(N(d.>ɵQZpl=` YAͧW;>λ'|]Ikφ>Tf<͛ ?zi#lzENݤ@ǜ`PF0Gs8>c^F`B!,i" $-GK*1?hPd!$ B}Ǯ+VR,?IIȄ|՗y) # \3h濵v۲@v#V,j\#}ph P}ЀC7\ `p~^? l"XM hm&shK pi/ۄ%Eq N; vvh{8"ίw,M&OWuAD$Wl)#.5!P)C~*ۖL,?@ ?%Z`G!kQ B*GJ\7\̉^Xy薊_"cn,X1EWb 4LVn_\-W94[k[$Ndzoi;ᘭi]$0klhcv5V֞=L 4{Zem&c@@=+sLYNiĥDG:'==ԳNbrW)ی:~Ag Cѻ8.g-/T$24}^ [ Saf͡o=¬YOsFB+s(Jy !@ć;z~3"k˶\Q$}nOpOcF ׻uʂA…e^BvAZQav\q {o\t,HS4t;!I"ʁB۸ŲxZhacH[Sv е ØIq4wZ嚃{%E~-`|/4DbVx&nZdPNz^W/Q~oA:&5 h`Kw<HdTqr_B]ny83YZ/VUTU_f)0)K%^!,tǸgo+1 bGTKtIa{y&6D @곀fO% t\} }D)}G;n}~̈XdeZ1<+ y:dؽESrp*O5]|XdqM1:|)vX`oڑ6fc+;8%&eI4NۙgP~ρ(@NG*~\fʺUr^Mc{5%8p 9fD?R #vGɺ^aclAgUtz#.ؑg"J {P/2B_S?%+r@40$ idvTdIF^hN6S JӁ KBzx0u%B$0kOܔޱhm`\:+ut_L!1x !4oWi ǍlT-#\ D npQoiv)YxѯP܋(mik_ Cgܗ۹.f RrY-fJ̈ƶ P}6'Y\ҥۢ}kJv{j]նCyoeq#lp]t xYPfcBlg9jBTE[Wn̚hnQ$X*qJXzP"2V!PwEl=|I9~q -qbnpk@A߽c@`!*9U4ǭX14U: ۉ%ea2zEg#@tٙZL!^6ԶhL)B _u==pQEL'3ͣo1i }J4hEɑGyi.Ѳ,}!)"R ~ן~ܾFOe%E"iZCК11ϵqH:HgUd6+ǘB'L>@'RSLj'97 G6 ;Fvf;Hm(3|1m;[)|yyh:m/o˘܃M[W{gj%\~#hɂ}z{S*[Vu"  j7|s~Tq~g^cj/>2A* cYOEsυ>`f&l)C]jc`s:A[~?ݼbUաIځǃNăA/ 1 XuJ-q셡 %xP?Vzy3XVliknV 2xk.b~#S+ϯ#H 0`H:֒(i/wlƂ '+A2>+j$I fzn=11~ JHP[P4=`=(i# n(c)|{ұqтVbe)G|ja ca +O@s@ 3nT 3z=3z;3oUS}|#4qἣ4ݑQI"FBdz܇/&)^h6w*FqK1Jb='JAOXm˒%6g}'7g5,KMoDW|_ ]^] w8t<+wV^Ȯµ}'g{UMĞU˴ڟQ]HUqCNA(3lt3ϘI~m`E~4JgHk{[Q2cڱ? U6 wrHFu rDSߍhBN۽ z B$: z} C~l!϶R7jkP>6hi{ "B8\ y%o,-tV #ڙr/{1s'"rVs1b;7;}_ٻa}TҪq٭v7LcX8@d\pa{ ؞ `zTS:3]!v7#Aɭc'4$z;27y1_X9PwSoi1b>HǺ| +'t =3'5fpmA#'PU.㮝#Qt{؀h& k{E=IF1r6cdlz%oZL쳰Fr;;*\"\2T^#XS Mssq[c ]Gm4"A+ܧ8xQ z56ƾ@$켐87h7 '|W{$8`n۾УB`l "/fM{a%*H@ %buڸ}cM̲͑瘄n89ΚZ+t%vǬ2-[g߷rZѻ 5ú/^cLqPLgLtS LΛr2\oGA/ :F$&9 6 -a~1}AmшɪN:$A#fTjZAaM ][㸕~_"%2  l-%QeOv%womI&E]m/ ||;$4I/ j!⚋c%">0/ܔ^Dd` U+we Z^ܹ-76D4y jA-gfHن3ӝckY'lPmJuDq/`#ۢLsKEZ6 Z"Ў%񖞋Au˫N+9<(䎹0/# uNk1OYְcFnᲩ!4(j-~3F;|4}&WN;(J̻ \ok,c,r 4ɉo=yStŴ!@1Q4n᳘XSa6mRcm{,ݳyIƍ^@ֱY|АD*C!ӤSw:T um`A2H\bmb<|KI!˝Pgx-%aiF &[u~46HU#>|s''r5ymC!CGSpO&&ڰkh]Y0@Ae zFwrMtW71Wm vX%IZKzSV?=GeycEDӧfJ* b5'2XB\4x^F8 "c-Y|GoK,_/{R2 IaE {@ _U }Q(w ߶ǯ'H#[/@ G’*ټ"h7=J?b , ) »Z/갛ޟM/ Vtp0?]S>U] Rn=Mw.}`˛pveS(u/&72 9^ȝZdjЂѓ0S9̛fi]/{3A Cl(eY٘$-Gk#GzVng#np#Oγ)y$݌!j-' TBÈ]p,ʹhh|ϭ*DD΄*hWV dq$u pfBV'YV~Nz|};^|Jp0a6G!a+zHBt=zN olw\>WQZƮhvoTfمY &B: aÞtK-Aq+ 1cLq zkЎ1ld:%5&'#2 \cL%1)1 dat B۠rXFO4&K˰!k㎵/-I'ɖ,/7mln+m3gZbV%kr=V%v7d)0+nHuXqWۊßߊBVж =V%o[V-k[V%k[rV%c[R}V-m[RV@7$!kM^+n-Ɋ{ A |`[q(0ùV%(7På6kŶ@5&ąOz X (iY8R9HYl>)TȳG G^J:8H*jћ@$zd!G.xkTtm,YCd_S:zEkc9@:$" m`T{`-7H)kXrlu*/;ϖgOTZK: (4$Iq^([~?AC71j۩p_T4,xࢱP>[ktiܧ5`?u|ıu`75t}tZgmSfaHF4nP~][ڕꆱA?d1f)Uӷniq.,뱳%5iwihwO cw_92^vQxJӭ|PnG+[i&hvJ_ ӹZ߯ۓYJX' 7䶿5t4o0Ё[z灠Yot7 n·/㷃GVЧ(ژYb5Vy.*M *ag*/HewC`vEg4W&\! !ۊe;B-8幎];ia{i!:ޡytz7,r5=<lx1{#IBR73S;LB:S/%5V5tO1u ̩S@Lm]L/sT^Tg|fj+.ӆ6Mˤ5ڰ9z9!Ѐ(6zC|wFqۧAѴa,EE ݘ[Z̀mu.|2СQLVϰt7T!l yCyћ&4$Ms j_xnc}7ۮ֤Ko~)'+|$FPG桽Cw*GagB1:Յ(VuSo5MYjgbD{%DW+MLݝwAiEDбX^~%8ylƀ ]hz=w֒ nŚ]rkim;:v_$_ǡլkr uqnB cDB]d$6&KHP7 TGYKvEK[5FJŴj0 CW?,AuN˽Ie"/ݡQWq-\nX^T>n {bnb53lJm!DeZ}r3%@ӵ\:zL`͵S!$j޵y7ke8G Cg킛:͎^XcouwO vW;lku3PDUhɾ&֚% W֋/Ӌ%`$J>܌NJM2H<2ٚt z %9 '8 }#vhxe^Pi 0r/~Eϟ,=nr[vNxsw.iqZHM#toIhWqo41bGvl~ _27F("5Չ| %Ð:r=$A<ׇ ;]< *'>- s r(oS"Jd%Hnvsm G-U a?]Lů'?$@Љ6KmMj{28`b i,"O_>vAΏ0Ls!h2zdlY5 pb Wj֡R;y&yȼ![le@a9nl+>XT."/ңN1m͘0 J3+Q xvn6򨟭GΡwgGx9 s=r3| \t⊸9sܰ`]g HempV*NoarZq2:H9TΦj ƈb>Ծ'w8H$ (H|{587F;Jiƕ[} Bq d |nkҕ*_^ı/4~- )V,ńݕZl ln`}n]c |4¡&3U5bMYa[V(sT#^}$c)+R4Vj,17Z%È،z+{L҃Apo\ 0Z,cA3?)+K-M VxR¢C_*zzӾ Io5[ԹWv ]Y`ٔ$b3g~PJq&Lek2L.tHi$V w ?\39D`hf>뤜vxރ" (5@{b1;.N&Z{n?;!xK@v;՜-]˸86| _[mNe;_DabKٮ0WA]|et5~I d@B6 辶DwLAx!8p_pp=4>e}o<ЈGgxe߳Wʚ6~ƦȩbL-ˎJr8Q Tn)J:t]8G/oL =ucbSqבl)-r%D+eC`'*L$t^OGPC"D+X M+gsPRܧ Brg,>8誥jd>vpv@xH ``TɃķ|<T#3*6;?|f?bLTL߄R2~֭s8ƦCAü 8פrO dgOw!`/YI,05y8k>A  J]= 3l&3fٻ"ջvp)\wR?A|w'Ǥg'M]/)C%e$Z`6О UYZ=hfZ_e6@f!|61 R ),`^>K)("}`u.免ZZ,↲.+\GdhN|gaI{yQޏKoSܙWg5 meqQC,3ƟĎĖB::GZTYVxHRۤ>pVՍ ժ3e;z竟^)IU1e]g!ݯuo@ɝݜUz:?v5{'CH==:+=}/ JUEKOZG?翾~.Q_/|`x^<jЕ̲J:6@c_eͣW`1jAy`+,`UJ艩߀?񉗾9,jVGϋ|b?Y9z{gx{4B=-lR]ˍHio  p8XAVy/?JP$9CZx#Eu-Sc+oZ>SYFr<}N9ra .'}1EL™-tJ~C{3@?8X}iƺDT,ّI=e(6,RD#M(,\~  O#^Qx&d&KJw,5%g/wޯzDo;)PcBj}<!^M'6p1i)nz4ҝ׆=BCt wFM$ٲ߬oV{qi<ӎQ3zU%H{tZ.$ߒaXԵ@ R>G+.e[©)2ct ³_ښi Y&}Q~_wz;,5Z@۽+WS6nH<-@A\Pθ^|X6 Cl,ͳ{-k{UYR hgJhv-Lmb-phXK Tʈu]yuR)&GWuI͋P0ӏJ gSf6=JeJXD$p.7]6Zm1 q< ua9:O:pˇi24Ⱥ]iHsl֛^ SG7w/Q&L З}@9-syVR3h(( e‚z̝3me$7265C BҦ( q H Yr~sv.Jkrzqkr"XS#;V?1q$3ҍlܢ.vXQݎ9-aS~f8֯D3ԢJ\")MҚm N>~u?CҘa Fi&X~]{ycc[Eʤ%ZVNU9Y@gZ Z"oB:A>Q G$"6+8:]5F/SkuIT5_k$)&Uz0$\Q>~V!azh=w?X!x- qV3k;Cck}'\}2d 5Mf/?s#TN%H1e] "A7͢ɍjZ$|ջ޽ \_} ߢotVRh6:&FMH\Mn4 Hr8Zz V4H_;mG|tk-I^@ OOשrBFHuAvc:$'; d-SIpy hRrgaROY{킵UUy8LeWir~ff_ooMO6J?>mZ ߰w柯޾ŋEfoܗ`y8Dx4e cr޼c\4`˛1j3m mL1e1!e1.sR:G2&V(4xvTtT$c>gC vCHkrsf׻\D])?g7B3t7Wf0DawOגFx #[|vԩ՝nwza4"owlO}mXKwC;t4CB=ދC*\l Xl251H '̥ܨSn_JM[-2R"qe5?έz{=Ǩa\yTDxȉ $oviN}Kێeִܿ/E,J0NhҋT- YV]FHoESSĞluJ|tv.J,y*Xj=pTA3jau/wjPyjU)B"9e>%R+yƕNh<61u<˃Հq{ޙlQHr\k* X}mZ|}Mbtuy{w]h BZx`4=*"~xV%*_{UQY8ŗ +Ċ,54B-ce7 횾1{U锠-q&C8 S>r) 9+Hj,]wT*Md{I3SA8Vh3*0u?)$Ar'4v0`H ?#f> qNd:$jY(KG \PR G:f:DӔgOcF(J锜ә>״` i\H#!#NOGcGL: g退qD^@PHQAh&ʹiǎ@'gAA꽗8d0su8Bc~SÁ~%:,4`)2&`fd3QV#MAҦ>ˁؼL]7'TS|uGOgպ&~XĮaA,QPu3iF)KnJ>Hՠ~GAӅfR{GQTP'p#n[j͝wԛX39/Z.j17;qulxGYM D ?ʀt (tp-3WVީǓNO\F{Y\oOB*Kzp=ɚsR2ԓ +NCC+H-'wu|reBw&.H_V*%vy%U~VJ뵼DyvR7Z&&ZEh}-4S/ce?a)Mi^ԔkBjU5"ߚp}M?Ve1qMI%ub.͔K\gL+bWh5c4MzY4M:j'W9+ ޑ_~z+s(CCV~7=Т'f"BU/:4\ISde5ϝߨշ>?E٣d/VZ^PKV$Mc\Qgz^¤•}RͳjWhmnj[ݚkF3F1E4Q4g~3zlޝCsvŎb5 Yˤ]xA%qvq=p50Pcg NKxq71e'9O cc_>37g=txLw81M)t]ӎ;.-cc W VVP v١Dǚ:wc9ʍ3,N?V36vXW& 4'V(͉ { d`S愞tҜнN& ל' O|uМLלXɊ$(DT"8*WDndCk G]-^M֭5=2[a;)3s AcPUPZW\mSGumSU'A&9ch@0c`eqڃ5oaR?WF{!(7Ӳ'1ƈ%}'_Ͷ]uI+*符g6>Z ζ~ j{AN[!y_&w`0 ~ {'83kI4*UG9LΛCQn_=ܼHcVݳ"G7h{!FpLN!ܿp9p巟:ul3A{KpsE&Eդub4X 6Mor6JYСVSϨ;G0\d7Z4bHfq>F;O'e:yHIx{nu-RN 4i+'O@X{F=#Β {[~NFz9NSMuG߫鐕=YkMEo;\  e>vi<:H1 }Y滑 VSS}QK%NN^ad© |?\|We`g| A'ԹHعy$yԹu` )@*SCss@D8s2v}nniP CI{|%ݱg P\W]m _buq vꋄji1EGJTPd+DAd3K>Vn-WZzPlN3 s[C1?oL[ŞZee턫~~#{#Naq.L:{+w㿝C4>MO R,#G̡Nl:b@2@zLh`Z_hvE{BzL6'iM5wxPP8cQ3E8mbwE w0b.S-Uq$pvucgoQÞjs*z& ]Gwq_ad#OVy8CP.,4ƼOQ֦TW3m;\ xY&WCJyI@]9b*HO$i6l|_ J*:Shb/H$3F6p4~iR3f#5󭺤f)zA# |n{~  }HȂj yG:.*'-ł$Q Z` \4柑' Z$;[. S=]wX */y4Pͺ²ePL uY48DVʎ,Wl~s8?=q/x/\QϠ~ݏ%0A7:"MVY@gqHz l%ut; }vͲ9]g<[F@. l(#k# wF5_+Cg}|1v >Sᨇv& }ۖMiLzo&a˚6V>]lv%b[8SLw!rReZ3%[^$\EB-yG6CSƱ"%-ØW)~2ý֥pdYQKVYS 1g(pa!^^lS7_4![Sc,r GM<5ul` d ?izI?ſ_ J˛YaekS:s9RT%GPR"?cؼ%;}-)?^LS'vG@Ff8̉E7$ˉ=1_u%. w8&X.U6DX9.-TceUƊ*bW=n6h67KzE8=| W"k'3%a3 L+w!dz9j1߱.y\$`\%IL8Z||.LdIS*cCx JeZr?7ul""aWƪv8hOZZ&LeFSYkf15'a+]XNZdYNa pkKsO_+wȜR&8p?ǟ߀eD}k eXYEyjJٕi4tc,wgąX rμϜa^ϧ_a*/xM$|H㫛O)kqhllNe镢Xy!g8f1y4cܑ%gW^n0`N > *Kx[@vJfs*`5LtPR^mҐOg4c8%KU6-b t~T5eC?-dj] qsIC:o0fw31hv6fܽC}i߮VuN%Y>Ƥ)_nE)|Grޭ5s|3om0llH€~D/?DX1W(bCOiSx kc⢈C'ngp؏jI1}Ũ ̉'J5,b RΛˊc@a~ g1CǧnPCV ;2>B e`p^]sC=r z G+{ V8KXE¬fE?IFƟOjzn;9$h, k# NEhKJ@ G]1 c!;]t*HTS2Pv5@s?!/xQauPgn*_N>54')EEje#hPb-EB2(h?xkڲ&}|eПvnN6~<˜~9%aruWm2lL8|.SUc%zދ頋CN$yCl jfC&YFWx7kymM@uDLO+@Q\Ym,9FdZKwˋ#$YOa:QYCmS$Vc>bʚ,T{^4"^Po!:#b^w>y{dYQfKFzW_ܷ RO})̝;Z0iz cF' e{bQ*]x6_ݹ8VԌBi~c435.ʄi q!8?χ=$u{U M6y+CRN e>xV!Z,iq)}0_ =['bG]Jض-.eO's*?d]L%8쉦̂N>EɈ9'7倬S#hʮq\E[P\f hc =R}- KM IQf6>4::E[ƩnxFq J>&NIP<&xKㅓ>1^ڣD?2H(:)*XfnM<څ??^e3du&Ѐ\H=qi$Y˜4n()#O_zm7wC0q \ҍʬ^m?$NQב$JQ8[ͮnߋMsJ3t*#YjRNHg<zi)irOb #S2%`n=k6fsb(2XB c‚:^xҾI쌹&s >]õ9m꼱e/ Wz=^SW_?[%U2%u#LъuikLa{TD y4HU̯F‰Q<:۞^uqGՋܴers4). J+0:j^鏧J-y2OÑ˙Y'4K/M0̩.蜺s1D+.3,Ff^X! >\Ix'T7LxAڝLpABFn3G2ߣVu|݅sb8G+ݱ=!HüQ!qyG1jy٢5v}5>wH~?@j}>JmqA@uV^AVo'.L j#fEW{?P ^UNx9N~g0A h϶VB}C|pZDydv ߀mVuDc[ hP,coK2@ivͩ́n>2\cQ7J:}4PoO{f:ǖ/91R%0- + =j4^G 3N)Z=}  { yF9"}@Sa_PtO@]wlx\@p~4&j\'8<38.8>%c%K2{jΥv//gUO)d |⎶5SAOyЊSnĥY;N,s UMPbѕz2&S9V*Qi 9a spxHy\_ϯ@ft dBGvq#]ڮQ-K>ue=\8JEokd㋗g)]HrOt?~JjZUH.Kcug@UB#Uͅ4%eJ LDe+_6<^[lyGs"Z.ͺ3?_̛ѺeP2IC^m ؗx9RcI^5gW OQ,]/QfL,pT/%FYSkwՎu7;up]Q4N%2 '5{əc "YE\ebAjNm z?使@ _aYV6g:-자nh^vU;WV9] /;([AV*tk>4'O-nm'8vWIMT8]8%AyM)#a'fw< [}}4C`ҒJ]ѩꆓtHno S{DQITLpݞ{WqƐi/@D.*+uKD,~?ퟏk|둇x؀"q ׏!,agёarK|tzἶ'eA|4gaPa=G ?} C>ՔaJ#)1|4p7"0Ǵ~ 4oF3> G{M=)]>C3S%yTIC*îcT3@^uqΤFn1P(g./h"peTV-ϛm-&VRugu[m֙?AG[-Y5)3ْNqy ꁌ9c(2XGlȱºA`ÜSTQXqѩ6wHH>m5ab۸%m 9>ϙe9DWjfɴ]MVC>,jxkhG31 ¬U|.3y'42PR7hp\uռgMm鹛oL&)9{Y,&렍wMFwgϙ6h_xth踑 eiU%F'?9@Ug阸baz#y6g M}I]`D$OqYbGݨI3B^eӥeKMWPjgxSCPO=ۼ68e ~`uO+L@;|6[l\Y@yYj'y}i߷!)faXt߹N;1BDͩbF`e̱-pC}EJhpP^T d nH2QI(C&cbPK7i׎Ք_%izWY⡦ nǝ@G~"_ @=Bm_sAő7bQՐG4BǜW3xP(Pe1*EcNμϏ\iͪ2rHaҫoG^b1Swee3QqK!m(O_jMb ROY~z֊N0PBKUL phMbmՔȯ`'ihQ+K.17ֲ7!5Ly5>s95kZg$W!H +#-![Š&O҄_6"#^M sɨtpT:r$'dK5${|~.y=! bF9IfSĔ;M.vp)P;\V*I\ҶIӺv=ښb'=&B27eU452DQ#!+; |s~~)6sښ)L"0'GB}\ɝx?Mlh~tYH"C@Ub~K4in53糘ԥ)-q@aɌ~-e%lt7p%8@:E:a*i=B{N/J7C r/J,o([Pg 'VB1.kVafO_RGg`vȂNȩ,/=9EYCIIIjMQr/1WSBhUt–ybXo`h  6d)Ԕe54&~# ӖﮤƞS9D$Ҥ$=XWfϾ;0 wM[f^,C_.LbFԕq,v"A!NI{̨_f"y!Z;JZJrP5Fu4Ó/sVMpIaM"&Ir5HVN{Nj1[a|u^ػ8Z_6U;8?x{%pA!9JMm٥ꆒ.̋5zeN_Û?3-4_ϯ1~yw%ͅ8_A6Z.{D31R}^V}{\V5冼h}ԥ>^eV+gB=P|w>*炎-nɡ7lR' b tn.HkA;nINPܯdȐM7ybhMa/?L!"xcF[ue@_ 6q F&@ȧC6O[ې7AOB R$OʇE/SsxT"Hq.h !r51 uX6MY<`DnCmq<_VW\g_WamkK׈+OuѮY';X@ ad6戶ˁ]ݿgJ[I̬?`UUO/Fz&EL3ơH4ti>CּFQ~8gU;::9ݝB댄æu)+6l6˗I:gcp}9,:AK[.Q@ʚMES0B x=Bc U2~{cٰDDb%n^lΧ-?Ds(t<-vhU>Out~ aH\xՑ%g܇I$V>jh;^\1c)5z$#; Wy8?~:z-!ys9 EĖ@dK :yy܈߁Ԋ<ɩɳIwەv:į]Ts1NxLr 8oO<$g3S1͢ˬI45 b'ԾZÎM-Qc+ג}g^Z~p$KSgu ]~ [՛1z){a6q>FӳDbukxٟڬE'KHQNSr?v{)edCd$T+d$V8aFםCPCe5!Y\%e %dk+`vc3zb*Κ;}hfkUf-L7?~#p*՝θrVc C4i"G}hK<^D70C0ʺ"-Ko0h7Xʠfd8!M.6b#`SyvQpG&Cf!&^}P%;6urg\/p-hʚج\yCOwP]턣SOC,t/ْ@~MT9tBuqx:,`۽_>(%plufg HP}% 1)4[vխ[<mLs ǨfqJ`_&lzݛ07q³q8@7j[@DB+]~I΋&䒩8b{-$_θsbJ ~zXK=衄Λ/* #%"G\ȑ QO%oSFsug;E EsO7o._G>?0 ||xBq\S3a^1E~_-h .phs4@ r] @-4K *aB&q9$?nq(*<㏭* q0A ǴF nOPv1s>!ӮV jqvD_Tp Py ζt4& [j&d*UgIGa\|Pg`0nQ~o&TD;弈Ǹo>](;l I |5:Yˡ`3dqPiO4xOF@R g]{w`l (mыJu Pt}?YGd :# waL|od|FT=g}{s h8 7uYIsМ h'Qx{sc~]9Kiٟ3=4,^Y.^J,=w,]J9k# 7+sW7@VĖ7C9s6\N ܢ3'0j.jk[5kEfgTC[~24A4.)3t1{tţuW}5MTm*.&&BqJq2R|Cms!2k^#Eh30yCc6=o>Es,KtJֻj⾪[bv'~xɚo綵d3"C$G2zNF)Z hy$Źv?SvII+<#d/L@T0.(r7F]sK9%jjG, e@4X5C˅(  Ƨ47II*)6v+(՝7ؤBW5Pj;??m~ݪݻ9ޱ ` 9B 7ń8"L]^b,wO1q{IhB:v8mETKlC }}@|TQ@ݎahP>$r%^^\"C3\0YZ1ZhTɜ.BT 0EŐsnV;V}vTAG;ZC].DX@?ZP!X[/ybd3,` ^Y,JK X:eI#ɻymGv4-it~Tsd-N{M&>+1q Mˣ͟km[o6瞼F~>lPIhwG:d= i |z_-y-oǮBȨ:N􀐇RdT璆(F-Is>4wǙ$mN>"Б>BӀ[UNVEYխOFvCufbqxv4R)ә]kD"N(O-6O*FceE"ډVX./vܩpi^H#Y~.V*4и Usp.Z0+yGp,]Q^ٺ jP~0.f05n~0ALfAZW4fEM_ 7&|%AsUXy[Qi\A{| A -&S,iJ;MIv.b8{gRKܐ#[Bv 7$Ckכ0иvh`4"=a]??ǯ{}[ o_h7Xooۺ+_ωrQF-dࢄfod˽o.nru;A=ޜN¼LKFĊ ES@u:g, ƥq69:8eMXNMH"{q1wOs{ @Hi|\;\k+ؕH{T΃u觺~-cT?nHrb./7] DU*KcrV&uUB }@H^4B̺жD2W4 J̝`GVDKCHA+]{ Oǐ :2]0Խ-CAl_~bB\\w2V7bש{@S/0Dj ,u'mSQv£nv,2*%xIBt bO$QAaz;O J,gQAq||pSeF#0aX9WIQ#FsdG}BH6yӣc wj7NF_RUlq[e+3gZh*'D[);7:>s(qfҎJCU {s6+|m‘d1IW/^Ϲ7*<^ z1lWN/,wjEҞrN.N-s>_Yl%g=f&܆P@Ѽvr/faP΃ƅ`T naݒqμR+h%$Oe[J|:sEe_ }Jkڴ]~!@< t@l= 2)>k[')Iy^pbPvO+^ŧ>v4.F vXׇeNP=tX.9o*YVhX8dvX|uюce8 Vlh8T=! 2䥔\OE?XGsoE_N^ |Gsyl nZX0 QN6乖tJa$œ@yW[̗2TX\S!t UJ韛<[6;Fƣt|ܯy{$,'7<,wB#Ei‘e1,uCO=(vp`nv'bjGdي,=|: 6 #zrqu`6#zgY6wYoKY\nEȳF BboR:Q7;(q2lE'rmZ+lvdy8YfFVo`dYO4,;7UYfU7Џգ`dY "^g5M-J MTdyulGi']Ȳ{[",i#e쇁JYz޷a.0K1*bK~d>XI[[p#˦,BkȲ~!>tvMĽ#"YA~2H%/݇8^jE*C e zSjɤe3lP݌,;7 ŪqfdYm&Z'"]dy"G0s(8Y̩o ,;2ҭ_B/fe!d]'hB+>HǪLBHG+_*\W}ep; uUqč,[E)<Ď,yJb~gΫ^#ç5![yݵ>_؍,n{6ˁKdtmYnh?`ѹ}9pL$88G&8AV<3(yWS`;{ͪ:z[B,E6qjNj{圡41nI{&WYBjwqKgIyW.}/Z+S?:6N+HJAరTB)'>i|4|y P$,Q^WrlAw)4QZ">TMObշ#O \;#GeB>2fokАu ) &z߳ u?|yUՄ"lKhS&Li!ݢ7%RMVVo\̿kUƃIx-]'14[ v. k32,p%)N/h+ܱ[csJٞL; ?bٲ) 6)?ɣFN7$`zqyo6,+*0@hAlS7ѵŸ5LP {Rd%k =0oN#/V?*E(tD\iQbT"Hd̦]lK̺^h8YȔ,Ix߷]:t'&*{ a^XNA?jfFWwy2wΊ8TeGc,y=x|8Vcs=o)9+0\-/8Pe,hZG/윊0 _HԄ"IJ$\(,N- v`9;[mV.D#j'rvb[Ng0)#c\^mҚxXms/sQP"bQ* p ,> cl"!ؑK#h*=DzN< !pbt~k29?WQ8r*1 + RԘν'SN{2q<ԁQeۂ t+>ZlJ!\nPQ*Ь[D@Tņ'U^7}>n,ClyupFi Iq~s1bNy[AK$-ARp@GG ӖcX<= 21Y|zR~SxkuaMg$-vN@6 wRШwn p*jL8 KҤ3$#O|;%yI'ߜ '< |sI&xZK+Yy{QoznSsFasO!tq_HFɘ \So1aО>ŗmqaPbcsp8_1§cn4)릊 U&Sm^ $RJ42ae0o3P75r.|BHcRuX{9b3X[ ;CZruS gI%Gd.Pq{dYXao~Z{l98 8"C6oȾvj%)ol\g: DPUtXY DZqK1 ~n*E~%ibb*ٙ;)4v"v/RLgS.%S3$rf!K$(2%j|s8KP@Ωǒ?@vvSvl+6Xy3[za2'J"~}*(/WK*~z]7yQ̈Vhz")a-=_՜U˦Ƥr(Mbie@Ah/[|%/o^ĖI OnΙM 9{>}2'M#~#{+j z :mSCI w(bFL ٌJvy4(@1^g<(2Hᒔ[Y#_ Ț&u$bh5 oIWYSўz*,ƙHlGdoUJ>)xU23زz)rW>% AL;6} :A:tN XjXwӵAKa *Z"Gv?nP1ti 9]=:(.άƜWbpI'*9'5Smֺ+n4p!ytb]VA-6;#-9{s &P9CƲbmvQs?E\1lt mtsQrg ΅tw.;I^ fTmЋ A4+&T[ eCT?𗗢lwN'|[Ѱ&&u',ER㍡UvSŶ96ʊuN2]wM@N3tKnH^71F6TL_:Ȧl9W@I?}z e~c}Y hl|W \q7p%YXvcd2صLiU"uħAWi uX`1..f&F7;1$GD֙j0d +(f B UmӋEvl*^8#;/;X3 i dVoN[D:qV3 .K. 턎'.H?{ EyaGmӡ; sq['~T$|'\#^‡FIF}8ڈRC<2 ԛ MuX1@폖] *ؐ rz׭k)Ay .k].2<KHI׶İՌwUoXwgusT.DI+>?xthUq?"pG{qx: W>OQ|~wzkL;uZ3>b~U70Iya !e9?eJ -ݣ kEclvd6y\Be +:|u~~2 :;eh oKL6n+K@<ljʌUܑ1vAD_u;highmXu| %=x$cg,ɉysɘ{a=_Cu^#]OF.Gr{|ەdVjþ|aT׫eւLqx8x,EVt!gim_|6t hL>%6bYz'DGzlOG^7JGH+̿\uRCZ hR3)N%+`pDAKؼd_F4a'qbvln )vca_FĬ9m 5wkegZm[bLAj-$HE9 Y(DYX;vJ6[]uW~J^ l\]p:p wq -2l2$+d%TӬTl],/#*ӥ݁[DxWwX{n.2YEBN-`c-/:f.R~ >|ݯ'nc<\Õ+{qJ{" \„U[P Nʨ,԰E?1Cgįݿ PE荥*0 f,Y$eus^KLTԬI ?!<5CwlP`6u#al+)M}[a<l8PU7i!q*zHTS$i1 K.|: c8Mj$dsoGz-~Qo'%aVBA;R IөtQ9fǗQMsVƵ#Gbs]_+<)bԶlk[TQm pyd0`L&;GV_Fb$2//شusiL$AM~ 3idw,YD撢Eļ GBCTC@p9[R,CݲEuvQ] djpa4Bb5dHff|Ǭs2Hh钬GtIVYGԬ06:k`Z:4Lɫ. O+!=L6.. sa J9w9J'kz%  X˚96V»vTyYNҶ@[ORO q4O9NS%b-[ԚY$e7Nab&ϦOj􏏛iR;7|כ-EF8k[nl}qsդ~yaZWt-B#5Beex|<i'܌}c/uAhc vŪ,k"8E5)>0zEY|+5?7Xn7HX!U 6pd=3S^WpX7eRi\ qXX1txujKK4eUҤN=ؚ њ3q^W aE $'cXkq%ୄLI^rhI>?j-r#ўm3&ٰ.u[44r!]縨}"!kG?_n.K=y!jS3YbdeJ#->G ^""abjP4-qFJ[ \g-M <ò&.l1]'oT*d@a$j[7˺!GvsYUsn,m7ǸV~M~Y }WVSH؁ P=Eb{]C< h\c$1vϩэ@ViMb+ekt'Dxx׊-i>(AE`m"4XMuX\*Q; X!SN6&Ey_AEyE*[$p.Y.D[F|J' R70ؽܿ|v,Tug c_1' rY!EKVj^F|tjUIa.چdi^#T:⌓f׺#wVhw7>|q8sY|,;a؇YMeG,*/-/XW&А=iVFMN E\S'@n*DZ;B~tR76l5Кw!'U):dƈ/զW潜D9/;4@1h0%?,.A_y@]jJ|B҉P|eDTEpkW|[we:y#PIb?oj_3_> _h2OPQ_NWR<7Lvvy-kؘ28k@ VtПc>+C}mT]Ńb6P;Ru?{<z*Ei~Ai|\;\p> mpAq|urdu];{۸MVlz%a=hfd jYe .u8˓ĺԗ].i<*}/rXat 3IN65/NfU¢]AϿ2 u&=+g|j]n˔\35MA/#1,n3C`JM&:m?1[[uevb" rXg'XB7,F7=lޞe1;N~=Sq=o%kZDsGwk;vHb>f&$P,gVg-76?o+g師[k0k£B[ fys.;4+MX%U2993,#=݆Y̽S~G4{>Kl 1(ss %g"{l]b[GܝAl !g BI 0gqCkv]gJ$9`%LPk<#Aɯ#sF`ETgy/Yq!MGB8)Hc sb<Jyи)ڠF촦1P1fAv!W/00 eggheads-eggdrop-9c00fe1/FEATURES000066400000000000000000000047161524730042700164740ustar00rootroot00000000000000Eggdrop Features Eggdrop is the most advanced IRC robot available. It has been under development since December 1993, and unlike most other bots, it is still regularly updated. Some of its features include: - Capability (CAP) support used to enable IRCv3 features. Eggdrop currently supports the following IRCv3 capability sets: account-notify, account-tag, away-notify, cap-notify, chghost, echo-message, extended-join, invite-notify, message-tags, monitor, SASL, server-time, setname, WHOX, and +typing. - Support for SSL-enabled IRC servers - Support for IPv6 users - Support for Twitch servers - Completely separate channel user lists like having a separate bot for each channel. - A "party line" available through dcc chat or telnet, with multiple channels, giving you the ability to talk to people without being affected by netsplits. - A "botnet". A botnet consists of one or more bots linked together. This can allow bots to op each other securely, control floods efficiently, and share user lists, ban lists, exempt/invite lists, and ignore lists (if sharing is enabled). - User records are saved on disk and alterable via dcc chat. Each user can have a password (encrypted), a list of valid hostmasks, a set of access flags, etc. - The ability to "learn" new users (if you choose to let the bot do so) by letting users /MSG the bot "hello". The bot will grant them automatic access of whatever type you specify (or even no access at all). - A file system where users can upload and download files in an environment that looks and acts (for the most part) like a typical UNIX system. It also has the ability to mark files and directories as hidden -- inaccessible to people without certain user flags. - Console mode: you can view each channel through dcc chat or telnet, selectively looking at mode changes, joins and parts, channel talk, or any combination of the above. - A scripting language: commands and features can be easily added to the bot by means of the Tcl scripting language, giving you the power of TOTAL customization of your bot. - Module support: you can remove/add features to your bot by adding or removing modules. Copyright (C) 1997 Robey Pointer Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/INSTALL000066400000000000000000000121651524730042700163610ustar00rootroot00000000000000Installing Eggdrop This is the quick install guide; if you have had little or no experience with UNIX or Eggdrop, READ THE README FILE NOW! This file is best for experienced users. For more information on compiling Eggdrop, see the Compile Guide in doc/COMPILE-GUIDE (and of course, the README FILE). QUICK STARTUP Eggdrop uses the GNU autoconfigure scripts to make things easier. 1. Type './configure' from the Eggdrop directory. The configure script will determine how your system is set up and figure out how to correctly compile Eggdrop. It will also try to find Tcl, which is required to compile. 2. Type either 'make config' or 'make iconfig' to determine which modules will be compiled. 'make config' compiles the default modules (everything but woobie.mod). If you want to choose which modules to compile, use 'make iconfig'. 3. Type 'make' from the Eggdrop directory, or to force a statically linked module bot, type 'make static'. Otherwise, the Makefile will compile whatever type of bot the configure script determined your system will support. Dynamic is always the better way to go if possible. There are also the 'debug' and 'sdebug' (static-debug) options, which will give more detailed output on a (highly unlikely :) crash. This will help the development team track down the crash and fix the bug. Debug and sdebug will take a little longer to compile and will enlarge the binary a bit, but it's worth it if you want to support Eggdrop development. 4. Eggdrop must be installed in a directory somewhere. This is accomplished by entering the UNIX command: make install This will install the Eggdrop in your home directory in a directory called 'eggdrop' (i.e. /home/user/eggdrop). If you want to install to a different directory, use: make install DEST= For example: make install DEST=/home/user/otherdir Note that you must use full path for every file to be correctly installed. 5. Since version 1.8, Eggdrop can use SSL to protect botnet links. If you intend on protecting botnet traffic between Eggdrops, you must generate SSL certificates by running: make sslcert Or, if you installed your eggdrop to a different directory in step 4, you will want to run: make sslcert DEST= For those using scripts to install Eggdrop, you can non-interactively generate a key and certificate by running: make sslsilent Read docs/TLS for more info on this process. [The following steps are performed in the directory you just installed Eggdrop into from the previous step] 6. Edit your config file completely. 7. Start the bot with the "-m" option to create a user file, i.e. : ./eggdrop -m LamestBot.conf 8. When starting the bot in the future, drop the "-m". If you have edited your config file correctly, you can type: chmod u+x For example: chmod u+x LamestBot.conf From then on, you will be able to use your config file as a shell script. You can just type "./LamestBot.conf" from your shell prompt to start up your bot. For this to work, the top line of your script MUST contain the correct path to the Eggdrop executable. 9. It's advisable to run your bot via crontab, so that it will automatically restart if the machine goes down or (heaven forbid) the bot should crash. Eggdrop includes a helper script to auto-generate either a systemd or crontab entry. To add a systemd job, run: ./scripts/autobotchk [yourconfig.conf] -systemd or to add a crontab job, run: ./scripts/autobotchk [yourconfig.conf] 10. Smile. You have an Eggdrop! CYGWIN REQUIREMENTS (WINDOWS) Eggdrop requires the following packages to be added from the Cygwin installation tool prior to compiling: Interpreters: tcl, tcl-devel Net: openssl, libssl-devel Devel: autoconf, gcc-core, git, make Libs: zlib-devel MODULES Modules are small pieces of code that can either be compiled into the binary or can be compiled separately into a file. This allows for a much smaller binary. If there are any modules that you have made or downloaded, you can add them to the bot by placing them in the /src/mod directory with a mod extension. They will be automatically compiled during make for you. They must have a valid Makefile and, of course, be compatible with the rest of the Eggdrop source. If you wish to add a module at a later time, follow the same steps in paragraph 2. After you have moved the appropriate files, you will only need to type 'make modules' to compile only the modules portion of the bot. This is the end. If you read to this point, hopefully you have also read the README file. If not, then READ IT!&@#%@! Have fun with Eggdrop! Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/Makefile.in000066400000000000000000000407621524730042700174010ustar00rootroot00000000000000# # This is the Makefile for EGGDROP (the IRC bot) # You should never need to edit this. SHELL = @SHELL@ top_srcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ @SET_MAKE@ prefix = @prefix@ DEST = @DEST@ MKDIR_P = @MKDIR_P@ # GNU Make < 3.82 ignores these != statements, they are for bsd make and gnu make >= 4.0. # gnu make 3.82 interprets a!= b as setting the variable a!. # it seems to interpret != otherwise and throws an error. DEST_PARENT_DIR!= dirname $(DEST) DEST_PARENT!= readlink -f $(DEST_PARENT_DIR) DEST_DIR!= basename $(DEST) ABSDEST!= echo $(DEST_PARENT)/$(DEST_DIR) # GNU Make ABSDEST ?= $(abspath $(DEST)) EGGEXEC = @EGGEXEC@ EGG_CROSS_COMPILING = @EGG_CROSS_COMPILING@ EGGVERSION = @EGGVERSION@ # Extra compiler flags # # Things you can put here: # # -Wall if you're using gcc and it supports it # (configure usually detects this anyway now) # # -DDEBUG generic debugging code # -DDEBUG_ASSERT to enable assert debugging # -DDEBUG_MEM to be able to debug memory allocation (.debug) # -DDEBUG_DNS to enable dns.mod extra debugging information # # Debug defines can be set with configure now. # See ./configure --help for more information. CFLGS = @CFLGS@ @TCL_INCLUDE_SPEC@ DEBCFLGS = @DEBCFLGS@ # ./configure SHOULD set these; however you may need to tweak them to get # modules to compile. If you do, PLEASE let the development team know so # we can incorporate any required changes into the next release. You can # contact us via eggdev@eggheads.org # Defaults CC = @CC@ LD = @CC@ STRIP = @STRIP@ RANLIB = @RANLIB@ # make 'modegg' MOD_CC = @MOD_CC@ MOD_LD = @MOD_LD@ MOD_STRIP = @MOD_STRIP@ # make 'modules' SHLIB_CC = @SHLIB_CC@ SHLIB_LD = @SHLIB_LD@ SHLIB_STRIP = @SHLIB_STRIP@ MOD_EXT = @MOD_EXT@ # Programs make install uses LN_S = @LN_S@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ # Stuff for Tcl TCLLIB = @TCL_LIB_SPEC@ # Extra libraries # XLIBS will be linked with everything # MODULE_XLIBS will only be linked with the module objects XLIBS = @SSL_LIBS@ @TCL_LIB_SPEC@ @LIBS@ MODULE_XLIBS = @MODULE_XLIBS@ # You shouldn't need to edit anything below this line. modconf = $(top_srcdir)/misc/modconfig --top_srcdir=$(top_srcdir) egg_test_run = if [ '$(EGG_CROSS_COMPILING)' = 'no' ]; \ then ./$(EGGEXEC) -v; \ else echo 'This build is a cross-compilation, skipping test run...'; \ fi post_config = echo "" && \ echo "You can now compile the bot, using \"make\"." && \ echo "" post_iconfig = $(modconf) update-depends && \ $(modconf) Makefile && \ (cd src/mod && $(MAKE_CONFIG) config) && \ $(modconf) Makefile run_config = $(modconf) modules-still-exist && \ $(modconf) detect-modules && \ $(modconf) update-depends && \ $(modconf) Makefile && \ cd src/mod && $(MAKE_CONFIG) config && cd ../../ && \ $(modconf) Makefile && \ $(post_config) egg_install_msg = echo "" && \ echo "Now run \"make install\" to install your bot." && \ echo "" ls_exe = echo "" && \ echo "Eggdrop successfully compiled:" && ls -l $(EGGEXEC) && \ echo "" ls_mods = $(ls_exe) && echo "" && \ echo "Modules successfully compiled:" && ls -l *.$(MOD_EXT) && \ echo "" show_test_run = echo "" && \ echo "Test run of ./eggdrop -v:" && \ $(egg_test_run) && \ echo "" # Equal for all build types SMAKE_GENERAL_ARGS = 'MAKE=$(MAKE)' 'RANLIB=$(RANLIB)' 'XLIBS=$(XLIBS)' \ 'TCLLIB=$(TCLLIB)' 'TCLINC=$(TCLINC)' # Equal for each builds category (static, shlib, modules) SMAKE_BUILD_STATIC_ARGS = 'CC=$(CC)' 'LD=$(LD)' 'MODOBJS=mod/*.o' \ 'EGGEXEC=$(EGGEXEC)' SMAKE_BUILD_SHLIB_ARGS = 'CC=$(MOD_CC)' 'LD=$(MOD_LD)' 'MODOBJS=' \ 'EGGEXEC=$(EGGEXEC)' SMAKE_BUILD_MODULES_ARGS = 'CC=$(SHLIB_CC)' 'LD=$(SHLIB_LD)' \ 'MOD_EXT=$(MOD_EXT)' 'MODULE_XLIBS=$(MODULE_XLIBS)' # Equal for each build type (standard, debug) SMAKE_NO_DEBUG_ARGS = 'STRIP=$(STRIP)' SMAKE_WITH_DEBUG_ARGS = 'STRIP=touch' # Combined MAKE_STATIC_ARGS = $(SMAKE_GENERAL_ARGS) $(SMAKE_BUILD_STATIC_ARGS) \ $(SMAKE_NO_DEBUG_ARGS) 'EGGBUILD=(static version)' \ 'CFLGS=$(CFLGS) -DSTATIC' MAKE_SDEBUG_ARGS = $(SMAKE_GENERAL_ARGS) $(SMAKE_BUILD_STATIC_ARGS) \ $(SMAKE_WITH_DEBUG_ARGS) 'EGGBUILD=(static debug version)' \ 'CFLGS=$(CFLGS) $(DEBCFLGS) -DSTATIC' MAKE_MODEGG_ARGS = $(SMAKE_GENERAL_ARGS) $(SMAKE_BUILD_SHLIB_ARGS) \ $(SMAKE_NO_DEBUG_ARGS) 'EGGBUILD=(standard build)' \ 'CFLGS=$(CFLGS)' MAKE_DEBEGG_ARGS = $(SMAKE_GENERAL_ARGS) $(SMAKE_BUILD_SHLIB_ARGS) \ $(SMAKE_WITH_DEBUG_ARGS) 'EGGBUILD=(debug build)' \ 'CFLGS=$(CFLGS) $(DEBCFLGS)' MAKE_MODULES_ARGS = $(SMAKE_GENERAL_ARGS) $(SMAKE_BUILD_MODULES_ARGS) \ $(SMAKE_NO_DEBUG_ARGS) 'CFLGS=$(CFLGS)' MAKE_DEBMOD_ARGS = $(SMAKE_GENERAL_ARGS) $(SMAKE_BUILD_MODULES_ARGS) \ $(SMAKE_WITH_DEBUG_ARGS) 'CFLGS=$(CFLGS) $(DEBCFLGS)' # Special target types MAKE_CONFIG = $(MAKE) 'MAKE=$(MAKE)' MAKE_INSTALL = $(MAKE) 'MAKE=$(MAKE)' 'DEST=$(ABSDEST)' 'MKDIR_P=$(MKDIR_P)' MAKE_DEPEND = $(MAKE) 'MAKE=$(MAKE)' 'CC=$(CC)' all: @DEFAULT_MAKE@ eggclean: @rm -f $(EGGEXEC) *.$(MOD_EXT) *.stamp core DEBUG *~ *.gcda *.gcno @cd doc && $(MAKE) clean @cd scripts && $(MAKE) clean @cd src && $(MAKE) clean @cd src/md5 && $(MAKE) clean @cd src/compat && $(MAKE) clean clean: eggclean @cd src/mod && $(MAKE) clean distclean: eggclean clean-modconfig @cd src/mod && $(MAKE) distclean @rm -f Makefile doc/Makefile scripts/Makefile src/Makefile src/md5/Makefile src/compat/Makefile src/mod/Makefile @rm -f config.cache config.log config.status config.h lush.h eggint.h @rm -rf autom4te.cache distrib: misc/releaseprep depend: @cat /dev/null > lush.h +@cd src && $(MAKE_DEPEND) depend +@cd src/md5 && $(MAKE_DEPEND) depend +@cd src/mod && $(MAKE_DEPEND) depend +@cd src/compat && $(MAKE_DEPEND) depend config: +@$(run_config) new-iconfig: +@$(modconf) modules-still-exist && \ $(modconf) update-depends && \ $(modconf) -n configure && \ $(post_iconfig) && \ $(post_config) iconfig: +@$(modconf) modules-still-exist && \ $(modconf) detect-modules && \ $(modconf) update-depends && \ $(modconf) configure && \ $(post_iconfig) && \ $(post_config) clean-modconfig: @rm -f .modules .known_modules conftest: @if test ! -f .modules; then \ echo ""; \ echo "You have NOT configured modules yet. This has to be done before you"; \ echo "can start compiling."; \ echo ""; \ echo " Run \"make config\" or \"make iconfig\" now."; \ echo ""; \ exit 1; \ fi reconfig: clean-modconfig +@$(run_config) # Modular builds modtest: conftest @if [ -f EGGDROP.stamp ]; then \ echo "You're trying to do a MODULE build of eggdrop when you've";\ echo "already run 'make' for a static build.";\ echo "You must first type \"make clean\" before you can build";\ echo "a module version.";\ exit 1;\ fi && \ echo "stamp" >EGGMOD.stamp eggdrop: modtest @echo "" && echo "Making core eggdrop files..." && echo "" && \ rm -f src/mod/mod.xlibs && \ (cd src && $(MAKE) $(MAKE_MODEGG_ARGS) $(EGGEXEC)) && \ echo "" && echo "Making modules..." && echo "" && \ (cd src/mod && $(MAKE) $(MAKE_MODULES_ARGS) modules) && \ $(show_test_run) && $(ls_mods) && $(egg_install_msg) debug: modtest @echo "" && echo "Making core eggdrop files..." && echo "" && \ rm -f src/mod/mod.xlibs && \ (cd src && $(MAKE) $(MAKE_DEBEGG_ARGS) $(EGGEXEC)) && \ echo "" && echo "Making modules..." && echo "" && \ (cd src/mod && $(MAKE) $(MAKE_DEBMOD_ARGS) modules) && \ $(show_test_run) && $(ls_mods) && $(egg_install_msg) # Static builds eggtest: conftest @if test -f EGGMOD.stamp; then \ echo "You're trying to do a STATIC build of eggdrop when you've";\ echo "already run 'make' for a module build.";\ echo "You must first type \"make clean\" before you can build";\ echo "a static version.";\ exit 1;\ fi && \ echo "stamp" >EGGDROP.stamp static: eggtest @echo "" && echo "Making module objects for static linking..." && \ echo "" && rm -f src/mod/mod.xlibs && \ (cd src/mod && $(MAKE) $(MAKE_STATIC_ARGS) static) && \ echo "" && echo "Making core eggdrop for static linking..." && \ echo "" && \ (cd src && $(MAKE) $(MAKE_STATIC_ARGS) $(EGGEXEC)) && \ $(show_test_run) && $(ls_exe) && $(egg_install_msg) sdebug: eggtest @echo "" && echo "Making module objects for static linking..." && \ echo "" && rm -f src/mod/mod.xlibs && \ (cd src/mod && $(MAKE) $(MAKE_SDEBUG_ARGS) static) && \ echo "" && echo "Making core eggdrop for static linking..." && \ echo "" && \ (cd src && $(MAKE) $(MAKE_SDEBUG_ARGS) $(EGGEXEC)) && \ $(show_test_run) && $(ls_exe) && $(egg_install_msg) # Install install-end = echo "" && echo "Installation completed." && echo "" && \ echo "If you intend to use this bot as a botnet hub for other Eggdrops using SSL," && \ echo "please run \"make sslcert\" now. If you installed eggdrop to a custom path" && \ echo "during the make install process, use the same DEST= option here." && \ echo "" && \ echo "You MUST ensure that you edit/verify your configuration file." && \ echo "An example configuration file, eggdrop.conf, is distributed with Eggdrop." && \ echo "" && \ echo "Remember to change directory to $(DEST) before you proceed." && \ echo "" sslstart: @if test ! -d $(DEST); then \ echo "You haven't installed eggdrop yet, or you installed using the DEST= flag.";\ echo "Please run \"make install\" first.";\ echo "If you have already run \"make install\" and used the DEST= option, please use it again here";\ exit 1;\ fi && \ if test -f $(DEST)/eggdrop.key; then \ cp $(DEST)/eggdrop.key $(DEST)/eggdrop.key~old; \ fi && \ if test -f $(DEST)/eggdrop.crt; then \ cp $(DEST)/eggdrop.crt $(DEST)/eggdrop.crt~old; \ fi sslcert: sslstart openssl req -new -x509 -nodes -days 365 -keyout $(DEST)/eggdrop.key -out $(DEST)/eggdrop.crt -config ssl.conf sslsilent: sslstart openssl req -new -x509 -nodes -days 365 -keyout $(DEST)/eggdrop.key -out $(DEST)/eggdrop.crt -config ssl.conf \ -subj "/O=Eggheads/OU=Eggdrop/CN=Self-generated Eggdrop Certificate" install: ainstall dinstall: eggdrop @$(MAKE) ainstall sinstall: static @$(MAKE) ainstall ainstall: install-bin install-modules install-data install-text \ install-help install-language install-filesys install-webui install-doc install-scripts @$(install-end) install-start: @if test ! -f $(EGGEXEC); then \ echo ""; \ echo "You haven't compiled Eggdrop yet."; \ echo "To compile Eggdrop, use:"; \ echo ""; \ echo " make [target]"; \ echo ""; \ echo "Valid targets: 'eggdrop', 'static', 'debug', 'sdebug'."; \ echo "Default target: '@DEFAULT_MAKE@'."; \ echo ""; \ exit 1; \ fi && \ if test "x$(DEST)" = "x"; then \ echo "You must specify a destination directory."; \ echo "Example:"; \ echo ""; \ echo " make install DEST=\"/home/wcc/mybot\""; \ echo ""; \ exit 1; \ fi && \ if test "$(DEST)" -ef .; then \ echo "You are trying to install into the source directory."; \ echo "That will not work. Please specify a different"; \ echo "install directory with:"; \ echo ""; \ echo "make install DEST=\"/home/wcc/mybot\""; \ echo ""; \ exit 1; \ fi && \ echo "" && \ $(egg_test_run) && \ echo "" && echo "Installing in directory: '$(ABSDEST)'." && \ echo "" && \ if test ! -d $(DEST); then \ echo "Creating directory '$(DEST)'."; \ $(MKDIR_P) $(DEST) >/dev/null; \ fi install-bin: install-start @if test -f $(DEST)/o$(EGGEXEC); then \ rm -f $(DEST)/o$(EGGEXEC); \ fi && \ if test -h $(DEST)/$(EGGEXEC); then \ echo "Removing symlink to archival eggdrop binary."; \ rm -f $(DEST)/$(EGGEXEC); \ fi && \ if test -f $(DEST)/$(EGGEXEC); then \ echo "Renaming old '$(EGGEXEC)' executable to 'o$(EGGEXEC)'."; \ mv -f $(DEST)/$(EGGEXEC) $(DEST)/o$(EGGEXEC); \ fi && \ echo "Copying new '$(EGGEXEC)' executable and creating symlink." && \ $(INSTALL_PROGRAM) $(EGGEXEC) $(DEST)/$(EGGEXEC)-$(EGGVERSION) && \ (cd $(DEST) && $(LN_S) $(EGGEXEC)-$(EGGVERSION) $(EGGEXEC)) install-modules: install-start @if test -h $(DEST)/modules; then \ echo "Removing symlink to archival modules subdirectory."; \ rm -f $(DEST)/modules; \ fi && \ if test -d $(DEST)/modules; then \ echo "Moving old modules into 'modules.old' subdirectory."; \ rm -rf $(DEST)/modules.old; \ mv -f $(DEST)/modules $(DEST)/modules.old; \ fi && \ if test ! "x`echo *.$(MOD_EXT)`" = "x*.$(MOD_EXT)"; then \ if test ! -d $(DEST)/modules-$(EGGVERSION); then \ echo "Creating 'modules-$(EGGVERSION)' subdirectory and symlink."; \ $(MKDIR_P) $(DEST)/modules-$(EGGVERSION) >/dev/null; \ fi; \ (cd $(DEST) && $(LN_S) modules-$(EGGVERSION) modules); \ echo "Copying new modules."; \ for i in *.$(MOD_EXT); do \ $(INSTALL_PROGRAM) $$i $(DEST)/modules-$(EGGVERSION)/; \ done; \ fi install-data: install-start @if test ! -f $(DEST)/eggdrop.conf; then \ $(INSTALL_DATA) $(srcdir)/eggdrop.conf $(DEST)/; \ fi @if test ! -f $(DEST)/eggdrop-basic.conf; then \ $(INSTALL_DATA) $(srcdir)/eggdrop-basic.conf $(DEST)/; \ fi @if test ! -d $(DEST)/logs; then \ echo "Creating 'logs' subdirectory."; \ $(MKDIR_P) $(DEST)/logs >/dev/null; \ $(INSTALL_DATA) $(srcdir)/logs/CONTENTS $(DEST)/logs/; \ fi install-text: install-start @if test ! -d $(DEST)/text; then \ echo "Creating 'text' subdirectory."; \ $(MKDIR_P) $(DEST)/text >/dev/null; \ fi && \ if test ! -f $(DEST)/text/motd; then \ $(INSTALL_DATA) $(srcdir)/text/motd $(DEST)/text/; \ fi && \ if test ! -f $(DEST)/text/banner; then \ $(INSTALL_DATA) $(srcdir)/text/banner $(DEST)/text/; \ fi install-help: install-start +@echo "Copying help files." && \ if test ! "x`echo $(srcdir)/help/*.help`" = "x$(srcdir)/help/*.help"; then \ if test ! -d $(DEST)/help; then \ echo "Creating 'help' subdirectory."; \ $(MKDIR_P) $(DEST)/help >/dev/null; \ fi; \ for i in $(srcdir)/help/*.help; do \ $(INSTALL_DATA) $$i $(DEST)/help/; \ done; \ fi && \ if test ! "x`echo $(srcdir)/help/msg/*.help`" = "x$(srcdir)/help/msg/*.help"; then \ if test ! -d $(DEST)/help/msg; then \ echo "Creating 'help/msg' subdirectory."; \ $(MKDIR_P) $(DEST)/help/msg >/dev/null; \ fi; \ for i in $(srcdir)/help/msg/*.help; do \ $(INSTALL_DATA) $$i $(DEST)/help/msg/; \ done; \ fi && \ if test ! "x`echo $(srcdir)/help/set/*.help`" = "x$(srcdir)/help/set/*.help"; then \ if test ! -d $(DEST)/help/set; then \ echo "Creating 'help/set' subdirectory."; \ $(MKDIR_P) $(DEST)/help/set >/dev/null; \ fi; \ for i in $(srcdir)/help/set/*.help; do \ $(INSTALL_DATA) $$i $(DEST)/help/set/; \ done; \ fi && \ cd src/mod/ && $(MAKE_INSTALL) install-help install-language: install-start +@echo "Copying language files." && \ if test ! "x`echo $(srcdir)/language/*.lang`" = "x$(srcdir)/language/*.lang"; then \ if test ! -d $(DEST)/language; then \ echo "Creating 'language' subdirectory."; \ $(MKDIR_P) $(DEST)/language >/dev/null; \ fi; \ for i in $(srcdir)/language/*.lang; do \ $(INSTALL_DATA) $$i $(DEST)/language/; \ done; \ fi && \ cd src/mod && $(MAKE_INSTALL) install-language install-filesys: install-start @if test ! -d $(DEST)/filesys; then \ echo "Creating skeletal filesystem subdirectories."; \ $(MKDIR_P) $(DEST)/filesys >/dev/null; \ $(MKDIR_P) $(DEST)/filesys/incoming >/dev/null; \ fi # We cannot do this in webui.mod Makefile because there is no make target and # we cannot create such make target without breaking every other mod Makefile. # The only alternative i see would be to hardcode the 3 files into char arrays # but this is ugly. File systems were invented for a reason. install-webui: install-start @if test ! -d $(DEST)/webui; then \ echo "Creating webui subdirectory."; \ $(MKDIR_P) $(DEST)/webui >/dev/null; \ fi && \ if test ! -f $(DEST)/webui/apple-touch-icon.png; then \ $(INSTALL_DATA) $(srcdir)/src/mod/webui.mod/apple-touch-icon.png $(DEST)/webui/; \ fi && \ if test ! -f $(DEST)/webui/favicon.ico; then \ $(INSTALL_DATA) $(srcdir)/src/mod/webui.mod/favicon.ico $(DEST)/webui/; \ fi && \ if test ! -f $(DEST)/webui/index.html; then \ $(INSTALL_DATA) $(srcdir)/src/mod/webui.mod/index.html $(DEST)/webui/; \ fi install-doc: install-start +@$(INSTALL_DATA) $(srcdir)/README $(DEST) && \ $(INSTALL_DATA) $(srcdir)/FEATURES $(DEST) && \ cd doc/ && $(MAKE_INSTALL) install install-scripts: install-start +@cd scripts/ && $(MAKE_INSTALL) install; \ cd ../src/mod && $(MAKE_INSTALL) install-scripts #safety hash eggheads-eggdrop-9c00fe1/NEWS000066400000000000000000000223471524730042700160320ustar00rootroot00000000000000News Last revised: September 6, 2026 _________________________________________________________________ What's new? This file lists major and incompatible changes in Eggdrop versions. You can find the full list of changes in doc/Changes1.10. For support, feel free to visit us on Libera #eggdrop. For upgrading help, read the UPGRADING file. In general, always make a BACKUP of your config and user/chanfile before performing an upgrade! _________________________________________________________________ Eggdrop v1.10.2rc1: General changes: - Added the ability to implement extended bans - Added initial handling of arbitrary modes via 005 messages - Fixed compile warnings when using openssl 4.0.0 - Fixed a type error when compiling with Tcl 9 - Fixed an issue where rehashing causes an issue releasing and reopening a listen port, causing Eggdrop to quit - Added option for unique systemd unit naming to autobotchk - Added support for declaring a python virtual environment when starting with autobotchk Botnet changes: - None Tcl API changes: - None Module changes: - Fixed traffic counter in transfer.mod - Fixed a crash condition in the notes module when adding a note Eggdrop config changes: - Added Python module to eggdrop-basic.conf - Removed the "offset" setting from the config - Updated timezone setting description to use "time setting" instead of "timezone" - Use time setting in set enz(TZ) Documentation changes: - Added partyline help to python.mod - Updated methods to connect to partyline - Updated net-type settings to reflect current usage _________________________________________________________________ Eggdrop v1.10.1: General changes: - Added a new SCRAM authentication method for SASL - Fixed an issue where the bot can crash if the bot parts while the channel memberlist is not yet synched - Removed TLS commands deprecated by openssl 3.0 - Fixed a 1.10.0 bug where userhost-in-names was always used if offered by the server, even if the bot did not request it - Account changes are now logged under the +j console flag, not the +k console flag - Fixed a bug handling long server or password strings in .jump - Fixed a possible crash condition if a bot is marked as away - Added compatibility for a change by inspircd adding extra :s to for all trailing commands - Fixed a bug where putlog timestamps may be off by a second - Add notice of expired TLS certs at startup and owner login - Fixed malformed content in the language files Botnet changes: - Added a check to ensure TLS handshake is complete before testing/writing to socket, eliminating many of those annoying "dequeue_sockets(): errno = 11 (Resource temporarily unavailable) on 7" log messages during botnet linking Tcl API changes: - Added a new CHANSET bind, allowing users to trigger Tcl procs when a a channel setting is modified - Removed a non-casesensitive comparison for non-stacked binds that prevented proc names from being overwritten - Fixed an issue with utimers ignoring the [count] argument - Fixed an issue where calling a bind log improperly clears the Tcl interpreter result, causing issues with subsequent processing Module changes: - Removed python.mod's reliance on IRC mod, thereby preventing a crash condition when unloaded (such as on a .restart) - Similar to .tcl, the .python command now requires the owner flag - Fixed a bug in the python module where storage was not properly segmented by threads - Fixed a bug introduced in 1.9.4 where unloading the IRC module caused a crash - Fixed an issue with PBKDF2 where it did not properly 'make' on certain systems (such as NetBSD) Eggdrop config changes: - Added "SCRAM-SHA-512" SASL authentication method to config Documentation changes: - Added python script tutorial - Added documentation for SASL SCRAM mode - Fixed userfile tutorial formatting - Fixed utimers description to specify the return value is seconds, not minutes - Updated Cygwin install requirements _________________________________________________________________ Eggdrop v1.10.0: General changes: - Added the new Tcl 'autoscripts' capability. By loading the autoscripts Tcl script, Eggdrop can automatically view, download and install Tcl scripts hosted by the Eggheads team, all via the partyline! No modification of the config file needed. - Eggdrop can now use the account a user is logged into as an identification method in the same manner that a hostmask does. For this feature to be fully accurate, Eggdrop use a server with WHOX enabled and negotiate the extended-join and account-notify IRCv3 capabilities. - Added the IRCv3 userhost-in-names capability. This capability is negotiated with the server at connect and prompts the server to add hostmask to the NAMES command. This is useful in a scenario where an IRC server has disabled the WHO command, but allows Eggdrop to still track hostmasks (and removes the "pending" status from the channel listing under .status). - Added the IRCv3 standard-replies capability. This capability is negotiated with the server at connect and enables the use of non-numeric FAIL, WARN, and NOTE messages. - Modified .bans to properly display channel bans. - Fixed a bug with network reads performed on TLS connections, where Eggdrop could hang until a connection times out, most commonly manifesting itself on server connects and userfile transfers. This is expected to fix the last documented issue with Eggdrop's handling of secure connections. - Eggdrop now requires TLS/SSL libs to be installed by default. Whereas previously Eggdrop would previously continue to compile if TLS libraries were not found, it will now error unless the user explicitly specifies the --disable-tls flag. - Fixed the Monitor bind to properly use wildcards in binds. - Updated language files from ISO-8859 to UTF-8. Botnet changes: - Fixed an issue in pbkdf2-only links properly using/comparing PASS2 entries Tcl API changes: - Updated much of the core Tcl integration to be compatible with the upcoming Tcl 9 release. Much work was done (thank you DasBrain!) to update Eggdrop's internal Tcl calls to prevent breakages using Tcl 9. Most of these changes are transparent to the user, but the one major item to call out is the improvement of Tcl's UTF-8 emoji handling, which no longer requires users to modify TCL_MAX and compile Tcl manually in order to use emojis properly. - The Python module (below) adds the pysource Tcl command, to load a python script into Eggdrop. - Added the Tcl CHGHOST bind, which is triggered when a user's hostmask is changed via an IRCv3 CHGHOST message. - Added the 'hidden-host' type to the event (EVNT) bind. This bind is triggered when the bot's own host is hidden (+x) by a 396 message sent by server. - Added the 'got-chanlist' type to the event (EVNT) bind. This bind is triggered once Eggdrop finishes receiving the list of usernames for a channel from a server. This can be used when Eggdrop needs to wait to perform specific functions when joining a channel but needs to wait until the users on a channel have been presented by the server. - Fixed a bug in the isidentified command to check if a user has definitively been identified or unidentified. Module changes: - Added a Python module! This module integrates an embedded Python interpreter similar to the Tcl interpreter already present in Eggdrop. This module adds the .python command to the partyline (again, similar to the .tcl command) to execute python strings, as well as adding the pysource Tcl command that will load a Python script into Eggdrop. See doc/modules/mod.python for details on how to use it, or example scripts in the scripts/ directory. Eggdrop config changes: - The copy-to-tmp option was removed from the config. This value is now functions under the old '1' behavior where an intermediate temp file is created before copying from/to files that are in use. - The quick-logs option was removed from the config. This value was created to hedge against frequent writes to disk, but is less relveant with today's technology. Eggdrop now writes logfiles to disk immediately. - The old DNS module section was removed - A Python module section was added, but does not load the python module by default. - The Autoscripts capability to load and manage some scripts from the partyline is loaded by default Documentation changes: - Added documentation that covers values commonly used when writing new Tcl binds in C - Added a tutorial to demonstrate how to share userfiles - Added version variable for document generation ________________________________________________________________________ Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/README000066400000000000000000000224271524730042700162120ustar00rootroot00000000000000README Please, at least SKIM this document before asking questions. In fact, READ IT if you've never successfully set up an Eggdrop bot before. NOTICE Make SURE that you select your +n (owner) users wisely. They have 100% access to your bot and account. ONLY GIVE THIS POWER TO SOMEONE YOU TRUST COMPLETELY! WHAT IS EGGDROP? Eggdrop is the world's most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature-rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms. An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc. One of the features that makes Eggdrop stand out from other bots is module and Tcl and Python scripting support. With scripts and modules you can make the bot perform almost any task you want. They can do anything: from preventing floods to greeting users and banning advertisers from channels. You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban/exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet. Eggdrop is always being improved and adjusted because there are bugs to be fixed and features to be added (if the users demand them and they make actually sense). In fact, it existed for several years as v0.7 - v0.9 before finally going 1.0. This version of Eggdrop is part of the 1.9 tree. A valiant effort has been made to chase down and destroy bugs. This README file contains information about how to get Eggdrop, command line options for Eggdrop, what you may need to do when upgrading from older versions, a list of frequently asked questions, how to set up a crontab, some boring legal stuff, some basics about git usage and some channels where you might get help with Eggdrop. HOW TO GET EGGDROP There are two official methods to download Eggdrop source code. Alternately, Eggdrop also comes as a docker image. FTP The latest Eggdrop stable source code is always located at https://geteggdrop.com. You can also download the current stable, previous stable, and development snapshot at https://ftp.eggheads.org/pub/eggdrop/source Git Development Snapshot Eggdrop developers use git to manage the Eggdrop codebase for development. If you are interested in trying out the VERY LATEST updates to Eggdrop, you can use git to obtain most recent code from the Eggheads repository. BE WARNED, the development branch of Eggdrop is not to be considered stable and may (haha) contain significant bugs still being worked on. To obtain Eggdrop via the git repository (hosted by GitHub), you can either clone the repository via git or download a development snapshot. To clone the repository, simply type: git clone https://github.com/eggheads/eggdrop.git Otherwise, you can download the development snapshot as a tar archive from: https://github.com/eggheads/eggdrop/archive/develop.tar.gz Docker You can pull the official Eggdrop Docker image via: docker pull eggdrop:latest Additional Eggdrop Docker documentation can be found at https://hub.docker.com/_/eggdrop SYSTEM PRE-REQUISITES Before you can compile Eggdrop, Tcl must be installed on your system. Many systems have Tcl installed on them by default (you can check by trying the command "tclsh"; if you are given a '%' for a prompt, it is, and you can type 'exit' to exit the Tcl shell. However, Eggdrop also requires the Tcl development header files to be installed. They can often be installed via an OS package manager, usually called something similar to 'tcl-dev' for the package name. You can also download Tcl source from https://www.tcl.tk/software/tcltk/download.html. Eggdrop also requires openssl (and its development headers) in order to enable SSL/TLS protection of network data. The header files are often called something similar to 'libssl-dev'. While not advised, this requirement can be removed by compiling using ./configure --disable-tls, but you will not be able to connect to TLS-protected IRC servers nor utilize secure botnet communication. MINIMUM REQUIREMENTS Some components of Eggdrop relies on a variety of third-party libraries, documented here. ----------------------------------------------------------------------- Functionality Package Minimum Version ------------------------------- ------------------- ------------------- Tcl interpreter (required) Tcl Dev Library 8.5.0 Secure communication OpenSSL 0.9.8 Python module Python 3.8.0 Compression module zlib Any ----------------------------------------------------------------------- QUICK STARTUP Please see the Install file after you finish reading this file. UPGRADING The upgrade process for Eggdrop is very simple, simply download the new source code and repeat the compile process. You will want to read the NEWS for any new configuration file settings you want to add. Please see Upgrading for full details. COMMAND LINE Eggdrop has some command line options - not many, because most things should be defined through the config file. However, sometimes you may want to start up the bot in a different mode and the command line options let you do that. Basically, the command line for Eggdrop is: ./eggdrop [options] [config-file] The options available are: -t: Don't background, use terminal. Your console will drop into an interactive partyline session, similar to a DCC chat with the bot. This is useful for troubleshooting connection issues with the bot. -c: Don't background, show channel info. Every 10 seconds your screen will clear and you will see the current channel status, sort of like "top". -m: Create userfile. If you don't have a userfile, this will make Eggdrop create one and give owner status to the first person that introduces himself or herself to it. You'll need to do this when you first set up your bot. -h: Show help, then quit. -v: Show version info, then quit. Most people never use any of the options except -m and you usually only need to use that once. AUTO-STARTING EGGDROP Systems go down from time to time, taking your Eggdrop along with it. You may not be not around to restart it manually, so you can instead use features of the operating system to automatically restart Eggdrop should it quit for any reason. Eggdrop comes with an autobotchk shell script that can create either a systemd or crontab entry. The systemd option will monitor your Eggdrop and a) start it when the machine boots and b) restart the Eggdrop if it crashes for any reason. The (older) crontab option will check (by default) every 10 minutes to see if your Eggdrop is still running, and attempt to restart it if it is not. To auto-generate a systemd job, from the Eggdrop install directory, simply run: ./scripts/autobotchk -systemd To auto-geneerate a script to check Eggdrop's status and run it via a crontab entry, simply run: ./scripts/autobotchk This will crontab your bot using the default setup. If you want a list of autobotchk options, type './autobotchk'. A crontab example with options would be: ./scripts/autobotchk -noemail -5 This would setup crontab to run the botchk every 5 minutes and not send you an email saying that it restarted your bot. DOCUMENTATION We're trying to keep the documentation up to date. If you feel that anything is missing here or that anything should be added, etc, please create an issue, or better yet a pull request, at https://www.github.com/eggheads/eggdrop Thank you! OBTAINING HELP You can obtain help with Eggdrop in the following IRC channels: - Libera Chat - #eggdrop (official channel), #eggheads (development discussion) - DALnet - #eggdrop - EFnet - #egghelp - IRCnet - #eggdrop - QuakeNet - #eggdrop.support - Undernet - #eggdrop If you plan to ask questions in any of the above channels, you should be familiar with and follow IRC etiquette: - Don't type using CAPITAL letters, colors or bold. - Don't use "!" and "?" excessively. - Don't /msg people without their permission. - Don't repeat or paste more than 4 lines of text to the channel. - 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 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/THANKS000066400000000000000000001042671524730042700162500ustar00rootroot00000000000000Eggdrop Developers and Contributors ------------------------------------------------------------------------------ This file contains a list people who have contributed to Eggdrop. These contributors sent bug reports, improved the code or documentation, or otherwise offered support to the Eggdrop project. Individual contributions are listed in the doc/Changes files included with Eggdrop. If you feel your name is missing from this list, if you'd rather not be listed, or if your information is incorrect, please send an email to bugs@eggheads.org. Developers and major contributors: arthur2 Jerome Carre arthur2@demoniak.hay-france.com BarkerJr Bryon Eldridge barkerjr@barkerjr.net Beldin Darrin Smith beldin@beldin.org bryand Bryan Drewery bryan@shatow.net ButchBub Scott G. Taylor staylor@mrynet.com Cizzle Christophe Beauval Cizzle@users.noreply.github.com cmwagner Chad Wagner cmwagner@sodre.net Cybah Jonathan Miles jon@zetnet.net Daklop darko`` Darko Ilic darko@onvol.net dk Steven Packard (Dynamite Kid) spackard@moran.mines.edu drummer Dobos Lorant drummer@buli.sk dw Per Johansson dw@lixom.nu EraseMe pyro@imag.net Ernst Ernesto Baschny ernst@baschny.de Eule Axel Franke eule@berlin.snafu.de Fabian Fabian Knittel fabian.knittel@avona.com Fred1 Chris Fuller G`Quann Florian Sander gquann@gmx.de Geo Geo Van O geo@eggheads.org guppy Jeff Fisher jeff@zaf.ca ITE Federico Mennite ite@freex.ch Jason Jason Ede j.d.ede@sheffield.ac.uk John` John Johnny@technik.sth.ac.at KingKurly Gregory Kubaryk kurly@mindless.com Kirben Travis Howell kirben@optushome.com.au Kirk Ian Kaney ikaney@crisiant.com mortmann Michael Ortmann ortmann@posteo.de LuckyStar lucky@binet.lv NC poptix Matthew Hallacy poptix@poptix.net pseudo Rumen Stoyanov pseudo@egg6.net Q Q@ping.be Robey Robey Pointer robey@lag.net rfc rfc@undernet.org rtc Peter 'Rattacresh' Backes rtc@helen.plasma.xg8.de simple Jonathan G. Rudolph skralg@gmail.com stdarg stdarg@yahoo.com Sup Teemu Hjelt sup@techmonkeys.org SuperS Kevin Lamothe supers@starblaze.2y.net Sven Sven Trenkel list@semidefinite.de TaKeDa Dariusz Kulinski takeda3@netzero.net TheGhost James Simmons tg@linuxpackages.net thommey Thomas Sader thommey@gmail.com toot Toby Verrall toot@holborn.melnet.co.uk Tothwolf tothwolf@techmonkeys.org Wcc Will Buckner wcc@techmonkeys.org Wiktor Wiktor Wodecki wodecki@gmx.de Wild Mike Hartman wild@klondike.com Contributors: os2warp@invlogic.com Turner@Undernet pepsi@gear.torque.net vince@who.net zathras3@hotmail.com a person Aaron Zollman aaronwl aba abfab Abraham Absinthe Adam Spiers Adze afterlife Al3X Alex alex@bmotel.com alex323 Amir Shalem amir@boom.org.il AmnesiAc Amun Andrej Andrej Presern andrejp@luz.fe.uni-lj.si Andy andy5995 Andy Alt andyqwerty@users.sourceforge.net Anon-e-mouse anon-e-mouse@anon-e-mouse.co.uk anonymous answer archimede archon argg Paul Anderson xerox@foonet.net Arkadietz Kiril Georgiev arkadietz@yahoo.com Armand udf-xerxes@barrysworld.com Arno arthur2 arthur2 axel_v aXs ayman azurIt B. Jamison Bass Beige Ben Barton Ben Dover eggdrop_soup@hotmail.com Benny Bert Bohla bhab Kelsey Hudson khudson@compendium.us bill Bill Brandt bill-tcl billyjoe bingony blackjac Blacky blaster^ blasterb@wanadoo.fr Ble Bruce brucee_ca@yahoo.com bleah Bob BoGuS Boing Boris Capitanu boro Bosko Bow Sineath Brad Edwards brenny Brian Brian T. Bruce Gingery Bruce S. Bryan Dolan bUrN Tobias Schwind Butthead By-Tor bytor@netside.com C. Massam C4thY capster Carlin0 Carlo Ponti CFusion Chad Fawcett CHaiNeSS chaos Charvel Chotaire chotaire@chotaire.moc.kw Chriphil Chris Northwood Chucky Burnett Cizzle Christophe Beauval Cizzle@users.noreply.github.com ClearlyJa ClubCX clusters CoderX2 cold fusion coolio coop Cosmo CrazyCat crazycat@c-p-f.org creatio Creative1 Joaquin Grech creative1@bigfoot.com Crotale cuentero Curt Lewis cyberscape CyberTech C_Olli d-e-m Daemus Dagmar daimonic Gary Wall gary@daimonic.org dalbien dan Dan Anatol Dania Stolfi Daniel danny dannyboy DarkDruid darkfall Darkfox Darki darknight DarkReap1 darkshde Darude DasBrain Johannes Kuhn data davd David Newhall II david@sleepers.net Dave Guzeman David David Brauman David Brown David Sesno David- De Kus deadgrrrl deaf DeathHand debaser Dejan Oklobdzija Del Carter demon denali DennisMV dereckson derrick DFrank Dicctr0s Digdilem Simon Avery flash@digdilem.org digital dirtymac DocSavag Jerry Sutton jerrystn@ccse.net dollar Domino Donovan Long Dopsy dopsy@orastie.org dorgan dork dracwolf DrDeath drnet Justin W. Pauler justin@jwpauler.org dtM Dude Matthew Stiefel matt@snip.net dunk dvlsadvct DVS01 Denis dvs01@dvs01.net easton Ec|ipse Eden edmiester eelcohuininga Eelco Huininga Ehrenberg ejm Emmanuel Marty Empus Erick- Erick Velez erick@erick.org eryg-kai Evo|ver exile Exterminador Vitor droider.bnc@gmail.com eye Fabian Knittel fabian.knittel@avona.com Fabulous fabulous@brasnet.org fasticus FeaRx Felix Felix3339 fhorst FirebaII fireball@lanparty.ch FireEgl Philip Moore fireegl@gmail.com Flame Lee Hardy flame@melnet.co.uk flammable Dan Pike flammable@start.com.au flash Flattie flyin lion foxmulder Fraggle Jérôme Benoit jerome.benoit@grenouille.com Freeder freeder@techmonkeys.org fuchs Bjoern Krombholz bjkro@gmx.de FumBa FuzzBuster Scott R. Godin mactech@webdragon.net fx2 garbanzo Alex Zepeda garbanzo@hooked.net gavroche Jamie Rishaw jamie@arpa.com genady Geo Geo Van O geo@eggheads.org Georg Schwarz Giga Gilles d'Andrea Gisselle Giuseppe Caulo goo GoodGuy Gord- Gordon Lloyd Goldberg ggold@panix.com grawity Mantas Mikulenas grawity@nullroute.eu.org GregMo Greg Mosier me@gregmo.us grnbrg Brian Greenberg grnbrg@grnbrg.org GSCEGO GTo Grigoris Ntotsis gto@the.forthnet.gr Gumbie gumbie@sunspot.tiac.net guruz guy gwyn Habeeb hal Hanno Harekiet Sjoerd van der Berg Hath hath@cyberops.org hdclown herz hobb hoopycat Hue Truong Hunger iamnimnul iceman Igmar illi imoq IpAddress islandic izy` J. Lehto jack3 the_kink_@hotmail.com jackal^ James James Colton Jason Ede j.d.ede@sheffield.ac.uk jay Jay Maynard Jay S. Monk jedis Jeff Fisher jeff@zaf.ca jeffx Jerome jerrold Jesse M. Jesse Schachter Jim Marco jkw jman Jobe Michael Beecher Joe Morris John Fulton Johnny Johnny Mnemonic johnny@themnemonic.org johoho johoho Joker jonte Jordan Jordy Julie junkie Juvenal Jz Karsten Keith Tyler Kelvin Kendrick Vegas keyoke kibbles KingBob Kinslayer Gaven Cohen dragon@wastelands.net kisser Koach Komandar Kool Cat kool_cat@softhome.net Krambaek Krome Kuja KuNgFo0 kungfo0@techmonkeys.org L0RE labtec lag-linaro Lam Larry lasher- Laurens v. Alphen ldm ledpighp lee Lefty Jeff Hartman lefty@sojourn.com LethalWP LinaSovereign Lobo^Loco Lord255 G skdlord.warrior@gmail.com Looser^ LSC LtGen Luca De Roberto Lucas Lucas Nussbaum lucas@lucas-nussbaum.net Luke Detering M. Koszik madhack maimizuno Manual Hernandez manuel manuel.leiner@gmx.de masskilla Mastr Matthew Hallacy poptix@poptix.net MBroLad mc Rico Gloeckner mc@verdinet.de MC_8 Carl M. Gregory mc8@purehype.net MeNearly Melvan metatron mho mtl@videotron.ca MHT ManHatTan mht@mygale.org Michael Michael D. Smith mike.ds@tiscali.co.uk Michele Miguel Ventura Mikael Mikael Hammarin Mikael Hedberg Mike Hammarin Mike McLagan Mikko Vester miltrypet mish Lev mish@mydestiny.net Mixter mixter@mixter.org mjg Mloe Mo-Ize Guillaume Leclanche mo-ize@nul-en.info modus operandi Mohammad Ali Rezaei monkers moonwolf MORA mortmann Michael Ortmann ortmann@posteo.de Mr. Wizard mrbond mrBuG mrbug@eggdrop.org.ru Mr_Jode MsingLnk MULTITUDE Murf mw nakee Ely Levy elylevy@cs.huji.ac.il NaTaS Nate Gardner Neale Pickett NeO-MeTaL NeoN nervous NESS NetG0D NetIrc neuro never Sebastian Schwardt never@nolife.org Nicholas J. Dear Nick_ nick@null.net Nico Golde nion@debian.org Nidhogg Niggurath Shub Niggurath rm@fh-worms.de NigtHuntr Nils Østbjerg shorty@business.auc.dk nitemoon NML_375 Fredrik Löhr Nobody node nomis Simon Arlott 70171+nomis@users.noreply.github NoPleX Noyga odidev offspring okey OldGhost OldGroo Oliver Olrick OpTiC-?X Paladin Alan Cameron paladin@techmonkeys.org Paladinz panasync paralyse paran0id paranorml paravoid partek pattyt Paul E. PaulBoehm peace peace@shell.webmaster.com PeGaSuS droider.pc@gmail.com Phoenix piglet pinchaser Pixelz Rickard Utgren rutgren@gmail.com pjb pjb@decafgeek.org plan9 Plex plof poseidon PPSlim Phil Richardson ppslim@acarr.plus.com Preston Prez Prime project10 proton prox pseudo Rumen Stoyanov pseudo@egg6.net pteron Andy Repton eggdrop@pteron.org QuakeMstr quest quest@mac.com R. Brooklyn R. Ramos RaakQ Kimmo Varis kimmov@gmail.com ragtop Raistlin Jason Slagle (Raistlin Majere) raistlin@tacorp.net RandomAPB Randy Summerfiled ranjha rastam0n rawdon rco133 ReBEL RebuM Martin Matuska rebum@gmx.at redbird ReDDawG Mike Chambers mike@netlyncs.com reed Regan Wallace Rich Rick Cuoto ridens Robby robby@chatbelgie.be Robert Brice Roger Yerramsetti Rok Papez romulus rsc Robert Scheck Rufus S Wilcox S7reaM sabi Sandoz sandvik sbraz Louis Sautier scipio Scorpius scott ScottDrake Scuzzi seadawg Sean T. Segfault Ryan Butler rbutler@tsss.org seljo senpai seth Seth Mattinen shadow Shane Shane0 Shaun Shaun Brown mastr@irrelevant.net Shawn888 Shayne shovon shrike sid3windr Tom Laermans tom.laermans@powersource.cx sidrus blfrie@gmail.com simple Jonathan Rudolph skralg@gmail.com simply sirfz Fayez iamfayez@gmail.com skiidoo Thomas Faucher aza.skiidoo@gmail.com Skorpion SKY skyline slennox Shayne Lennox slennox@egghelp.org slenny slix sloot slt slug Chris Porter slug@quakenet.org slyngshede smash Patrick Ringl patrick_@freenet.de smok softlord Solal somni sorta Souperman Graeme Donaldson webmonster@eggdrop.za.net spacey sprite stary station stdarg stdarg@yahoo.com Stephen Hunter Stephen P. Clouse stephenc SteppenWolf Steven Nikkel stomper Stream strolchi Stu Stu Jones suizide sx66627 Sergey symband symbands T. Salomäki tabo tabb tartarus taz-man tchaika tdmarti TheAvatar Søren Jensen sjj@eushells.com thembones TheQ TheUnknown TheUnknown@Bigfoot.com The_Dawn Thomas Thomas Neumayer Thomas Sader thommey@gmail.com thx-1138 timothy Timothy Barbeisch Ting Ronny Vårdal rv@umbraco.nu Tit00n TiTi Toblerone tolim Tom Rini Tomas Szaniszlo Toon topcat Tori Tothwolf tothwolf@techmonkeys.org trey tris trojan Trond Refsnes troy Troy Davis tuvix tuvok tyson upstream Christian Larsen upstream@shell2.lomag.net Uwe Schindler uwe@thetaphi.de Vassago Ryan Addams rowan@unix.mclv.net vBm vern-n vertex Vertigo Sergey Goltsov sergiosolnet@gmail.com vod Roger Stone capsterx@hotmail.com void VYOinLove Olbosanu Viorel vyoinlove@yahoo.com Wade Wanderer wanderer@telenet.be Warmage WauloK Jason Oakley waulok@bangrocks.com waxmaster wayhigh Kevin Lynn klynn@kevinlynn.com Webbie Peter Chiu webbie@ipfw.org wheely Kevin Walker kevin@racbot.dot.org Whicked Whilor Arne Beyer whilor@whilor.de WildCraze wilk Marcin L wilq.pl@vp.pl Wcc Will Buckner wcc@techmonkeys.org Wingman Christian Birkl wingman@techmonkeys.org winkey Brian Case rush@winkey.org wrath Wull wnelson@enterprise.net wylie Todd Wright wylie@geekasylum.org xberry BBerry xberry@slo.net Xerxes Lonnie Denison XGen xian Xtoper zamf zarni zarni@weirdness.com Zart Konstantin Zemlyak zart@zartsoft.ru ZarTek ZarTek Creole zartek.creole@gmail.com Ze Yann Richard ze@nbox.org zenoran ZeveRoaRe RoeLt zeveroar@phreaker.net ziffie ZiMiaS zip Ian Campbell zip@techmonkeys.org [Eazy|E] [joco] [secret] [sL] strikelight@tclscript.com ]x[ \-\itman ^Baron^ Zev Toledano eggdrop@thelastexit.net ^PRS4^ James P. prs4@hotmail.com ^You^ |mmortal |SKY| |^Raven^| eggheads-eggdrop-9c00fe1/UPGRADING000066400000000000000000000065261524730042700165770ustar00rootroot00000000000000Upgrading Eggdrop It is easy to upgrade Eggdrop to a new version! To have a full picture of the changes made since your last upgrade, we recommend reading the NEWS file. Upgrades from the 1.8 and 1.9 lines of Eggdrop should take place with little to no issues. While the config file, user files, and channel files can all be reused, it is recommended to review the NEWS files for each release to identify new configuration settings that have been added. For support, feel free to visit us on Libera #eggdrop. HOW TO UPGRADE To upgrade to a newer version of Eggdrop, first backup your .config file, .user file, and .chan file. While they are saved and should not be overwritten by the upgrade process, it never hurts to make anothere copy :) Next, view the NEWS file to see what changes have been made, paying particular attention to the config file section to see what new/different commands have been added. Update your config file appropriately. Then, simply follow the same steps you followed to install Eggdrop previously. Download the source, unzip/untar it, and run the './configure', 'make config', 'make', and 'make install' commands. Restart your Eggdrop and you will be up and running with the latest version of Eggdrop. MUST-READ CHANGES FOR EGGDROP V1.10 These are NOT all the changes or new settings; rather just the "killer" changes that may directly affect Eggdrop's previous performance without modification. Config file changes To migrate from a 1.8 to a Eggdrop, some changes are suggested to be made in your configuration file: - Eggdrop has deprecated the blowfish module for password hashing in favor of the PBKDF2 module. This is a BIG change which, if done carelessly, has the potential to render stored passwords useless. Please see doc/PBKDF2 for information on how to properly migrate your userfiles and passwords to the new module. - Eggdrop 1.9 switched from the "set servers {}" syntax to the "server add" command. For example, if your configuration file previously had: set servers { my.server.com:6667 } you should now instead use: server add my.server.com 6667 Please read the config file for additional examples - Eggdrop no longer requires the '-n' flag to start Eggdrop in terminal mode. Modules While most 3rd party modules that worked on older Eggdrop versions should still work with Eggdrop , many of them contain a version check that only allows them to run on 1.6.x bots. We have removed the version check from some of the more popular modules provide them at https://ftp.eggheads.org/pub/eggdrop/modules/1.10/ Scripts All 3rd party Tcl scripts that worked with Eggdrop versions as early as v1.6 should still fully work with Eggdrop . Botnet In Eggdrop v1.8, Eggdrop bots would automatically attempt to upgrade any botnet link to an SSL/TLS connection. Since v1.9, the user is required to explicitly request an SSL/TLS connection by prefixing the port with a '+'. If you wish your botnet to take advantage of encryption, use the .chaddr command to update your ports to start with a '+'. Tcl Commands A lot of backwards-compatible additions and changes have been made to Tcl commands. Please look at doc/tcl-commands.doc to see them. Documentation Documentation has been updated to reflect new and removed commands and variables. eggheads-eggdrop-9c00fe1/aclocal.m4000066400000000000000000001325261524730042700171740ustar00rootroot00000000000000dnl aclocal.m4: macros autoconf uses when building configure from configure.ac dnl dnl Copyright (C) 1999 - 2025 Eggheads Development Team dnl dnl This program is free software; you can redistribute it and/or dnl modify it under the terms of the GNU General Public License dnl as published by the Free Software Foundation; either version 2 dnl of the License, or (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. dnl dnl Load tcl macros builtin(include,m4/tcl.m4) dnl Load python macros builtin(include,m4/python.m4) dnl Load gnu autoconf archive macros builtin(include,m4/ax_check_compile_flag.m4) builtin(include,m4/ax_create_stdint_h.m4) builtin(include,m4/ax_lib_socket_nsl.m4) builtin(include,m4/ax_pthread.m4) builtin(include,m4/ax_type_socklen_t.m4) dnl dnl Message macros. dnl dnl EGG_MSG_CONFIGURE_START() dnl AC_DEFUN([EGG_MSG_CONFIGURE_START], [ AC_MSG_RESULT AC_MSG_RESULT([This is Eggdrop's GNU configure script.]) AC_MSG_RESULT([It's going to run a bunch of tests to hopefully make your compile]) AC_MSG_RESULT([work without much twiddling.]) AC_MSG_RESULT ]) dnl EGG_MSG_CONFIGURE_END() dnl AC_DEFUN([EGG_MSG_CONFIGURE_END], [ AC_MSG_RESULT([Type 'make config' to configure the modules, or type 'make iconfig']) AC_MSG_RESULT([to interactively choose which modules to compile.]) AC_MSG_RESULT ]) dnl EGG_MSG_SUMMARY() dnl dnl Print summary with OS, TLS and TCL info. AC_DEFUN([EGG_MSG_SUMMARY], [ AC_MSG_RESULT([Operating System: $egg_cv_var_system_type $egg_cv_var_system_release]) AC_MSG_RESULT([IPv6 Support: $ipv6_enabled]) ADD="" if test "x$TCL_THREADS" = "x1"; then ADD=" (threaded)" fi AC_MSG_RESULT([Tcl version: $TCL_PATCHLEVEL$ADD]) ADD="" if test "x$tls_enabled" = "xyes"; then EGG_FIND_SSL_VERSION if test "x$tls_version" != "x"; then ADD=" ($tls_version)" fi fi AC_MSG_RESULT([SSL/TLS Support: $tls_enabled$ADD]) AC_MSG_RESULT([Threaded DNS core: $tdns_enabled]) AC_MSG_RESULT ]) dnl EGG_FIND_SSL_VERSION() dnl dnl Tries to find the SSL version used AC_DEFUN([EGG_FIND_SSL_VERSION], [ tmpout="tmpfile" if test ! -e "tmp.c" && test ! -e "$tmpout"; then cat >tmp.c < #include int main(void) { printf("%s\n", OPENSSL_VERSION_TEXT); return 0; } EOF $CC $SSL_INCLUDES tmp.c -o "$tmpout" >/dev/null 2>&1 if test -x "./$tmpout"; then tls_version=$("./$tmpout") tls_versionf= fi rm -f "$tmpout" tmp.c >/dev/null 2>&1 fi ]) dnl EGG_MSG_WEIRDOS() dnl dnl Print some messages at the end of configure to give extra information to dnl users of 'weird' operating systems. dnl AC_DEFUN([EGG_MSG_WEIRDOS], [ if test "$UNKNOWN_OS" = yes; then AC_MSG_RESULT([WARNING:]) AC_MSG_RESULT AC_MSG_RESULT([ Unknown Operating System: $egg_cv_var_system_type $egg_cv_var_system_release]) AC_MSG_RESULT AC_MSG_RESULT([ Module support has been disabled for this build.]) AC_MSG_RESULT AC_MSG_RESULT([ Please let us know what type of system this is by e-mailing]) AC_MSG_RESULT([ bugs@eggheads.org. The output of uname -a, and some other basic]) AC_MSG_RESULT([ information about the OS should be included.]) AC_MSG_RESULT else if test "$WEIRD_OS" = yes; then AC_MSG_RESULT([WARNING:]) AC_MSG_RESULT AC_MSG_RESULT([ The operating system you are using has not yet had a great]) AC_MSG_RESULT([ deal of testing with Eggdrop. For this reason, this compile]) AC_MSG_RESULT([ will default to "make static".]) AC_MSG_RESULT AC_MSG_RESULT([ To enable module support, type "make eggdrop" instead of just]) AC_MSG_RESULT([ "make" after you run "make config" (or "make iconfig").]) AC_MSG_RESULT AC_MSG_RESULT([ As we have not done a sufficient amount of testing on this]) AC_MSG_RESULT([ OS, your feedback is greatly appreciated. Please let us know]) AC_MSG_RESULT([ at bugs@eggheads.org if there are any problems compiling with]) AC_MSG_RESULT([ module support, or if you got it to work :)]) AC_MSG_RESULT fi AC_MSG_RESULT([If you experience any problems compiling Eggdrop, please read the]) AC_MSG_RESULT([compile guide, found in doc/COMPILE-GUIDE.]) AC_MSG_RESULT fi ]) dnl EGG_APPEND_VAR() dnl dnl Append a non-empty string to a variable dnl dnl $1 = variable dnl $2 = string dnl AC_DEFUN([EGG_APPEND_VAR], [ if test "x$2" != x; then if test "x$$1" = x; then $1="$2" else $1="$$1 $2" fi fi ]) dnl dnl Compiler checks. dnl dnl EGG_CHECK_CC() dnl dnl Check for a working C compiler. dnl AC_DEFUN([EGG_CHECK_CC], [ if test "x$cross_compiling" = x; then cat << 'EOF' >&2 configure: error: This system does not appear to have a working C compiler. A working C compiler is required to compile Eggdrop. EOF exit 1 fi ]) dnl EGG_CHECK_CC_C99() dnl dnl Check for a working C99 C compiler. dnl AC_DEFUN([EGG_CHECK_CC_C99], [ if test "$ac_cv_prog_cc_c11" = no && test "$ac_cv_prog_cc_c99" = no; then cat << 'EOF' >&2 configure: error: This C compiler does not appear to have a working C99/C11 mode. A working C99/C11 C compiler is required to compile Eggdrop. EOF exit 1 fi ]) dnl EGG_HEADER_STDC() dnl AC_DEFUN([EGG_HEADER_STDC], [ if test "$ac_cv_header_stdc" = no; then cat << 'EOF' >&2 configure: error: Your system must support ANSI C Header files. These are required for the language support. Sorry. EOF exit 1 fi ]) dnl EGG_CHECK_ICC() dnl dnl Check for Intel's C compiler. It attempts to emulate gcc but doesn't dnl accept all the standard gcc options. dnl dnl AC_DEFUN([EGG_CHECK_ICC],[ AC_CACHE_CHECK([for icc], egg_cv_var_cc_icc, [ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #if !(defined(__ICC) || defined(__ECC) || defined(__INTEL_COMPILER)) "Toto, I've a feeling we're not in Kansas anymore." #endif ]])], [ egg_cv_var_cc_icc="yes" ], [ egg_cv_var_cc_icc="no" ]) ]) if test "$egg_cv_var_cc_icc" = yes; then ICC="yes" else ICC="no" fi ]) dnl EGG_CHECK_CCPIPE() dnl dnl This macro checks whether or not the compiler supports the `-pipe' flag, dnl which speeds up the compilation. dnl AC_DEFUN([EGG_CHECK_CCPIPE], [ if test "$GCC" = yes && test "$ICC" = no; then AC_CACHE_CHECK([whether the compiler understands -pipe], egg_cv_var_ccpipe, [ ac_old_CC="$CC" CC="$CC -pipe" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ return(0); ]])], [ egg_cv_var_ccpipe="yes" ], [ egg_cv_var_ccpipe="no" ]) CC="$ac_old_CC" ]) if test "$egg_cv_var_ccpipe" = yes; then EGG_APPEND_VAR(CFLAGS, -pipe) fi fi ]) dnl EGG_CHECK_CCWALL() dnl dnl See if the compiler supports -Wall. dnl AC_DEFUN([EGG_CHECK_CCWALL], [ if test "$GCC" = yes && test "$ICC" = no; then AC_CACHE_CHECK([whether the compiler understands -Wall], egg_cv_var_ccwall, [ ac_old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ return(0); ]])], [ egg_cv_var_ccwall="yes" ], [ egg_cv_var_ccwall="no" ]) CFLAGS="$ac_old_CFLAGS" ]) if test "$egg_cv_var_ccwall" = yes; then EGG_APPEND_VAR(CFLAGS, -Wall) fi fi ]) dnl dnl Checks for types and functions. dnl dnl EGG_FUNC_B64_NTOP() dnl AC_DEFUN([EGG_FUNC_B64_NTOP], [ # https://raw.githubusercontent.com/tmux/tmux/2dd9a4fb9cd73987bdca5b8b2f85ca8b1a6e4e73/configure.ac # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. AC_MSG_CHECKING(for b64_ntop) AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[b64_ntop(NULL, 0, NULL, 0);]])],[found_b64_ntop=yes],[found_b64_ntop=no ]) if test "x$found_b64_ntop" = xno; then AC_MSG_RESULT(no) AC_MSG_CHECKING(for b64_ntop with -lresolv) OLD_LIBS="$LIBS" LIBS="$LIBS -lresolv" AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[b64_ntop(NULL, 0, NULL, 0);]])],[found_b64_ntop=yes],[found_b64_ntop=no ]) if test "x$found_b64_ntop" = xno; then LIBS="$OLD_LIBS" AC_MSG_RESULT(no) AC_MSG_CHECKING(for b64_ntop with -lnetwork) OLD_LIBS="$LIBS" LIBS="-lnetwork" AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[b64_ntop(NULL, 0, NULL, 0);]])],[found_b64_ntop=yes],[found_b64_ntop=no ]) if test "x$found_b64_ntop" = xno; then LIBS="$OLD_LIBS" AC_MSG_RESULT(no) fi fi fi if test "x$found_b64_ntop" = xyes; then AC_DEFINE([HAVE_BASE64], [1], [Define if b64_ntop exists.]) AC_MSG_RESULT(yes) else AC_LIBOBJ(base64) fi ]) dnl dnl Checks for programs. dnl dnl EGG_PROG_HEAD_1() dnl dnl This macro checks for the proper 'head -1' command variant to use. dnl AC_DEFUN([EGG_PROG_HEAD_1], [ cat << 'EOF' > conftest.head a b c EOF for ac_prog in 'head -n 1' 'head -1' 'sed 1q'; do AC_MSG_CHECKING([whether $ac_prog works]) AC_CACHE_VAL([ac_cv_prog_HEAD_1], [ if test -n "$HEAD_1"; then # Let the user override the test. ac_cv_prog_HEAD_1="$HEAD_1" else if test `cat conftest.head | $ac_prog` = a; then ac_cv_prog_HEAD_1="$ac_prog" fi fi ]) if test -n "$ac_cv_prog_HEAD_1"; then AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done if test "x$ac_cv_prog_HEAD_1" = x; then cat << 'EOF' >&2 configure: error: This system seems to lack a working 'head -1' or 'head -n 1' command. A working 'head -1' (or equivalent) command is required to compile Eggdrop. EOF exit 1 fi rm -f conftest.head HEAD_1="$ac_cv_prog_HEAD_1" AC_SUBST(HEAD_1) ]) dnl EGG_PROG_STRIP() dnl AC_DEFUN([EGG_PROG_STRIP], [ AC_CHECK_PROG(STRIP, strip, strip) if test "x$STRIP" = x; then STRIP=touch fi ]) dnl EGG_PROG_AWK() dnl AC_DEFUN([EGG_PROG_AWK], [ AC_PROG_AWK if test "x$AWK" = x; then cat << 'EOF' >&2 configure: error: This system seems to lack a working 'awk' command. A working 'awk' command is required to compile Eggdrop. EOF exit 1 fi ]) dnl EGG_PROG_BASENAME() dnl AC_DEFUN([EGG_PROG_BASENAME], [ AC_CHECK_PROG(BASENAME, basename, basename) if test "x$BASENAME" = x; then cat << 'EOF' >&2 configure: error: This system seems to lack a working 'basename' command. A working 'basename' command is required to compile Eggdrop. EOF exit 1 fi ]) dnl EGG_ENABLE_STRIP() dnl AC_DEFUN([EGG_ENABLE_STRIP], [ AC_ARG_ENABLE([strip], [ --enable-strip enable stripping of binaries], [enable_strip="$enableval"], [enable_strip="no"]) if test "$enable_strip" = yes; then cat << 'EOF' >&2 configure: WARNING: Stripping the executable, while saving some disk space, will make bug reports nearly worthless. If Eggdrop crashes and you wish to report a bug, you will need to recompile with stripping disabled. EOF else STRIP="touch" fi ]) dnl EGG_ENABLE_COVERAGE() dnl dnl Adds gcov instrumentation to CFLAGS/LDFLAGS so a build produces .gcno dnl files at compile time and .gcda files at run time. Use lcov / gcov to dnl summarize after running the test suite. Off by default; only meant for dnl development builds against the test harness, never production. dnl AC_DEFUN([EGG_ENABLE_COVERAGE], [ AC_ARG_ENABLE([coverage], [ --enable-coverage build with gcov instrumentation (developer / test-coverage)], [enable_coverage="$enableval"], [enable_coverage="no"]) if test "$enable_coverage" = yes; then AC_MSG_NOTICE([enabling gcov coverage instrumentation: --coverage -fPIC -O0 -ggdb3]) CFLAGS="$CFLAGS --coverage -fPIC -O0 -ggdb3" LDFLAGS="$LDFLAGS --coverage" fi ]) dnl dnl Checks for operating system and module support. dnl dnl EGG_OS_VERSION() dnl AC_DEFUN([EGG_OS_VERSION], [ dnl FIXME: Eventually replace these with the results of AC_CANONICAL_* below AC_CACHE_CHECK([system type], egg_cv_var_system_type, [egg_cv_var_system_type=`$UNAME -s`]) AC_CACHE_CHECK([system release], egg_cv_var_system_release, [egg_cv_var_system_release=`$UNAME -r`]) AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET ]) dnl EGG_CYGWIN_BINMODE dnl dnl Check for binmode.o on Cygwin. dnl AC_DEFUN([EGG_CYGWIN_BINMODE], [ if test "$EGG_CYGWIN" = yes; then AC_MSG_CHECKING([for /usr/lib/binmode.o]) if test -r /usr/lib/binmode.o; then AC_MSG_RESULT([yes]) EGG_APPEND_VAR(LIBS, /usr/lib/binmode.o) else AC_MSG_RESULT([no]) AC_MSG_WARN([Make sure the directory Eggdrop is installed into is mounted in binary mode.]) fi fi ]) dnl EGG_DARWIN_BUNDLE dnl dnl Locate bundle1.o on Darwin. Test systems seem to have it in /usr/lib, dnl however the official docs say /lib. dnl AC_DEFUN([EGG_DARWIN_BUNDLE], [ BUNDLE="" for bundlepath in "/lib" "/usr/lib" "/usr/local/lib"; do AC_MSG_CHECKING([for bundle1.o in ${bundlepath}]) if test -r "${bundlepath}/bundle1.o"; then AC_MSG_RESULT([yes]) BUNDLE="${bundlepath}/bundle1.o" break else AC_MSG_RESULT([no]) fi done if test "x$BUNDLE" = x; then cat << 'EOF' >&2 configure: WARNING: bundle1.o cannot be located. A module build might not compile correctly. EOF fi ]) dnl EGG_CHECK_MODULE_SUPPORT() dnl dnl Checks for module support. This should be run after EGG_OS_VERSION. dnl AC_DEFUN([EGG_CHECK_MODULE_SUPPORT], [ MODULES_OK="yes" MOD_EXT="so" DEFAULT_MAKE="eggdrop" LOAD_METHOD="dl" WEIRD_OS="yes" UNKNOWN_OS="no" MODULE_XLIBS="" AC_MSG_CHECKING([module loading capabilities]) AC_MSG_RESULT AC_CHECK_HEADERS([dl.h dlfcn.h loader.h mach-o/dyld.h]) AC_CHECK_FUNCS([dlopen load NSLinkModule shl_load]) # Note to other maintainers: # Bourne shell has no concept of "fall through" case "$egg_cv_var_system_type" in CYGWI*) WEIRD_OS="no" MOD_EXT="dll" ;; HP-UX) LOAD_METHOD="shl" ;; dell) # do nothing ;; IRIX) # do nothing ;; Ultrix) # No dlopen() or similar on Ultrix. We can't use modules. MODULES_OK="no" ;; BeOS) # We don't yet support BeOS's dynamic linking interface. MODULES_OK="no" ;; Linux) WEIRD_OS="no" ;; Lynx) # do nothing ;; QNX) # do nothing # QNX (recent versions at least) support dlopen(). ;; OSF1) case `echo "$egg_cv_var_system_release" | cut -d . -f 1` in 1.*) LOAD_METHOD="loader" ;; esac ;; SunOS) WEIRD_OS="no" ;; FreeBSD|OpenBSD|NetBSD|DragonFly) WEIRD_OS="no" ;; Darwin) # In macOS 10.4 (Darwin 8), dlopen was rewritten to be a native part of dyld. AC_MSG_CHECKING([darwin version >= 8 with native dlopen]) darwin_major_version=`echo $egg_cv_var_system_release | cut -d. -f1` if test $darwin_major_version -ge 8; then AC_MSG_RESULT([yes]) WEIRD_OS="no" else AC_MSG_RESULT([no]) LOAD_METHOD="dyld" EGG_DARWIN_BUNDLE EGG_APPEND_VAR(MODULE_XLIBS, $BUNDLE) fi ;; Haiku) WEIRD_OS="no" ;; Minix) WEIRD_OS="no" ;; *) # QNX apparently supports dlopen()... Fallthrough. if test -r /cmds; then UNKNOWN_OS="yes" MODULES_OK="no" fi ;; esac if test "$MODULES_OK" = yes; then AC_DEFINE(MODULES_OK, 1, [Define if modules will work on your system.]) case $LOAD_METHOD in dl) AC_DEFINE(MOD_USE_DL, 1, [Define if modules should be loaded using the dl*() functions.]) ;; shl) AC_DEFINE(MOD_USE_SHL, 1, [Define if modules should be loaded using the shl_*() functions.]) ;; dyld) AC_DEFINE(MOD_USE_DYLD, 1, [Define if modules should be loaded using the NS*() functions.]) ;; loader) AC_DEFINE(MOD_USE_LOADER, 1, [Define if modules should be loaded using the ldr*() and *load() functions.]) ;; esac else DEFAULT_MAKE="static" fi if test "$WEIRD_OS" = yes; then # Default to "make static" for 'weird' operating systems. Will print a # note at the end of configure explaining. This way, Eggdrop should compile # "out of the box" on most every operating system we know of, and they can # do a "make eggdrop" if they want to use(/try to use) module support. - Wcc DEFAULT_MAKE="static" fi AC_SUBST(DEFAULT_MAKE) AC_SUBST(MOD_EXT) AC_SUBST(MODULE_XLIBS) AC_DEFINE_UNQUOTED(EGG_MOD_EXT, "$MOD_EXT", [Defines the extension of Eggdrop modules.]) ]) dnl EGG_CHECK_OS() dnl dnl Various operating system tests. dnl AC_DEFUN([EGG_CHECK_OS], [ MOD_CC="$CC" MOD_LD="$CC" MOD_STRIP="$STRIP" SHLIB_CC="$CC" SHLIB_LD="$CC" SHLIB_STRIP="$STRIP" LINUX="no" IRIX="no" SUNOS_GCC="no" HPUX="no" EGG_CYGWIN="no" case "$egg_cv_var_system_type" in CYGWI*) SHLIB_LD="$CC -shared" MOD_CC="$CC" MOD_LD="$CC" EGG_CYGWIN="yes" EGG_CYGWIN_BINMODE AC_DEFINE(CYGWIN_HACKS, 1, [Define if running under Cygwin.]) ;; HP-UX) HPUX="yes" if test "$CC" = cc; then # HP-UX ANSI C Compiler. MOD_LD="$CC +z" SHLIB_CC="$CC +z" else # GCC MOD_LD="$CC -fPIC -shared" SHLIB_CC="$CC -fPIC" fi SHLIB_LD="ld -b" ;; dell) SHLIB_STRIP="touch" MOD_LD="$CC -lelf -lucb" ;; IRIX) SHLIB_LD="ld -n32 -shared -rdata_shared" IRIX="yes" SHLIB_STRIP="touch" ;; Ultrix) SHLIB_STRIP="touch" DEFAULT_MAKE="static" SHELL="/bin/sh5" ;; SINIX*) SHLIB_STRIP="touch" SHLIB_CC="cc -G" ;; BeOS) # do nothing ;; Linux) LINUX="yes" MOD_LD="$CC" SHLIB_CC="$CC -fPIC" SHLIB_LD="$CC -shared -nostartfiles" ;; Lynx) # do nothing ;; QNX) SHLIB_LD="ld -shared" AC_DEFINE(QNX_HACKS, 1, [Define if running under QNX.]) ;; OSF1) case `echo "$egg_cv_var_system_release" | cut -d . -f 1` in V*) # Digital OSF uses an ancient version of gawk if test "$AWK" = gawk; then AWK="awk" fi SHLIB_LD="ld -shared -expect_unresolved \"'*'\"" SHLIB_STRIP="touch" ;; 1.0|1.1|1.2) SHLIB_LD="ld -R -export $@:" ;; 1.*) SHLIB_CC="$CC -fpic" SHLIB_LD="ld -shared" ;; esac AC_DEFINE(BROKEN_SNPRINTF, 1, [Define to use Eggdrop's snprintf functions regardless of HAVE_SNPRINTF.]) AC_DEFINE(STOP_UAC, 1, [Define if running on OSF/1 platform.]) ;; SunOS) if test -n "$GCC"; then SUNOS_GCC="yes" SHLIB_CC="$CC -fPIC" SHLIB_LD="$CC -shared" else SHLIB_CC="$CC -KPIC" SHLIB_LD="$CC -G -z text" fi ;; FreeBSD|DragonFly|OpenBSD|NetBSD) SHLIB_CC="$CC -fPIC" SHLIB_LD="$CC -shared" ;; Darwin) # macOS. SHLIB_CC="$CC -fPIC" case "$egg_cv_var_system_release" in 2*) SHLIB_LD="$CC -shared" ;; *) # macOS < 11 (Darwin 20). SHLIB_LD="ld -bundle -undefined error" ;; esac AC_DEFINE(BIND_8_COMPAT, 1, [Define if running on macOS with dns.mod.]) ;; *) if test -r /cmds; then # Probably QNX. SHLIB_LD="ld -shared" SHLIB_STRIP="touch" fi ;; esac AC_SUBST(MOD_LD) AC_SUBST(MOD_CC) AC_SUBST(MOD_STRIP) AC_SUBST(SHLIB_LD) AC_SUBST(SHLIB_CC) AC_SUBST(SHLIB_STRIP) ]) dnl dnl Library tests. dnl dnl EGG_CHECK_LIBS() dnl AC_DEFUN([EGG_CHECK_LIBS], [ # FIXME: this needs to be fixed so that it works on IRIX if test "$IRIX" = yes; then AC_MSG_WARN([Skipping library tests because they CONFUSE IRIX.]) else AX_LIB_SOCKET_NSL AX_PTHREAD(LDFLAGS="$LDFLAGS $PTHREAD_LIBS") AC_SEARCH_LIBS([dlopen], [dl]) AC_CHECK_LIB(m, tan, EGG_MATH_LIB="-lm") if test "$HPUX" = yes; then AC_CHECK_LIB(dld, shl_load) fi fi ]) dnl EGG_ARG_HANDLEN() dnl AC_DEFUN([EGG_ARG_HANDLEN], [ AC_ARG_WITH(handlen, [ --with-handlen=VALUE set the maximum length a handle on the bot can be], [ if test -n $withval && test $withval -ge 9 && test $withval -le 32; then AC_DEFINE_UNQUOTED(EGG_HANDLEN, $withval, [ Define the maximum length of handles on the bot. ]) else AC_MSG_WARN([Invalid handlen given (must be a number between 9 and 32), defaulting to 9.]) fi ]) ]) dnl dnl Misc checks. dnl dnl EGG_EXEEXT() dnl dnl Test for executable suffix and define Eggdrop's executable name accordingly. dnl AC_DEFUN([EGG_EXEEXT], [ EGGEXEC="eggdrop" AC_EXEEXT if test "x$EXEEXT" != x; then EGGEXEC="eggdrop${EXEEXT}" fi AC_SUBST(EGGEXEC) ]) dnl dnl Tcl checks. dnl dnl EGG_TCL_ARG_WITH() dnl AC_DEFUN([EGG_TCL_ARG_WITH], [ AC_ARG_WITH(tcllib, [ --with-tcllib=PATH full path to Tcl library (e.g. /usr/lib/libtcl8.6.so)], [tcllibname="$withval"]) AC_ARG_WITH(tclinc, [ --with-tclinc=PATH full path to Tcl header (e.g. /usr/include/tcl.h)], [tclincname="$withval"]) WARN=0 # Make sure either both or neither $tcllibname and $tclincname are set if test "x$tcllibname" != x; then if test "x$tclincname" = x; then WARN=1 tcllibname="" TCLLIB="" TCLINC="" fi else if test "x$tclincname" != x; then WARN=1 tclincname="" TCLLIB="" TCLINC="" fi fi if test "$WARN" = 1; then cat << 'EOF' >&2 configure: WARNING: You must specify both --with-tcllib and --with-tclinc for either to work. configure will now attempt to autodetect both the Tcl library and header. EOF fi ]) dnl EGG_TCL_WITH_TCLLIB() dnl AC_DEFUN([EGG_TCL_WITH_TCLLIB], [ # Look for Tcl library: if $tcllibname is set, check there first if test "x$tcllibname" != x; then if test -f "$tcllibname" && test -r "$tcllibname"; then TCLLIB=`echo $tcllibname | sed 's%/[[^/]][[^/]]*$%%'` TCLLIBFN=`$BASENAME $tcllibname | cut -c4-` TCLLIBEXT=`echo $TCLLIBFN | $AWK '{j=split([$]1, i, "."); suffix=""; while (i[[j]] ~ /^[[0-9]]+$/) { suffix = "." i[[j--]] suffix; }; print "." i[[j]] suffix }'` TCLLIBFNS=`$BASENAME $tcllibname $TCLLIBEXT | cut -c4-` # Set default make as static for unshared Tcl library if test TCLLIBEXT = ".a"; then if test "$DEFAULT_MAKE" != static; then cat << 'EOF' >&2 configure: WARNING: Your Tcl library is not a shared lib. configure will now set default make type to static. EOF DEFAULT_MAKE="static" AC_SUBST(DEFAULT_MAKE) fi fi else cat << EOF >&2 configure: WARNING: The file '$tcllibname' given to option --with-tcllib is not valid. Specify the full path including the file name (e.g. /usr/lib/libtcl8.6.so) configure will now attempt to autodetect both the Tcl library and header. EOF tcllibname="" tclincname="" TCLLIB="" TCLLIBFN="" TCLINC="" TCLINCFN="" fi fi ]) dnl EGG_TCL_WITH_TCLINC() dnl AC_DEFUN([EGG_TCL_WITH_TCLINC], [ # Look for Tcl header: if $tclincname is set, check there first if test "x$tclincname" != x; then if test -f "$tclincname" && test -r "$tclincname"; then TCLINC=`echo $tclincname | sed 's%/[[^/]][[^/]]*$%%'` TCLINCFN=`$BASENAME $tclincname` else cat << EOF >&2 configure: WARNING: The file '$tclincname' given to option --with-tclinc is not valid. Specify the full path including the file name (e.g. /usr/include/tcl.h) configure will now attempt to autodetect both the Tcl library and header. EOF tcllibname="" tclincname="" TCLLIB="" TCLLIBFN="" TCLINC="" TCLINCFN="" fi fi ]) dnl EGG_TCL_TCLCONFIG() dnl AC_DEFUN([EGG_TCL_TCLCONFIG], [ if test "x$TCLLIBFN" = x; then AC_MSG_NOTICE([Autoconfiguring Tcl with tclConfig.sh]) egg_tcl_changed="yes" TEA_INIT("3.10") TEA_PATH_TCLCONFIG TEA_LOAD_TCLCONFIG TEA_TCL_LINK_LIBS # Overwrite TCL_LIBS again, which TCL_LOAD_TCLCONFIG unfortunately overwrites from tclConfig.sh # Also, use the Tcl linker idea to be compatible with their ldflags if test -r ${TCL_BIN_DIR}/tclConfig.sh; then . ${TCL_BIN_DIR}/tclConfig.sh AC_SUBST(SHLIB_LD, $TCL_SHLIB_LD) AC_MSG_CHECKING([for Tcl linker]) AC_MSG_RESULT([$SHLIB_LD]) else TCL_LIBS="${EGG_MATH_LIB}" fi TCL_PATCHLEVEL="${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_PATCH_LEVEL}" TCL_LIB_SPEC="${TCL_LIB_SPEC} ${TCL_LIBS}" else egg_tcl_changed="yes" if test -r ${TCLLIB}/tclConfig.sh; then . ${TCLLIB}/tclConfig.sh AC_SUBST(SHLIB_LD, $TCL_SHLIB_LD) AC_MSG_CHECKING([for Tcl linker]) AC_MSG_RESULT([$SHLIB_LD]) TCL_PATCHLEVEL="${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_PATCH_LEVEL}" TCL_LIB_SPEC="${TCL_LIB_SPEC} ${TCL_LIBS}" else TCL_LIB_SPEC="-L$TCLLIB -l$TCLLIBFNS ${EGG_MATH_LIB}" TCL_INCLUDE_SPEC="" TCL_VERSION=`grep TCL_VERSION $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/"/, "", [$]3); print [$]3}'` TCL_PATCHLEVEL=`grep TCL_PATCH_LEVEL $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/"/, "", [$]3); print [$]3}'` TCL_MAJOR_VERSION=`echo $TCL_VERSION | cut -d. -f1` TCL_MINOR_VERSION=`echo $TCL_VERSION | cut -d. -f2` if test $TCL_MAJOR_VERSION -gt 8 || test $TCL_MAJOR_VERSION -eq 8 -a $TCL_MINOR_VERSION -ge 6; then TCL_LIB_SPEC="$TCL_LIB_SPEC -lz" fi fi fi if test -z "$ac_cv_lib_dlopen"; then TCL_LIB_SPEC=$(echo $TCL_LIB_SPEC | sed -- 's/-ldl//g') fi if test "$SUNOS_GCC" = yes; then SHLIB_LD=$(echo $SHLIB_LD | sed -- 's/-z text//') AC_MSG_NOTICE([SunOS found, SHLIB_LD = $SHLIB_LD]) fi AC_MSG_CHECKING([for Tcl version]) AC_MSG_RESULT([$TCL_PATCHLEVEL]) AC_MSG_CHECKING([for Tcl library flags]) AC_MSG_RESULT([$TCL_LIB_SPEC]) AC_MSG_CHECKING([for Tcl header flags]) AC_MSG_RESULT([$TCL_INCLUDE_SPEC]) AC_SUBST(TCL_LIB_SPEC) AC_SUBST(TCL_INCLUDE_SPEC) ]) dnl EGG_TCL_CHECK_VERSION() dnl AC_DEFUN([EGG_TCL_CHECK_VERSION], [ if test "x$TCL_MAJOR_VERSION" = x || test "x$TCL_MINOR_VERSION" = x || test $TCL_MAJOR_VERSION -lt 8 || test $TCL_MAJOR_VERSION -eq 8 -a $TCL_MINOR_VERSION -lt 5; then cat << EOF >&2 configure: error: Your Tcl version is much too old for Eggdrop to use. You should download and compile a more recent version. The most reliable current version is $tclrecommendver and can be downloaded from ${tclrecommendsite}. We require at least Tcl 8.5. See doc/COMPILE-GUIDE's 'Tcl Detection and Installation' section for more information. EOF exit 1 fi ]) dnl EGG_CACHE_UNSET(CACHE-ID) dnl dnl Unsets a certain cache item. Typically called before using the AC_CACHE_*() dnl macros. dnl AC_DEFUN([EGG_CACHE_UNSET], [unset $1]) dnl EGG_SUBST_EGGVERSION() dnl AC_DEFUN([EGG_SUBST_EGGVERSION], [ EGGVERSION=`grep '^ *# *define *EGG_STRINGVER ' $srcdir/src/version.h | $AWK '{gsub(/(")/, "", $NF); print $NF}'` egg_version_num=`echo $EGGVERSION | $AWK 'BEGIN {FS = "."} {printf("%d%02d%02d", [$]1, [$]2, [$]3)}'` AC_SUBST(EGGVERSION) AC_DEFINE_UNQUOTED(EGG_VERSION, $egg_version_num, [Defines the current Eggdrop version.]) ]) dnl EGG_SUBST_DEST() AC_DEFUN([EGG_SUBST_DEST], [ if test "x$DEST" = x; then DEST=\${prefix} fi AC_SUBST(DEST) ]) dnl EGG_SUBST_MOD_UPDIR() dnl dnl Since module's Makefiles aren't generated by configure, some paths in dnl src/mod/Makefile.in take care of them. For correct path "calculation", we dnl need to keep absolute paths in mind (which don't need a "../" prepended). dnl AC_DEFUN([EGG_SUBST_MOD_UPDIR], [ case "$srcdir" in [[\\/]]* | ?:[[\\/]]*) MOD_UPDIR="" ;; *) MOD_UPDIR="../" ;; esac AC_SUBST(MOD_UPDIR) ]) dnl EGG_REPLACE_IF_CHANGED(FILE-NAME, CONTENTS-CMDS, INIT-CMDS) dnl dnl Replace FILE-NAME if the newly created contents differs from the existing dnl file contents. Otherwise, leave the file alone. This avoids needless dnl recompiles. dnl m4_define(EGG_REPLACE_IF_CHANGED, [ AC_CONFIG_COMMANDS([replace-if-changed], [[ egg_replace_file="$1" $2 if test -f "$egg_replace_file" && cmp -s conftest.out $egg_replace_file; then echo "$1 is unchanged" else echo "creating $1" mv conftest.out $egg_replace_file fi rm -f conftest.out ]], [[$3]]) ]) dnl EGG_TCL_LUSH() dnl AC_DEFUN([EGG_TCL_LUSH], [ if test "x$TCLINCFN" != x; then egg_tclinc="\\\"$TCLINC/$TCLINCFN\\\"" else egg_tclinc="" fi EGG_REPLACE_IF_CHANGED(lush.h, [ cat > conftest.out << EOF /* Ignore me but do not erase me. I am a kludge. */ #include ${egg_tclinc} EOF ],[ egg_tclinc="${egg_tclinc}" ]) ]) dnl EGG_DEBUG_ENABLE() dnl AC_DEFUN([EGG_DEBUG_ENABLE], [ AC_ARG_ENABLE(debug, [ --enable-debug enable generic debug code (default for 'make debug')], [enable_debug="$enableval"], [enable_debug="auto"]) AC_ARG_ENABLE(debug, [ --disable-debug disable generic debug code], [enable_debug="$enableval"], [enable_debug="auto"]) AC_ARG_ENABLE(debug-assert, [ --enable-debug-assert enable assert debug code (default for 'make debug')], [enable_debug_assert="$enableval"], [enable_debug_assert="auto"]) AC_ARG_ENABLE(debug-assert, [ --disable-debug-assert disable assert debug code], [enable_debug_assert="$enableval"], [enable_debug_assert="auto"]) AC_ARG_ENABLE(debug-mem, [ --enable-debug-mem enable memory debug code (default for 'make debug')], [enable_debug_mem="$enableval"], [enable_debug_mem="auto"]) AC_ARG_ENABLE(debug-mem, [ --disable-debug-mem disable memory debug code], [enable_debug_mem="$enableval"], [enable_debug_mem="auto"]) AC_ARG_ENABLE(debug-dns, [ --enable-debug-dns enable dns.mod debug messages (default for 'make debug')], [enable_debug_dns="$enableval"], [enable_debug_dns="auto"]) AC_ARG_ENABLE(debug-dns, [ --disable-debug-dns disable dns.mod debug messages], [enable_debug_dns="$enableval"], [enable_debug_dns="auto"]) AC_ARG_ENABLE(debug-context, [ --enable-debug-context enable context debug code (default)], [enable_debug_context="$enableval"], [enable_debug_context="auto"]) AC_ARG_ENABLE(debug-context, [ --disable-debug-context disable context debug code], [enable_debug_context="$enableval"], [enable_debug_context="auto"]) ]) dnl EGG_DEBUG_DEFAULTS() dnl AC_DEFUN([EGG_DEBUG_DEFAULTS], [ # Defaults: # make: 'eggdrop' or 'static' default_std_debug="no" default_std_debug_assert="no" default_std_debug_mem="no" default_std_debug_context="yes" default_std_debug_dns="no" # make: 'debug' or 'sdebug' default_deb_debug="yes" default_deb_debug_assert="yes" default_deb_debug_mem="yes" default_deb_debug_context="yes" default_deb_debug_dns="yes" if test "$DEFAULT_MAKE" = eggdrop || test "$DEFAULT_MAKE" = static; then default_debug="$default_std_debug" default_debug_assert="$default_std_debug_assert" default_debug_mem="$default_std_debug_mem" default_debug_context="$default_std_debug_context" default_debug_dns="$default_std_debug_dns" else default_debug="$default_deb_debug" default_debug_assert="$default_deb_debug_assert" default_debug_mem="$default_deb_debug_mem" default_debug_context="$default_deb_debug_context" default_debug_dns="$default_deb_debug_dns" fi debug_options="debug debug_assert debug_mem debug_dns" debug_cflags_debug="-g3 -DDEBUG" AX_CHECK_COMPILE_FLAG([-Og], [debug_cflags_debug="-Og $debug_cflags_debug"]) debug_cflags_debug_assert="-DDEBUG_ASSERT" debug_cflags_debug_mem="-DDEBUG_MEM" debug_cflags_debug_dns="-DDEBUG_DNS" debug_stdcflags_debug="" debug_stdcflags_debug_assert="" debug_stdcflags_debug_mem="" debug_stdcflags_debug_dns="" debug_debcflags_debug="" debug_debcflags_debug_assert="" debug_debcflags_debug_mem="" debug_debcflags_debug_dns="" ]) dnl EGG_DEBUG_OPTIONS() dnl AC_DEFUN([EGG_DEBUG_OPTIONS], [ for enable_option in $debug_options; do eval enable_value=\$enable_$enable_option # Check if either --enable- or --disable- was used if test "$enable_value" != auto; then # Make sure an invalid option wasn't passed as --enable-=foo if test "$enable_value" != yes && test "$enable_value" != no; then opt_name=`echo $enable_option | sed 's/_/-/g'` eval opt_default=\$default_$enable_option AC_MSG_WARN([Invalid option '$enable_value' passed to --enable-${opt_name}, defaulting to '$opt_default']) eval enable_$enable_option="auto" fi fi if test "$enable_value" = auto; then # Note: options generally should not end up in both std and deb but # there may be options in the future where this behavior is desired. if test `eval echo '${'default_std_$enable_option'}'` = yes; then eval `echo debug_stdcflags_$enable_option`=\$debug_cflags_$enable_option fi if test `eval echo '${'default_deb_$enable_option'}'` = yes; then eval `echo debug_debcflags_$enable_option`=\$debug_cflags_$enable_option fi else if test "$enable_value" = yes; then # If option defaults to 'yes' for debug, always put it in stdcflags # when the option is forced on because someone may want it enabled # for a non-debug build. if test `eval echo '${'default_deb_$enable_option'}'` = yes; then eval `echo debug_stdcflags_$enable_option`=\$debug_cflags_$enable_option else # option defaulted to 'no' so put it in debcflags eval `echo debug_debcflags_$enable_option`=\$debug_cflags_$enable_option fi fi fi done ]) dnl EGG_DEBUG_CFLAGS() dnl AC_DEFUN([EGG_DEBUG_CFLAGS], [ for cflg_option in $debug_options; do eval stdcflg_value=\$debug_stdcflags_$cflg_option EGG_APPEND_VAR(CFLGS, $stdcflg_value) eval debcflg_value=\$debug_debcflags_$cflg_option EGG_APPEND_VAR(DEBCFLGS, $debcflg_value) done # Disable debug symbol stripping if compiled with --enable-debug # This will result in core dumps that are actually useful. if test "x$debug_stdcflags_debug" != x; then STRIP="touch" MOD_STRIP="touch" SHLIB_STRIP="touch" fi AC_SUBST(CFLGS) AC_SUBST(DEBCFLGS) ]) dnl EGG_ENABLE_DEBUG_CONTEXT() dnl AC_DEFUN([EGG_ENABLE_DEBUG_CONTEXT], [ # Check if either --enable-debug-context or --disable-debug-context was used if test "$enable_debug_context" != auto; then # Make sure an invalid option wasn't passed as --enable-debug-context=foo if test "$enable_debug_context" != yes && test "$enable_debug_context" != no; then AC_MSG_WARN([Invalid option '$enable_debug_context' passed to --enable-debug-context, defaulting to '$default_debug_context']) enable_debug_context="$default_debug_context" fi else enable_debug_context="$default_debug_context" fi if test "$enable_debug_context" = yes; then AC_DEFINE(DEBUG_CONTEXT, 1, [Define for context debugging.]) else cat << 'EOF' >&2 configure: WARNING: You have disabled context debugging. Eggdrop will not be able to provide context information if it crashes. Bug reports without context are less helpful when tracking down bugs. EOF fi ]) dnl EGG_CATCH_MAKEFILE_REBUILD() dnl AC_DEFUN([EGG_CATCH_MAKEFILE_REBUILD], [ AC_CONFIG_COMMANDS([catch-make-rebuild], [[ if test -f .modules; then $srcdir/misc/modconfig --top_srcdir="$srcdir/src" Makefile fi ]]) ]) dnl EGG_SAVE_PARAMETERS() dnl dnl Remove --cache-file and --srcdir arguments so they do not pile up. dnl AC_DEFUN([EGG_SAVE_PARAMETERS], [ egg_ac_parameters= ac_prev= for ac_arg in $ac_configure_args; do if test -n "$ac_prev"; then ac_prev= continue fi case $ac_arg in -cache-file | --cache-file | --cache-fil | --cache-fi | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) ;; --config-cache | -C) ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) ;; *) egg_ac_parameters="$egg_ac_parameters $ac_arg" ;; esac done AC_SUBST(egg_ac_parameters) AC_DEFINE_UNQUOTED(EGG_AC_ARGS_RAW, $egg_ac_parameters, [Arguments passed to configure]) ]) dnl EGG_IPV6_COMPAT dnl AC_DEFUN([EGG_IPV6_COMPAT], [ if test "$enable_ipv6" = "yes"; then AC_CHECK_TYPES([struct in6_addr], egg_cv_var_have_in6_addr="yes", egg_cv_var_have_in6_addr="no", [ #include #include ]) if test "$egg_cv_var_have_in6_addr" = "yes"; then # Check for in6addr_any AC_CACHE_CHECK([for the in6addr_any constant], [egg_cv_var_have_in6addr_any], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[struct in6_addr i6 = in6addr_any;]])],[egg_cv_var_have_in6addr_any="yes"],[egg_cv_var_have_in6addr_any="no"]) ]) if test "$egg_cv_var_have_in6addr_any" = "yes"; then AC_DEFINE(HAVE_IN6ADDR_ANY, 1, [Define to 1 if you have the in6addr_any constant.]) fi # Check for in6addr_loopback AC_CACHE_CHECK([for the in6addr_loopback constant], [egg_cv_var_have_in6addr_loopback], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[struct in6_addr i6 = in6addr_loopback;]])],[egg_cv_var_have_in6addr_loopback="yes"],[egg_cv_var_have_in6addr_loopback="no"]) ]) if test "$egg_cv_var_have_in6addr_loopback" = "yes"; then AC_DEFINE(HAVE_IN6ADDR_LOOPBACK, 1, [Define to 1 if you have the in6addr_loopback constant.]) fi AC_CHECK_TYPES([struct sockaddr_in6], , , [ #include #include ]) else AC_MSG_NOTICE([no in6_addr found, skipping dependent checks. Custom definitions will be used.]) fi fi ]) dnl EGG_IPV6_ENABLE dnl AC_DEFUN([EGG_IPV6_ENABLE], [ ipv6_enabled="no" AC_ARG_ENABLE(ipv6, [ --enable-ipv6 enable IPv6 support (autodetect)], [enable_ipv6="$enableval"], [enable_ipv6="$egg_cv_var_ipv6_supported"]) AC_ARG_ENABLE(ipv6, [ --disable-ipv6 disable IPv6 support ], [enable_ipv6="$enableval"]) if test "$enable_ipv6" = "yes"; then if test "$egg_cv_var_ipv6_supported" = "no"; then AC_MSG_WARN([You have enabled IPv6 but your system doesn't seem to support it.]) AC_MSG_WARN([Eggdrop will compile but will be limited to IPv4 on this host.]) fi AC_DEFINE(IPV6, 1, [Define to 1 if you want to enable IPv6 support.]) ipv6_enabled="yes" fi ]) dnl EGG_IPV6_STATUS dnl AC_DEFUN([EGG_IPV6_STATUS], [ AC_CACHE_CHECK([for system IPv6 support], [egg_cv_var_ipv6_supported], [ AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ int s = socket(AF_INET6, SOCK_STREAM, 0); if (s != -1) close(s); return((s == -1)); ]])], [ egg_cv_var_ipv6_supported="yes" ], [ egg_cv_var_ipv6_supported="no" ], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ int s = socket(AF_INET6, SOCK_STREAM, 0); if (s != -1) close(s); return((s == -1)); ]])], [ egg_cv_var_ipv6_supported="yes" ], [ egg_cv_var_ipv6_supported="no" ]) ]) ]) ]) dnl EGG_TLS_ENABLE dnl AC_DEFUN([EGG_TLS_ENABLE], [ AC_MSG_CHECKING([whether to enable TLS support]) AC_ARG_ENABLE(tls, [ --disable-tls disable TLS support ], [tls_enabled="$enableval"], [tls_enabled="yes"]) AC_MSG_RESULT([$tls_enabled]) ]) dnl EGG_TLS_WITHSSL dnl AC_DEFUN(EGG_TLS_WITHSSL, [ save_LIBS="$LIBS" AC_ARG_WITH(sslinc, [ --with-sslinc=PATH Path to OpenSSL headers], [ if test "$tls_enabled" != "no"; then if test -d "$withval"; then save_CC="$CC" save_CPP="$CPP" CC="$CC -I$withval" CPP="$CPP -I$withval" AC_CHECK_HEADERS([openssl/ssl.h openssl/x509v3.h], [sslinc="-I$withval"], [ AC_MSG_ERROR([Invalid path to OpenSSL headers. $withval/openssl/ doesn't contain the required files.]) sslinc="" break ], [[ #ifdef CYGWIN_HACKS # ifndef __int64 # define __int64 long long # endif #endif ]]) AC_SUBST(SSL_INCLUDES, [$sslinc]) CC="$save_CC" CPP="$save_CPP" else AC_MSG_ERROR([Invalid path to OpenSSL headers. $withval is not a directory.]) fi fi ]) AC_ARG_WITH(ssllib, [ --with-ssllib=PATH Path to OpenSSL libraries], [ if test "$tls_enabled" != "no"; then if test -d "$withval"; then AC_CHECK_LIB(crypto, X509_digest, , [havessllib="no"], [-L$withval -lssl]) AC_CHECK_LIB(ssl, SSL_accept, , [havessllib="no"], [-L$withval -lcrypto]) if test "$havessllib" = "no"; then AC_MSG_ERROR([Invalid path to OpenSSL libs. $withval doesn't contain the required files.]) else AC_SUBST(SSL_LIBS, [-L$withval]) LDFLAGS="${LDFLAGS} -L$withval" fi else AC_MSG_ERROR([You have specified an invalid path to OpenSSL libs. $withval is not a directory.]) fi fi ]) ]) dnl EGG_TLS_DETECT dnl AC_DEFUN([EGG_TLS_DETECT], [ if test "$tls_enabled" != "no"; then if test -z "$SSL_INCLUDES"; then AC_CHECK_HEADERS([openssl/ssl.h openssl/x509v3.h], , [havesslinc="no"], [ #ifdef CYGWIN_HACKS # ifndef __int64 # define __int64 long long # endif #endif ]) fi if test -z "$SSL_LIBS"; then AC_CHECK_LIB(crypto, X509_digest, , [havessllib="no"], [-lssl]) AC_CHECK_LIB(ssl, SSL_accept, , [havessllib="no"], [-lcrypto]) fi AC_CHECK_FUNCS([EVP_sha1 a2i_IPADDRESS], , [[ havessllib="no" break ]]) AC_CHECK_FUNCS([EVP_md5]) AC_CHECK_FUNC(OPENSSL_buf2hexstr, , AC_CHECK_FUNC(hex_to_string, AC_DEFINE([OPENSSL_buf2hexstr], [hex_to_string], [Define this to hex_to_string when using OpenSSL < 1.1.0]) , [[ havessllib="no" break ]]) ) AC_CHECK_FUNC(OPENSSL_hexstr2buf, , AC_CHECK_FUNC(string_to_hex, AC_DEFINE([OPENSSL_hexstr2buf], [string_to_hex], [Define this to string_to_hex when using OpenSSL < 1.1.0]) , [[ havessllib="no" break ]]) ) if test "$havesslinc" = "no"; then AC_MSG_WARN([Cannot find OpenSSL headers.]) AC_MSG_WARN([Please specify the path to the openssl include dir using --with-sslinc=path]) fi if test "$havessllib" = "no"; then AC_MSG_WARN([Cannot find OpenSSL library 0.9.8 or newer.]) AC_MSG_WARN([Please specify the path to libssl and libcrypto using --with-ssllib=path]) fi AC_MSG_CHECKING([for OpenSSL]) if test "$havesslinc" = "no" || test "$havessllib" = "no"; then AC_MSG_RESULT([no]) AC_MSG_RESULT([* We tried, but couldn't find TLS libraries. If installed, please specify their paths using the configure flags above]) AC_MSG_RESULT([* While not recommended, you can continue without TLS protection by specifying the --disable-tls configure flag]) AC_MSG_ERROR([TLS Libraries not found]) else AC_MSG_RESULT([yes]) if test "$EGG_CYGWIN" = "yes"; then AC_CHECK_TYPE([__int64], , [ AC_DEFINE([__int64], [long long], [Define this to a 64-bit type on Cygwin to satisfy OpenSSL dependencies.]) ]) fi AC_DEFINE(TLS, 1, [Define this to enable SSL support.]) AC_CHECK_FUNC(ASN1_STRING_get0_data, AC_DEFINE([egg_ASN1_string_data], [ASN1_STRING_get0_data], [Define this to ASN1_STRING_get0_data when using OpenSSL 1.1.0+, ASN1_STRING_data otherwise.]) , AC_DEFINE([egg_ASN1_string_data], [ASN1_STRING_data], [Define this to ASN1_STRING_get0_data when using OpenSSL 1.1.0+, ASN1_STRING_data otherwise.]) ) dnl EVP_PKEY_get1_EC_KEY: OpenSSL without EC (SunOS 5.11 Solaris 11.3 I love you Oracle) AC_CHECK_FUNCS([EVP_PKEY_get1_EC_KEY]) EGG_MD5_COMPAT fi fi ]) dnl EGG_TDNS_ENABLE dnl AC_DEFUN([EGG_TDNS_ENABLE], [ AC_ARG_ENABLE([tdns], [AS_HELP_STRING([--disable-tdns], [disable threaded DNS core])], [tdns_enabled="$enableval"], [tdns_enabled="yes"]) if test "x$ax_pthread_ok" = "xno"; then tdns_enabled="no (pthread.h not found)" else if test "$tdns_enabled" = "yes"; then AC_DEFINE([EGG_TDNS], [1], [Define this to enable threaded DNS core.]) fi fi ]) dnl EGG_MD5_COMPAT dnl AC_DEFUN([EGG_MD5_COMPAT], [ save_CC="$CC" save_CPP="$CPP" CC="$CC $sslinc" CPP="$CPP $sslinc" AC_CHECK_HEADERS([openssl/md5.h], [ AC_CHECK_FUNCS([MD5_Init MD5_Update MD5_Final], , [havesslmd5="no"]) ]) if test "$havesslmd5" != "no"; then AC_DEFINE(HAVE_OPENSSL_MD5, 1, [Define this if your OpenSSL library has MD5 cipher support.]) fi CC="$save_CC" CPP="$save_CPP" ]) eggheads-eggdrop-9c00fe1/config.h.in000066400000000000000000000352451524730042700173570ustar00rootroot00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_CONFIG_H #define _EGG_CONFIG_H /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define if running on macOS with dns.mod. */ #undef BIND_8_COMPAT /* Define to use Eggdrop's snprintf functions regardless of HAVE_SNPRINTF. */ #undef BROKEN_SNPRINTF /* Define if running under Cygwin. */ #undef CYGWIN_HACKS /* Define for context debugging. */ #undef DEBUG_CONTEXT /* Arguments passed to configure */ #undef EGG_AC_ARGS_RAW /* Define the maximum length of handles on the bot. */ #undef EGG_HANDLEN /* Defines the extension of Eggdrop modules. */ #undef EGG_MOD_EXT /* Define this to enable threaded DNS core. */ #undef EGG_TDNS /* Defines the current Eggdrop version. */ #undef EGG_VERSION /* Define to nothing if C supports flexible array members, and to 1 if it does not. That way, with a declaration like `struct s { int n; double d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99 compilers. When computing the size of such an object, don't use 'sizeof (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)' instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with MSVC and with C++ compilers. */ #undef FLEXIBLE_ARRAY_MEMBER /* Define to 1 if you have the `a2i_IPADDRESS' function. */ #undef HAVE_A2I_IPADDRESS /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define if b64_ntop exists. */ #undef HAVE_BASE64 /* Define to 1 if you have the `clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME /* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. */ #undef HAVE_DECL_TZNAME /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_DIRENT_H /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the `dlopen' function. */ #undef HAVE_DLOPEN /* Define to 1 if you have the header file. */ #undef HAVE_DL_H /* Define to 1 if you have the `dprintf' function. */ #undef HAVE_DPRINTF /* Define to 1 if you have the `EVP_md5' function. */ #undef HAVE_EVP_MD5 /* Define to 1 if you have the `EVP_PKEY_get1_EC_KEY' function. */ #undef HAVE_EVP_PKEY_GET1_EC_KEY /* Define to 1 if you have the `EVP_sha1' function. */ #undef HAVE_EVP_SHA1 /* Define to 1 if you have the `explicit_bzero' function. */ #undef HAVE_EXPLICIT_BZERO /* Define to 1 if you have the `explicit_memset' function. */ #undef HAVE_EXPLICIT_MEMSET /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the `getpagesize' function. */ #undef HAVE_GETPAGESIZE /* Define to 1 if you have the `getrandom' function. */ #undef HAVE_GETRANDOM /* Define to 1 if you have the in6addr_any constant. */ #undef HAVE_IN6ADDR_ANY /* Define to 1 if you have the in6addr_loopback constant. */ #undef HAVE_IN6ADDR_LOOPBACK /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON /* Define to 1 if the system has the type `intptr_t'. */ #undef HAVE_INTPTR_T /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `crypto' library (-lcrypto). */ #undef HAVE_LIBCRYPTO /* Define to 1 if you have the `dld' library (-ldld). */ #undef HAVE_LIBDLD /* Define to 1 if you have the `ssl' library (-lssl). */ #undef HAVE_LIBSSL /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the `load' function. */ #undef HAVE_LOAD /* Define to 1 if you have the header file. */ #undef HAVE_LOADER_H /* Define to 1 if you have the header file. */ #undef HAVE_LOCALE_H /* Define to 1 if you have the header file. */ #undef HAVE_MACH_O_DYLD_H /* Define to 1 if you have the `MD5_Final' function. */ #undef HAVE_MD5_FINAL /* Define to 1 if you have the `MD5_Init' function. */ #undef HAVE_MD5_INIT /* Define to 1 if you have the `MD5_Update' function. */ #undef HAVE_MD5_UPDATE /* Define to 1 if you have the `memset_explicit' function. */ #undef HAVE_MEMSET_EXPLICIT /* Define to 1 if you have the `memset_s' function. */ #undef HAVE_MEMSET_S /* Define to 1 if you have the header file. */ #undef HAVE_MINIX_CONFIG_H /* Define to 1 if you have a working `mmap' system call. */ #undef HAVE_MMAP /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Do we have ? */ #undef HAVE_NET_ERRNO_H /* Define to 1 if you have the `NSLinkModule' function. */ #undef HAVE_NSLINKMODULE /* Define this if your OpenSSL library has MD5 cipher support. */ #undef HAVE_OPENSSL_MD5 /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_MD5_H /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_SSL_H /* Define to 1 if you have the header file. */ #undef HAVE_OPENSSL_X509V3_H /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the `shl_load' function. */ #undef HAVE_SHL_LOAD /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_STDARG_H /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if the system has the type `struct in6_addr'. */ #undef HAVE_STRUCT_IN6_ADDR /* Define to 1 if the system has the type `struct sockaddr_in6'. */ #undef HAVE_STRUCT_SOCKADDR_IN6 /* Define to 1 if `tm_zone' is a member of `struct tm'. */ #undef HAVE_STRUCT_TM_TM_ZONE /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_DIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILE_H /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* 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 /* Define to 1 if you don't have `tm_zone' but do have the external array `tzname'. */ #undef HAVE_TZNAME /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_WCHAR_H /* Define to 1 if you want to enable IPv6 support. */ #undef IPV6 /* Define if modules will work on your system. */ #undef MODULES_OK /* Define if modules should be loaded using the dl*() functions. */ #undef MOD_USE_DL /* Define if modules should be loaded using the NS*() functions. */ #undef MOD_USE_DYLD /* Define if modules should be loaded using the ldr*() and *load() functions. */ #undef MOD_USE_LOADER /* Define if modules should be loaded using the shl_*() functions. */ #undef MOD_USE_SHL /* Define this to hex_to_string when using OpenSSL < 1.1.0 */ #undef OPENSSL_buf2hexstr /* Define this to string_to_hex when using OpenSSL < 1.1.0 */ #undef OPENSSL_hexstr2buf /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define if running under QNX. */ #undef QNX_HACKS /* Define to the type of arg 1 for `select'. */ #undef SELECT_TYPE_ARG1 /* Define to the type of args 2, 3 and 4 for `select'. */ #undef SELECT_TYPE_ARG234 /* Define to the type of arg 5 for `select'. */ #undef SELECT_TYPE_ARG5 /* The size of `char', as computed by sizeof. */ #undef SIZEOF_CHAR /* The size of `int', as computed by sizeof. */ #undef SIZEOF_INT /* The size of `long', as computed by sizeof. */ #undef SIZEOF_LONG /* The size of `short', as computed by sizeof. */ #undef SIZEOF_SHORT /* The size of `void*', as computed by sizeof. */ #undef SIZEOF_VOIDP /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Define if running on OSF/1 platform. */ #undef STOP_UAC /* Define this to enable SSL support. */ #undef TLS /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable general extensions on macOS. */ #ifndef _DARWIN_C_SOURCE # undef _DARWIN_C_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable X/Open compliant socket functions that do not require linking with -lxnet on HP-UX 11.11. */ #ifndef _HPUX_ALT_XOPEN_SOCKET_API # undef _HPUX_ALT_XOPEN_SOCKET_API #endif /* Identify the host operating system as Minix. This macro does not affect the system headers' behavior. A future release of Autoconf may stop defining this macro. */ #ifndef _MINIX # undef _MINIX #endif /* Enable general extensions on NetBSD. Enable NetBSD compatibility extensions on Minix. */ #ifndef _NETBSD_SOURCE # undef _NETBSD_SOURCE #endif /* Enable OpenBSD compatibility extensions on NetBSD. Oddly enough, this does nothing on OpenBSD. */ #ifndef _OPENBSD_SOURCE # undef _OPENBSD_SOURCE #endif /* Define to 1 if needed for POSIX-compatible behavior. */ #ifndef _POSIX_SOURCE # undef _POSIX_SOURCE #endif /* Define to 2 if needed for POSIX-compatible behavior. */ #ifndef _POSIX_1_SOURCE # undef _POSIX_1_SOURCE #endif /* Enable POSIX-compatible threading on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ #ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ # undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ #ifndef __STDC_WANT_IEC_60559_BFP_EXT__ # undef __STDC_WANT_IEC_60559_BFP_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ #ifndef __STDC_WANT_IEC_60559_DFP_EXT__ # undef __STDC_WANT_IEC_60559_DFP_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ #ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ # undef __STDC_WANT_IEC_60559_FUNCS_EXT__ #endif /* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ # undef __STDC_WANT_IEC_60559_TYPES_EXT__ #endif /* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ #ifndef __STDC_WANT_LIB_EXT2__ # undef __STDC_WANT_LIB_EXT2__ #endif /* Enable extensions specified by ISO/IEC 24747:2009. */ #ifndef __STDC_WANT_MATH_SPEC_FUNCS__ # undef __STDC_WANT_MATH_SPEC_FUNCS__ #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable X/Open extensions. Define to 500 only if necessary to make mbstate_t available. */ #ifndef _XOPEN_SOURCE # undef _XOPEN_SOURCE #endif /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Define this to a 64-bit type on Cygwin to satisfy OpenSSL dependencies. */ #undef __int64 /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define this to ASN1_STRING_get0_data when using OpenSSL 1.1.0+, ASN1_STRING_data otherwise. */ #undef egg_ASN1_string_data /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to the type of a signed integer type wide enough to hold a pointer, if such a type exists, and if the system does not define it. */ #undef intptr_t /* Define as a signed integer type capable of holding a process identifier. */ #undef pid_t /* Define to `unsigned int' if does not define. */ #undef size_t /* Substitute for socklen_t */ #undef socklen_t /* Define to empty if the keyword `volatile' does not work. Warning: valid code using `volatile' can become incorrect without. Disable with care. */ #undef volatile #endif /* !_EGG_CONFIG_H */ eggheads-eggdrop-9c00fe1/configure000077500000000000000000013046331524730042700172440ustar00rootroot00000000000000#! /bin/sh # From configure.ac 1e0fe383. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Eggdrop 1.10.1. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # # Copyright (C) 1999 - 2025 Eggheads Development Team ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) 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 test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and bugs@eggheads.org $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Eggdrop' PACKAGE_TARNAME='eggdrop' PACKAGE_VERSION='1.10.1' PACKAGE_STRING='Eggdrop 1.10.1' PACKAGE_BUGREPORT='bugs@eggheads.org' PACKAGE_URL='https://www.eggheads.org' ac_unique_file="src/eggdrop.h" ac_default_prefix=\${HOME}/eggdrop # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_func_c_list= ac_subst_vars='LTLIBOBJS EGG_CROSS_COMPILING MOD_UPDIR DEST EGGVERSION egg_with_python_config SSL_LIBS SSL_INCLUDES DEBCFLGS CFLGS TCL_INCLUDE_SPEC MATH_LIBS TCL_SHLIB_LD_LIBS TCL_LD_FLAGS TCL_EXTRA_CFLAGS TCL_DEFS TCL_LIBS CLEANFILES TCL_STUB_LIB_SPEC TCL_STUB_LIB_FLAG TCL_STUB_LIB_FILE TCL_LIB_SPEC TCL_LIB_FLAG TCL_LIB_FILE TCL_SRC_DIR TCL_BIN_DIR TCL_PATCH_LEVEL TCL_VERSION PKG_CFLAGS PKG_LIBS PKG_INCLUDES PKG_HEADERS PKG_TCL_SOURCES PKG_STUB_OBJECTS PKG_STUB_SOURCES PKG_STUB_LIB_FILE PKG_LIB_FILE CYGPATH TEA_TK_EXTENSION LIBOBJS SHLIB_STRIP SHLIB_CC SHLIB_LD MOD_STRIP MOD_CC MOD_LD MODULE_XLIBS MOD_EXT DEFAULT_MAKE target_os target_vendor target_cpu target PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CXX PTHREAD_CC ax_pthread_config EGREP GREP CPP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build UNAME BASENAME AWK STRIP HEAD_1 LN_S RANLIB MKDIR_P SET_MAKE MAKE INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM EGGEXEC OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC egg_ac_parameters target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_strip enable_coverage with_tcllib with_tclinc with_tcl enable_debug enable_debug_assert enable_debug_mem enable_debug_dns enable_debug_context with_handlen enable_ipv6 enable_tls with_sslinc with_ssllib enable_tdns with_python_config ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Eggdrop 1.10.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/eggdrop] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Eggdrop 1.10.1:";; esac cat <<\_ACEOF Optional Features and Packages: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --enable-strip enable stripping of binaries --enable-coverage build with gcov instrumentation (developer / test-coverage) --with-tcllib=PATH full path to Tcl library (e.g. /usr/lib/libtcl8.6.so) --with-tclinc=PATH full path to Tcl header (e.g. /usr/include/tcl.h) --with-tcl directory containing tcl configuration (tclConfig.sh) --enable-debug enable generic debug code (default for 'make debug') --disable-debug disable generic debug code --enable-debug-assert enable assert debug code (default for 'make debug') --disable-debug-assert disable assert debug code --enable-debug-mem enable memory debug code (default for 'make debug') --disable-debug-mem disable memory debug code --enable-debug-dns enable dns.mod debug messages (default for 'make debug') --disable-debug-dns disable dns.mod debug messages --enable-debug-context enable context debug code (default) --disable-debug-context disable context debug code --with-handlen=VALUE set the maximum length a handle on the bot can be --enable-ipv6 enable IPv6 support (autodetect) --disable-ipv6 disable IPv6 support --disable-tls disable TLS support --with-sslinc=PATH Path to OpenSSL headers --with-ssllib=PATH Path to OpenSSL libraries --disable-tdns disable threaded DNS core --with-python-config=PATH Path to python-config Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . Eggdrop home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF Eggdrop configure 1.10.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Copyright (C) 1999 - 2025 Eggheads Development Team _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_try_run LINENO # ---------------------- # Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that # executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 printf %s "checking for $2.$3... " >&6; } if eval test \${$4+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$4 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member # ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR # ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_check_decl # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=$ac_mid; break else $as_nop as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_lo=$ac_mid; break else $as_nop as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done else $as_nop ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=$ac_mid else $as_nop as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval (void) { return $2; } static unsigned long int ulongval (void) { return $2; } #include #include int main (void) { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Eggdrop $as_me 1.10.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME 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 printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" 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/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" as_fn_append ac_func_c_list " getpagesize HAVE_GETPAGESIZE" # Auxiliary files required by this configure script. ac_aux_files="config.guess config.sub install-sh" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}/misc" # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" # Print start message { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: This is Eggdrop's GNU configure script." >&5 printf "%s\n" "This is Eggdrop's GNU configure script." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: It's going to run a bunch of tests to hopefully make your compile" >&5 printf "%s\n" "It's going to run a bunch of tests to hopefully make your compile" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: work without much twiddling." >&5 printf "%s\n" "work without much twiddling." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } egg_ac_parameters= ac_prev= for ac_arg in $ac_configure_args; do if test -n "$ac_prev"; then ac_prev= continue fi case $ac_arg in -cache-file | --cache-file | --cache-fil | --cache-fi | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) ;; --config-cache | -C) ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) ;; *) egg_ac_parameters="$egg_ac_parameters $ac_arg" ;; esac done printf "%s\n" "#define EGG_AC_ARGS_RAW $egg_ac_parameters" >>confdefs.h # Check for a working C compiler ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in gcc cc clang do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; 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_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else 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_prog_CC="$ac_tool_prefix$ac_prog" 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 fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in gcc cc clang do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; 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_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else 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_prog_ac_ct_CC="$ac_prog" 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 fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$cross_compiling" = x; then cat << 'EOF' >&2 configure: error: This system does not appear to have a working C compiler. A working C compiler is required to compile Eggdrop. EOF exit 1 fi if test "$ac_cv_prog_cc_c11" = no && test "$ac_cv_prog_cc_c99" = no; then cat << 'EOF' >&2 configure: error: This C compiler does not appear to have a working C99/C11 mode. A working C99/C11 C compiler is required to compile Eggdrop. EOF exit 1 fi # These 3 need to be done before any AC_COMPILE_IFELSE()'s. ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; } if test ${ac_cv_safe_to_define___extensions__+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_safe_to_define___extensions__=yes else $as_nop ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether _XOPEN_SOURCE should be defined" >&5 printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; } if test ${ac_cv_should_define__xopen_source+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_should_define__xopen_source=no if test $ac_cv_header_wchar_h = yes then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include mbstate_t x; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE 500 #include mbstate_t x; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_should_define__xopen_source=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5 printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; } printf "%s\n" "#define _ALL_SOURCE 1" >>confdefs.h printf "%s\n" "#define _DARWIN_C_SOURCE 1" >>confdefs.h printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h printf "%s\n" "#define _HPUX_ALT_XOPEN_SOCKET_API 1" >>confdefs.h printf "%s\n" "#define _NETBSD_SOURCE 1" >>confdefs.h printf "%s\n" "#define _OPENBSD_SOURCE 1" >>confdefs.h printf "%s\n" "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_BFP_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_LIB_EXT2__ 1" >>confdefs.h printf "%s\n" "#define __STDC_WANT_MATH_SPEC_FUNCS__ 1" >>confdefs.h printf "%s\n" "#define _TANDEM_SOURCE 1" >>confdefs.h if test $ac_cv_header_minix_config_h = yes then : MINIX=yes printf "%s\n" "#define _MINIX 1" >>confdefs.h printf "%s\n" "#define _POSIX_SOURCE 1" >>confdefs.h printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h else $as_nop MINIX= fi if test $ac_cv_safe_to_define___extensions__ = yes then : printf "%s\n" "#define __EXTENSIONS__ 1" >>confdefs.h fi if test $ac_cv_should_define__xopen_source = yes then : printf "%s\n" "#define _XOPEN_SOURCE 500" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing strerror" >&5 printf %s "checking for library containing strerror... " >&6; } if test ${ac_cv_search_strerror+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char strerror (); int main (void) { return strerror (); ; return 0; } _ACEOF for ac_lib in '' cposix do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_strerror=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_strerror+y} then : break fi done if test ${ac_cv_search_strerror+y} then : else $as_nop ac_cv_search_strerror=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strerror" >&5 printf "%s\n" "$ac_cv_search_strerror" >&6; } ac_res=$ac_cv_search_strerror if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Check C compiler characteristics. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for icc" >&5 printf %s "checking for icc... " >&6; } if test ${egg_cv_var_cc_icc+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if !(defined(__ICC) || defined(__ECC) || defined(__INTEL_COMPILER)) "Toto, I've a feeling we're not in Kansas anymore." #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : egg_cv_var_cc_icc="yes" else $as_nop egg_cv_var_cc_icc="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_cc_icc" >&5 printf "%s\n" "$egg_cv_var_cc_icc" >&6; } if test "$egg_cv_var_cc_icc" = yes; then ICC="yes" else ICC="no" fi if test "$GCC" = yes && test "$ICC" = no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler understands -pipe" >&5 printf %s "checking whether the compiler understands -pipe... " >&6; } if test ${egg_cv_var_ccpipe+y} then : printf %s "(cached) " >&6 else $as_nop ac_old_CC="$CC" CC="$CC -pipe" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { return(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : egg_cv_var_ccpipe="yes" else $as_nop egg_cv_var_ccpipe="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CC="$ac_old_CC" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_ccpipe" >&5 printf "%s\n" "$egg_cv_var_ccpipe" >&6; } if test "$egg_cv_var_ccpipe" = yes; then if test "x-pipe" != x; then if test "x$CFLAGS" = x; then CFLAGS="-pipe" else CFLAGS="$CFLAGS -pipe" fi fi fi fi if test "$GCC" = yes && test "$ICC" = no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler understands -Wall" >&5 printf %s "checking whether the compiler understands -Wall... " >&6; } if test ${egg_cv_var_ccwall+y} then : printf %s "(cached) " >&6 else $as_nop ac_old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { return(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : egg_cv_var_ccwall="yes" else $as_nop egg_cv_var_ccwall="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$ac_old_CFLAGS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_ccwall" >&5 printf "%s\n" "$egg_cv_var_ccwall" >&6; } if test "$egg_cv_var_ccwall" = yes; then if test "x-Wall" != x; then if test "x$CFLAGS" = x; then CFLAGS="-Wall" else CFLAGS="$CFLAGS -Wall" fi fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler accepts -std=gnu99" >&5 printf %s "checking whether the C compiler accepts -std=gnu99... " >&6; } if test ${ax_cv_check_cflags___std_gnu99+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS if test x"$GCC" = xyes ; then add_gnu_werror="-Werror" fi CFLAGS="$CFLAGS -std=gnu99 $add_gnu_werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___std_gnu99=yes else $as_nop ax_cv_check_cflags___std_gnu99=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___std_gnu99" >&5 printf "%s\n" "$ax_cv_check_cflags___std_gnu99" >&6; } if test "x$ax_cv_check_cflags___std_gnu99" = xyes then : CFLAGS="-std=gnu99 $CFLAGS" else $as_nop : fi # Check for executable extension. EGGEXEC="eggdrop" if test "x$EXEEXT" != x; then EGGEXEC="eggdrop${EXEEXT}" fi # Checks for programs. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop 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 # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' for ac_prog in make gmake do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; 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_prog_MAKE+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$MAKE"; then ac_cv_prog_MAKE="$MAKE" # Let the user override the test. else 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_prog_MAKE="$ac_prog" 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 fi fi MAKE=$ac_cv_prog_MAKE if test -n "$MAKE"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAKE" >&5 printf "%s\n" "$MAKE" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$MAKE" && break done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else $as_nop cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ 'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$MKDIR_P" >&6; } # Replace relative path with absolute path MKDIR_P=$(echo "$MKDIR_P" | sed "s|^./misc|${ac_pwd}/misc|") if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; 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_prog_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else 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_prog_RANLIB="${ac_tool_prefix}ranlib" 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 fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; 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_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else 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_prog_ac_ct_RANLIB="ranlib" 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 fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi cat << 'EOF' > conftest.head a b c EOF for ac_prog in 'head -n 1' 'head -1' 'sed 1q'; do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $ac_prog works" >&5 printf %s "checking whether $ac_prog works... " >&6; } if test ${ac_cv_prog_HEAD_1+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$HEAD_1"; then # Let the user override the test. ac_cv_prog_HEAD_1="$HEAD_1" else if test `cat conftest.head | $ac_prog` = a; then ac_cv_prog_HEAD_1="$ac_prog" fi fi fi if test -n "$ac_cv_prog_HEAD_1"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } break else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi done if test "x$ac_cv_prog_HEAD_1" = x; then cat << 'EOF' >&2 configure: error: This system seems to lack a working 'head -1' or 'head -n 1' command. A working 'head -1' (or equivalent) command is required to compile Eggdrop. EOF exit 1 fi rm -f conftest.head HEAD_1="$ac_cv_prog_HEAD_1" # Extract the first word of "strip", so it can be a program name with args. set dummy strip; 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_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else 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_prog_STRIP="strip" 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 fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$STRIP" = x; then STRIP=touch fi 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else 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_prog_AWK="$ac_prog" 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 fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done if test "x$AWK" = x; then cat << 'EOF' >&2 configure: error: This system seems to lack a working 'awk' command. A working 'awk' command is required to compile Eggdrop. EOF exit 1 fi # Extract the first word of "basename", so it can be a program name with args. set dummy basename; 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_prog_BASENAME+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$BASENAME"; then ac_cv_prog_BASENAME="$BASENAME" # Let the user override the test. else 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_prog_BASENAME="basename" 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 fi fi BASENAME=$ac_cv_prog_BASENAME if test -n "$BASENAME"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BASENAME" >&5 printf "%s\n" "$BASENAME" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$BASENAME" = x; then cat << 'EOF' >&2 configure: error: This system seems to lack a working 'basename' command. A working 'basename' command is required to compile Eggdrop. EOF exit 1 fi # Extract the first word of "uname", so it can be a program name with args. set dummy uname; 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_prog_UNAME+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$UNAME"; then ac_cv_prog_UNAME="$UNAME" # Let the user override the test. else 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_prog_UNAME="uname" 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 fi fi UNAME=$ac_cv_prog_UNAME if test -n "$UNAME"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $UNAME" >&5 printf "%s\n" "$UNAME" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Check if we will strip the binary # Do this *before* EGG_CHECK_OS # Check whether --enable-strip was given. if test ${enable_strip+y} then : enableval=$enable_strip; enable_strip="$enableval" else $as_nop enable_strip="no" fi if test "$enable_strip" = yes; then cat << 'EOF' >&2 configure: WARNING: Stripping the executable, while saving some disk space, will make bug reports nearly worthless. If Eggdrop crashes and you wish to report a bug, you will need to recompile with stripping disabled. EOF else STRIP="touch" fi # Optional: build with gcov instrumentation for the test suite. # Check whether --enable-coverage was given. if test ${enable_coverage+y} then : enableval=$enable_coverage; enable_coverage="$enableval" else $as_nop enable_coverage="no" fi if test "$enable_coverage" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: enabling gcov coverage instrumentation: --coverage -fPIC -O0 -ggdb3" >&5 printf "%s\n" "$as_me: enabling gcov coverage instrumentation: --coverage -fPIC -O0 -ggdb3" >&6;} CFLAGS="$CFLAGS --coverage -fPIC -O0 -ggdb3" LDFLAGS="$LDFLAGS --coverage" fi # Checks for system libraries. # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 printf %s "checking for a sed that does not truncate output... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 else $as_nop ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST 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_prog in sed gsed do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 printf "%s\n" "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 printf %s "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test ${ac_cv_prog_CPP+y} then : printf %s "(cached) " >&6 else $as_nop # Double quotes because $CC needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 printf "%s\n" "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in grep ggrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 printf %s "checking for egrep... " >&6; } if test ${ac_cv_path_EGREP+y} then : printf %s "(cached) " >&6 else $as_nop if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in egrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" # FIXME: this needs to be fixed so that it works on IRIX if test "$IRIX" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Skipping library tests because they CONFUSE IRIX." >&5 printf "%s\n" "$as_me: WARNING: Skipping library tests because they CONFUSE IRIX." >&2;} else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 printf %s "checking for library containing gethostbyname... " >&6; } if test ${ac_cv_search_gethostbyname+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gethostbyname (); int main (void) { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' nsl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_gethostbyname+y} then : break fi done if test ${ac_cv_search_gethostbyname+y} then : else $as_nop ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 printf "%s\n" "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 printf %s "checking for library containing socket... " >&6; } if test ${ac_cv_search_socket+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char socket (); int main (void) { return socket (); ; return 0; } _ACEOF for ac_lib in '' socket do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_socket=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_socket+y} then : break fi done if test ${ac_cv_search_socket+y} then : else $as_nop ac_cv_search_socket=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 printf "%s\n" "$ac_cv_search_socket" >&6; } ac_res=$ac_cv_search_socket if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 printf %s "checking for socket in -lsocket... " >&6; } if test ${ac_cv_lib_socket_socket+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket -lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char socket (); int main (void) { return socket (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_socket=yes else $as_nop ac_cv_lib_socket_socket=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 printf "%s\n" "$ac_cv_lib_socket_socket" >&6; } if test "x$ac_cv_lib_socket_socket" = xyes then : LIBS="-lsocket -lnsl $LIBS" fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" if test "x$PTHREAD_CC" != "x" then : CC="$PTHREAD_CC" fi if test "x$PTHREAD_CXX" != "x" then : CXX="$PTHREAD_CXX" fi CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS" >&5 printf %s "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pthread_join (); int main (void) { return pthread_join (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 printf "%s\n" "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items with a "," contain both # C compiler flags (before ",") and linker flags (after ","). Other items # starting with a "-" are C compiler flags, and remaining items are # library names, except for "none" which indicates that we try without # any flags at all, and "pthread-config" which is a program returning # the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1 then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5 printf "%s\n" "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;} fi rm -rf conftest* ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" ;; esac # Are we compiling with Clang? { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC is Clang" >&5 printf %s "checking whether $CC is Clang... " >&6; } if test ${ax_cv_PTHREAD_CLANG+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1 then : ax_cv_PTHREAD_CLANG=yes fi rm -rf conftest* fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5 printf "%s\n" "$ax_cv_PTHREAD_CLANG" >&6; } ax_pthread_clang="$ax_cv_PTHREAD_CLANG" # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) # Note that for GCC and Clang -pthread generally implies -lpthread, # except when -nostdlib is passed. # This is problematic using libtool to build C++ shared libraries with pthread: # [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 # [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 # [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 # To solve this, first try -pthread together with -lpthread for GCC if test "x$GCC" = "xyes" then : ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags" fi # Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first if test "x$ax_pthread_clang" = "xyes" then : ax_pthread_flags="-pthread,-lpthread -pthread" fi # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac if test "x$ax_pthread_check_macro" = "x--" then : ax_pthread_check_cond=0 else $as_nop ax_pthread_check_cond="!defined($ax_pthread_check_macro)" fi if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 printf %s "checking whether pthreads work without any flags... " >&6; } ;; *,*) PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with \"$PTHREAD_CFLAGS\" and \"$PTHREAD_LIBS\"" >&5 printf %s "checking whether pthreads work with \"$PTHREAD_CFLAGS\" and \"$PTHREAD_LIBS\"... " >&6; } ;; -*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $ax_pthread_try_flag" >&5 printf %s "checking whether pthreads work with $ax_pthread_try_flag... " >&6; } PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) # Extract the first word of "pthread-config", so it can be a program name with args. set dummy pthread-config; 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_prog_ax_pthread_config+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ax_pthread_config"; then ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. else 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_prog_ax_pthread_config="yes" 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 test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" fi fi ax_pthread_config=$ac_cv_prog_ax_pthread_config if test -n "$ax_pthread_config"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 printf "%s\n" "$ax_pthread_config" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ax_pthread_config" = "xno" then : continue fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$ax_pthread_try_flag" >&5 printf %s "checking for the pthreads library -l$ax_pthread_try_flag... " >&6; } PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void *some_global = NULL; static void routine(void *a) { /* To avoid any unused-parameter or unused-but-set-parameter warning. */ some_global = a; } static void *start_routine(void *a) { return a; } int main (void) { pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_pthread_ok=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 printf "%s\n" "$ax_pthread_ok" >&6; } if test "x$ax_pthread_ok" = "xyes" then : break fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread" >&5 printf %s "checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread... " >&6; } if test ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`printf "%s\n" "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do if test "x$ax_pthread_try" = "xunknown" then : break fi CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void){return 0;} _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_link="$ax_pthread_2step_ac_link" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void){return 0;} _ACEOF if ac_fn_c_try_link "$LINENO" then : break fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" if test "x$ax_pthread_try" = "x" then : ax_pthread_try=no fi ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5 printf "%s\n" "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; } case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 printf %s "checking for joinable pthread attribute... " >&6; } if test ${ax_cv_PTHREAD_JOINABLE_ATTR+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int attr = $ax_pthread_attr; return attr /* ; */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5 printf "%s\n" "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; } if test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes" then : printf "%s\n" "#define PTHREAD_CREATE_JOINABLE $ax_cv_PTHREAD_JOINABLE_ATTR" >>confdefs.h ax_pthread_joinable_attr_defined=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether more special flags are required for pthreads" >&5 printf %s "checking whether more special flags are required for pthreads... " >&6; } if test ${ax_cv_PTHREAD_SPECIAL_FLAGS+y} then : printf %s "(cached) " >&6 else $as_nop ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5 printf "%s\n" "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; } if test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes" then : PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 printf %s "checking for PTHREAD_PRIO_INHERIT... " >&6; } if test ${ax_cv_PTHREAD_PRIO_INHERIT+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int i = PTHREAD_PRIO_INHERIT; return i; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ax_cv_PTHREAD_PRIO_INHERIT=yes else $as_nop ax_cv_PTHREAD_PRIO_INHERIT=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 printf "%s\n" "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes" then : printf "%s\n" "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h ax_pthread_prio_inherit_defined=yes fi CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) case "x/$CC" in #( x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : #handle absolute path differently from PATH based program lookup case "x$CC" in #( x/*) : if as_fn_executable_p ${CC}_r then : PTHREAD_CC="${CC}_r" fi if test "x${CXX}" != "x" then : if as_fn_executable_p ${CXX}_r then : PTHREAD_CXX="${CXX}_r" fi fi ;; #( *) : for ac_prog in ${CC}_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; 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_prog_PTHREAD_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$PTHREAD_CC"; then ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else 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_prog_PTHREAD_CC="$ac_prog" 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 fi fi PTHREAD_CC=$ac_cv_prog_PTHREAD_CC if test -n "$PTHREAD_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 printf "%s\n" "$PTHREAD_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$PTHREAD_CC" && break done test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" if test "x${CXX}" != "x" then : for ac_prog in ${CXX}_r do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; 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_prog_PTHREAD_CXX+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$PTHREAD_CXX"; then ac_cv_prog_PTHREAD_CXX="$PTHREAD_CXX" # Let the user override the test. else 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_prog_PTHREAD_CXX="$ac_prog" 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 fi fi PTHREAD_CXX=$ac_cv_prog_PTHREAD_CXX if test -n "$PTHREAD_CXX"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CXX" >&5 printf "%s\n" "$PTHREAD_CXX" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$PTHREAD_CXX" && break done test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" fi ;; esac ;; #( *) : ;; esac ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then LDFLAGS="$LDFLAGS $PTHREAD_LIBS" : else ax_pthread_ok=no fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 printf %s "checking for library containing dlopen... " >&6; } if test ${ac_cv_search_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF for ac_lib in '' dl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_dlopen=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_dlopen+y} then : break fi done if test ${ac_cv_search_dlopen+y} then : else $as_nop ac_cv_search_dlopen=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 printf "%s\n" "$ac_cv_search_dlopen" >&6; } ac_res=$ac_cv_search_dlopen if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tan in -lm" >&5 printf %s "checking for tan in -lm... " >&6; } if test ${ac_cv_lib_m_tan+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char tan (); int main (void) { return tan (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_m_tan=yes else $as_nop ac_cv_lib_m_tan=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_tan" >&5 printf "%s\n" "$ac_cv_lib_m_tan" >&6; } if test "x$ac_cv_lib_m_tan" = xyes then : EGG_MATH_LIB="-lm" fi if test "$HPUX" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 printf %s "checking for shl_load in -ldld... " >&6; } if test ${ac_cv_lib_dld_shl_load+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char shl_load (); int main (void) { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dld_shl_load=yes else $as_nop ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes then : printf "%s\n" "#define HAVE_LIBDLD 1" >>confdefs.h LIBS="-ldld $LIBS" fi fi fi # Test the OS and set the module linking settings. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 printf %s "checking target system type... " >&6; } if test ${ac_cv_target+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "${ac_aux_dir}config.sub" $target_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $target_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 printf "%s\n" "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking system type" >&5 printf %s "checking system type... " >&6; } if test ${egg_cv_var_system_type+y} then : printf %s "(cached) " >&6 else $as_nop egg_cv_var_system_type=`$UNAME -s` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_system_type" >&5 printf "%s\n" "$egg_cv_var_system_type" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking system release" >&5 printf %s "checking system release... " >&6; } if test ${egg_cv_var_system_release+y} then : printf %s "(cached) " >&6 else $as_nop egg_cv_var_system_release=`$UNAME -r` fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_system_release" >&5 printf "%s\n" "$egg_cv_var_system_release" >&6; } MODULES_OK="yes" MOD_EXT="so" DEFAULT_MAKE="eggdrop" LOAD_METHOD="dl" WEIRD_OS="yes" UNKNOWN_OS="no" MODULE_XLIBS="" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking module loading capabilities" >&5 printf %s "checking module loading capabilities... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } ac_fn_c_check_header_compile "$LINENO" "dl.h" "ac_cv_header_dl_h" "$ac_includes_default" if test "x$ac_cv_header_dl_h" = xyes then : printf "%s\n" "#define HAVE_DL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" if test "x$ac_cv_header_dlfcn_h" = xyes then : printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "loader.h" "ac_cv_header_loader_h" "$ac_includes_default" if test "x$ac_cv_header_loader_h" = xyes then : printf "%s\n" "#define HAVE_LOADER_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "mach-o/dyld.h" "ac_cv_header_mach_o_dyld_h" "$ac_includes_default" if test "x$ac_cv_header_mach_o_dyld_h" = xyes then : printf "%s\n" "#define HAVE_MACH_O_DYLD_H 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes then : printf "%s\n" "#define HAVE_DLOPEN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "load" "ac_cv_func_load" if test "x$ac_cv_func_load" = xyes then : printf "%s\n" "#define HAVE_LOAD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "NSLinkModule" "ac_cv_func_NSLinkModule" if test "x$ac_cv_func_NSLinkModule" = xyes then : printf "%s\n" "#define HAVE_NSLINKMODULE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes then : printf "%s\n" "#define HAVE_SHL_LOAD 1" >>confdefs.h fi # Note to other maintainers: # Bourne shell has no concept of "fall through" case "$egg_cv_var_system_type" in CYGWI*) WEIRD_OS="no" MOD_EXT="dll" ;; HP-UX) LOAD_METHOD="shl" ;; dell) # do nothing ;; IRIX) # do nothing ;; Ultrix) # No dlopen() or similar on Ultrix. We can't use modules. MODULES_OK="no" ;; BeOS) # We don't yet support BeOS's dynamic linking interface. MODULES_OK="no" ;; Linux) WEIRD_OS="no" ;; Lynx) # do nothing ;; QNX) # do nothing # QNX (recent versions at least) support dlopen(). ;; OSF1) case `echo "$egg_cv_var_system_release" | cut -d . -f 1` in 1.*) LOAD_METHOD="loader" ;; esac ;; SunOS) WEIRD_OS="no" ;; FreeBSD|OpenBSD|NetBSD|DragonFly) WEIRD_OS="no" ;; Darwin) # In macOS 10.4 (Darwin 8), dlopen was rewritten to be a native part of dyld. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking darwin version >= 8 with native dlopen" >&5 printf %s "checking darwin version >= 8 with native dlopen... " >&6; } darwin_major_version=`echo $egg_cv_var_system_release | cut -d. -f1` if test $darwin_major_version -ge 8; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } WEIRD_OS="no" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LOAD_METHOD="dyld" BUNDLE="" for bundlepath in "/lib" "/usr/lib" "/usr/local/lib"; do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for bundle1.o in ${bundlepath}" >&5 printf %s "checking for bundle1.o in ${bundlepath}... " >&6; } if test -r "${bundlepath}/bundle1.o"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } BUNDLE="${bundlepath}/bundle1.o" break else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi done if test "x$BUNDLE" = x; then cat << 'EOF' >&2 configure: WARNING: bundle1.o cannot be located. A module build might not compile correctly. EOF fi if test "x$BUNDLE" != x; then if test "x$MODULE_XLIBS" = x; then MODULE_XLIBS="$BUNDLE" else MODULE_XLIBS="$MODULE_XLIBS $BUNDLE" fi fi fi ;; Haiku) WEIRD_OS="no" ;; Minix) WEIRD_OS="no" ;; *) # QNX apparently supports dlopen()... Fallthrough. if test -r /cmds; then UNKNOWN_OS="yes" MODULES_OK="no" fi ;; esac if test "$MODULES_OK" = yes; then printf "%s\n" "#define MODULES_OK 1" >>confdefs.h case $LOAD_METHOD in dl) printf "%s\n" "#define MOD_USE_DL 1" >>confdefs.h ;; shl) printf "%s\n" "#define MOD_USE_SHL 1" >>confdefs.h ;; dyld) printf "%s\n" "#define MOD_USE_DYLD 1" >>confdefs.h ;; loader) printf "%s\n" "#define MOD_USE_LOADER 1" >>confdefs.h ;; esac else DEFAULT_MAKE="static" fi if test "$WEIRD_OS" = yes; then # Default to "make static" for 'weird' operating systems. Will print a # note at the end of configure explaining. This way, Eggdrop should compile # "out of the box" on most every operating system we know of, and they can # do a "make eggdrop" if they want to use(/try to use) module support. - Wcc DEFAULT_MAKE="static" fi printf "%s\n" "#define EGG_MOD_EXT \"$MOD_EXT\"" >>confdefs.h MOD_CC="$CC" MOD_LD="$CC" MOD_STRIP="$STRIP" SHLIB_CC="$CC" SHLIB_LD="$CC" SHLIB_STRIP="$STRIP" LINUX="no" IRIX="no" SUNOS_GCC="no" HPUX="no" EGG_CYGWIN="no" case "$egg_cv_var_system_type" in CYGWI*) SHLIB_LD="$CC -shared" MOD_CC="$CC" MOD_LD="$CC" EGG_CYGWIN="yes" if test "$EGG_CYGWIN" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /usr/lib/binmode.o" >&5 printf %s "checking for /usr/lib/binmode.o... " >&6; } if test -r /usr/lib/binmode.o; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } if test "x/usr/lib/binmode.o" != x; then if test "x$LIBS" = x; then LIBS="/usr/lib/binmode.o" else LIBS="$LIBS /usr/lib/binmode.o" fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Make sure the directory Eggdrop is installed into is mounted in binary mode." >&5 printf "%s\n" "$as_me: WARNING: Make sure the directory Eggdrop is installed into is mounted in binary mode." >&2;} fi fi printf "%s\n" "#define CYGWIN_HACKS 1" >>confdefs.h ;; HP-UX) HPUX="yes" if test "$CC" = cc; then # HP-UX ANSI C Compiler. MOD_LD="$CC +z" SHLIB_CC="$CC +z" else # GCC MOD_LD="$CC -fPIC -shared" SHLIB_CC="$CC -fPIC" fi SHLIB_LD="ld -b" ;; dell) SHLIB_STRIP="touch" MOD_LD="$CC -lelf -lucb" ;; IRIX) SHLIB_LD="ld -n32 -shared -rdata_shared" IRIX="yes" SHLIB_STRIP="touch" ;; Ultrix) SHLIB_STRIP="touch" DEFAULT_MAKE="static" SHELL="/bin/sh5" ;; SINIX*) SHLIB_STRIP="touch" SHLIB_CC="cc -G" ;; BeOS) # do nothing ;; Linux) LINUX="yes" MOD_LD="$CC" SHLIB_CC="$CC -fPIC" SHLIB_LD="$CC -shared -nostartfiles" ;; Lynx) # do nothing ;; QNX) SHLIB_LD="ld -shared" printf "%s\n" "#define QNX_HACKS 1" >>confdefs.h ;; OSF1) case `echo "$egg_cv_var_system_release" | cut -d . -f 1` in V*) # Digital OSF uses an ancient version of gawk if test "$AWK" = gawk; then AWK="awk" fi SHLIB_LD="ld -shared -expect_unresolved \"'*'\"" SHLIB_STRIP="touch" ;; 1.0|1.1|1.2) SHLIB_LD="ld -R -export :" ;; 1.*) SHLIB_CC="$CC -fpic" SHLIB_LD="ld -shared" ;; esac printf "%s\n" "#define BROKEN_SNPRINTF 1" >>confdefs.h printf "%s\n" "#define STOP_UAC 1" >>confdefs.h ;; SunOS) if test -n "$GCC"; then SUNOS_GCC="yes" SHLIB_CC="$CC -fPIC" SHLIB_LD="$CC -shared" else SHLIB_CC="$CC -KPIC" SHLIB_LD="$CC -G -z text" fi ;; FreeBSD|DragonFly|OpenBSD|NetBSD) SHLIB_CC="$CC -fPIC" SHLIB_LD="$CC -shared" ;; Darwin) # macOS. SHLIB_CC="$CC -fPIC" case "$egg_cv_var_system_release" in 2*) SHLIB_LD="$CC -shared" ;; *) # macOS < 11 (Darwin 20). SHLIB_LD="ld -bundle -undefined error" ;; esac printf "%s\n" "#define BIND_8_COMPAT 1" >>confdefs.h ;; *) if test -r /cmds; then # Probably QNX. SHLIB_LD="ld -shared" SHLIB_STRIP="touch" fi ;; esac # Checks for header files. if test "$ac_cv_header_stdc" = no; then cat << 'EOF' >&2 configure: error: Your system must support ANSI C Header files. These are required for the language support. Sorry. EOF exit 1 fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 printf %s "checking for $ac_hdr that defines DIR... " >&6; } if eval test \${$as_ac_Header+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include <$ac_hdr> int main (void) { if ((DIR *) 0) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$as_ac_Header=yes" else $as_nop eval "$as_ac_Header=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$as_ac_Header { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF ac_header_dirent=$ac_hdr; break fi done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 printf %s "checking for library containing opendir... " >&6; } if test ${ac_cv_search_opendir+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char opendir (); int main (void) { return opendir (); ; return 0; } _ACEOF for ac_lib in '' dir do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_opendir=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_opendir+y} then : break fi done if test ${ac_cv_search_opendir+y} then : else $as_nop ac_cv_search_opendir=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 printf "%s\n" "$ac_cv_search_opendir" >&6; } ac_res=$ac_cv_search_opendir if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 printf %s "checking for library containing opendir... " >&6; } if test ${ac_cv_search_opendir+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char opendir (); int main (void) { return opendir (); ; return 0; } _ACEOF for ac_lib in '' x do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_opendir=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_opendir+y} then : break fi done if test ${ac_cv_search_opendir+y} then : else $as_nop ac_cv_search_opendir=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 printf "%s\n" "$ac_cv_search_opendir" >&6; } ac_res=$ac_cv_search_opendir if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" 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 : printf "%s\n" "#define HAVE_ARPA_INET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" if test "x$ac_cv_header_fcntl_h" = xyes then : printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes then : printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default" if test "x$ac_cv_header_locale_h" = xyes then : printf "%s\n" "#define HAVE_LOCALE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default" if test "x$ac_cv_header_netdb_h" = xyes then : printf "%s\n" "#define HAVE_NETDB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_in_h" = xyes then : printf "%s\n" "#define HAVE_NETINET_IN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdio.h" "ac_cv_header_stdio_h" "$ac_includes_default" if test "x$ac_cv_header_stdio_h" = xyes then : printf "%s\n" "#define HAVE_STDIO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdarg.h" "ac_cv_header_stdarg_h" "$ac_includes_default" if test "x$ac_cv_header_stdarg_h" = xyes then : printf "%s\n" "#define HAVE_STDARG_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stddef.h" "ac_cv_header_stddef_h" "$ac_includes_default" if test "x$ac_cv_header_stddef_h" = xyes then : printf "%s\n" "#define HAVE_STDDEF_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/file.h" "ac_cv_header_sys_file_h" "$ac_includes_default" if test "x$ac_cv_header_sys_file_h" = xyes then : printf "%s\n" "#define HAVE_SYS_FILE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" if test "x$ac_cv_header_sys_param_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" if test "x$ac_cv_header_sys_select_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" if test "x$ac_cv_header_sys_socket_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" 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 then : printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "wchar.h" "ac_cv_header_wchar_h" "$ac_includes_default" if test "x$ac_cv_header_wchar_h" = xyes then : printf "%s\n" "#define HAVE_WCHAR_H 1" >>confdefs.h fi # Checks for typedefs, structures, and compiler characteristics. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 printf %s "checking for an ANSI C-conforming const... " >&6; } if test ${ac_cv_c_const+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __cplusplus /* Ultrix mips cc rejects this sort of thing. */ typedef int charset[2]; const charset cs = { 0, 0 }; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* IBM XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this sort of thing. */ char tx; char *t = &tx; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* IBM XL C 1.02.0.0 rejects this sort of thing, saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; } bx; struct s *b = &bx; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_const=yes else $as_nop ac_cv_c_const=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 printf "%s\n" "$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then printf "%s\n" "#define const /**/" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 printf %s "checking whether byte ordering is bigendian... " >&6; } if test ${ac_cv_c_bigendian+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO" then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ unsigned short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; unsigned short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } unsigned short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; unsigned short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main (void) { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_bigendian=no else $as_nop ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 printf "%s\n" "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 printf %s "checking for inline... " >&6; } if test ${ac_cv_c_inline+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo (void) {return 0; } $ac_kw foo_t foo (void) {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for flexible array members" >&5 printf %s "checking for flexible array members... " >&6; } if test ${ac_cv_c_flexmember+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include struct s { int n; double d[]; }; int main (void) { int m = getchar (); struct s *p = (struct s *) malloc (offsetof (struct s, d) + m * sizeof (double)); p->d[0] = 0.0; return p->d != (double *) NULL; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_flexmember=yes else $as_nop ac_cv_c_flexmember=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_flexmember" >&5 printf "%s\n" "$ac_cv_c_flexmember" >&6; } if test $ac_cv_c_flexmember = yes; then printf "%s\n" "#define FLEXIBLE_ARRAY_MEMBER /**/" >>confdefs.h else printf "%s\n" "#define FLEXIBLE_ARRAY_MEMBER 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "intptr_t" "ac_cv_type_intptr_t" "$ac_includes_default" if test "x$ac_cv_type_intptr_t" = xyes then : printf "%s\n" "#define HAVE_INTPTR_T 1" >>confdefs.h else $as_nop for ac_type in 'int' 'long int' 'long long int'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { static int test_array [1 - 2 * !(sizeof (void *) <= sizeof ($ac_type))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define intptr_t $ac_type" >>confdefs.h ac_type= fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test -z "$ac_type" && break done fi ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default " if test "x$ac_cv_type_pid_t" = xyes then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined _WIN64 && !defined __CYGWIN__ LLP64 #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_pid_type='int' else $as_nop ac_pid_type='__int64' fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : else $as_nop printf "%s\n" "#define size_t unsigned int" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 printf %s "checking whether struct tm is in sys/time.h or time.h... " >&6; } if test ${ac_cv_struct_tm+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct tm tm; int *p = &tm.tm_sec; return !p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_struct_tm=time.h else $as_nop ac_cv_struct_tm=sys/time.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 printf "%s\n" "$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then printf "%s\n" "#define TM_IN_SYS_TIME 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' else $as_nop ac_cv_c_undeclared_builtin_options=$ac_arg fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; esac ac_fn_c_check_member "$LINENO" "struct tm" "tm_zone" "ac_cv_member_struct_tm_tm_zone" "#include #include <$ac_cv_struct_tm> " if test "x$ac_cv_member_struct_tm_tm_zone" = xyes then : printf "%s\n" "#define HAVE_STRUCT_TM_TM_ZONE 1" >>confdefs.h fi if test "$ac_cv_member_struct_tm_tm_zone" = yes; then printf "%s\n" "#define HAVE_TM_ZONE 1" >>confdefs.h else ac_fn_check_decl "$LINENO" "tzname" "ac_cv_have_decl_tzname" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_tzname" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_TZNAME $ac_have_decl" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tzname" >&5 printf %s "checking for tzname... " >&6; } if test ${ac_cv_var_tzname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #if !HAVE_DECL_TZNAME extern char *tzname[]; #endif int main (void) { return tzname[0][0]; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_var_tzname=yes else $as_nop ac_cv_var_tzname=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_var_tzname" >&5 printf "%s\n" "$ac_cv_var_tzname" >&6; } if test $ac_cv_var_tzname = yes; then printf "%s\n" "#define HAVE_TZNAME 1" >>confdefs.h fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 printf %s "checking for working volatile... " >&6; } if test ${ac_cv_c_volatile+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { volatile int x; int * volatile y = (int *) 0; return !x && !y; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_volatile=yes else $as_nop ac_cv_c_volatile=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5 printf "%s\n" "$ac_cv_c_volatile" >&6; } if test $ac_cv_c_volatile = no; then printf "%s\n" "#define volatile /**/" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for socklen_t" >&5 printf %s "checking for socklen_t... " >&6; } if test ${ac_cv_ax_type_socklen_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { socklen_t len = (socklen_t) 42; return (!len); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_ax_type_socklen_t=yes else $as_nop ac_cv_ax_type_socklen_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ax_type_socklen_t" >&5 printf "%s\n" "$ac_cv_ax_type_socklen_t" >&6; } if test $ac_cv_ax_type_socklen_t != yes; then printf "%s\n" "#define socklen_t int" >>confdefs.h fi # Create stdint.h C99 compatibility header. # ------ AX CREATE STDINT H ------------------------------------- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint types" >&5 printf %s "checking for stdint types... " >&6; } ac_stdint_h=`echo eggint.h` # try to shortcircuit - if the default include path of the compiler # can find a "stdint.h" header then we assume that all compilers can. if test ${ac_cv_header_stdint_t+y} then : printf %s "(cached) " >&6 else $as_nop old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" old_CFLAGS="$CFLAGS" ; CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int_least32_t v = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_stdint_result="(assuming C99 compatible system)" ac_cv_header_stdint_t="stdint.h"; else $as_nop ac_cv_header_stdint_t="" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then CFLAGS="-std=c99" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int_least32_t v = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: your GCC compiler has a defunct stdint.h for its default-mode" >&5 printf "%s\n" "$as_me: WARNING: your GCC compiler has a defunct stdint.h for its default-mode" >&2;} fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi CXXFLAGS="$old_CXXFLAGS" CPPFLAGS="$old_CPPFLAGS" CFLAGS="$old_CFLAGS" fi v="... $ac_cv_header_stdint_h" if test "$ac_stdint_h" = "stdint.h" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (are you sure you want them in ./stdint.h?)" >&5 printf "%s\n" "(are you sure you want them in ./stdint.h?)" >&6; } elif test "$ac_stdint_h" = "inttypes.h" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (are you sure you want them in ./inttypes.h?)" >&5 printf "%s\n" "(are you sure you want them in ./inttypes.h?)" >&6; } elif test "_$ac_cv_header_stdint_t" = "_" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (putting them into $ac_stdint_h)$v" >&5 printf "%s\n" "(putting them into $ac_stdint_h)$v" >&6; } else ac_cv_header_stdint="$ac_cv_header_stdint_t" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint (shortcircuit)" >&5 printf "%s\n" "$ac_cv_header_stdint (shortcircuit)" >&6; } fi if test "_$ac_cv_header_stdint_t" = "_" ; then # cannot shortcircuit.. inttype_headers=`echo | sed -e 's/,/ /g'` ac_cv_stdint_result="(no helpful system typedefs seen)" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint uintptr_t" >&5 printf %s "checking for stdint uintptr_t... " >&6; } if test ${ac_cv_header_stdint_x+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 printf "%s\n" "(..)" >&6; } for i in stdint.h inttypes.h sys/inttypes.h $inttype_headers do unset ac_cv_type_uintptr_t unset ac_cv_type_uint64_t ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "#include <$i> " if test "x$ac_cv_type_uintptr_t" = xyes then : ac_cv_header_stdint_x=$i else $as_nop continue fi ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "#include<$i> " if test "x$ac_cv_type_uint64_t" = xyes then : and64="/uint64_t" else $as_nop and64="" fi ac_cv_stdint_result="(seen uintptr_t$and64 in $i)" break done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint uintptr_t" >&5 printf %s "checking for stdint uintptr_t... " >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint_x" >&5 printf "%s\n" "$ac_cv_header_stdint_x" >&6; } if test "_$ac_cv_header_stdint_x" = "_" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint uint32_t" >&5 printf %s "checking for stdint uint32_t... " >&6; } if test ${ac_cv_header_stdint_o+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 printf "%s\n" "(..)" >&6; } for i in inttypes.h sys/inttypes.h stdint.h $inttype_headers do unset ac_cv_type_uint32_t unset ac_cv_type_uint64_t ac_fn_c_check_type "$LINENO" "uint32_t" "ac_cv_type_uint32_t" "#include <$i> " if test "x$ac_cv_type_uint32_t" = xyes then : ac_cv_header_stdint_o=$i else $as_nop continue fi ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "#include<$i> " if test "x$ac_cv_type_uint64_t" = xyes then : and64="/uint64_t" else $as_nop and64="" fi ac_cv_stdint_result="(seen uint32_t$and64 in $i)" break break; done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint uint32_t" >&5 printf %s "checking for stdint uint32_t... " >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint_o" >&5 printf "%s\n" "$ac_cv_header_stdint_o" >&6; } fi if test "_$ac_cv_header_stdint_x" = "_" ; then if test "_$ac_cv_header_stdint_o" = "_" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint u_int32_t" >&5 printf %s "checking for stdint u_int32_t... " >&6; } if test ${ac_cv_header_stdint_u+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 printf "%s\n" "(..)" >&6; } for i in sys/types.h inttypes.h sys/inttypes.h $inttype_headers ; do unset ac_cv_type_u_int32_t unset ac_cv_type_u_int64_t ac_fn_c_check_type "$LINENO" "u_int32_t" "ac_cv_type_u_int32_t" "#include <$i> " if test "x$ac_cv_type_u_int32_t" = xyes then : ac_cv_header_stdint_u=$i else $as_nop continue fi ac_fn_c_check_type "$LINENO" "u_int64_t" "ac_cv_type_u_int64_t" "#include<$i> " if test "x$ac_cv_type_u_int64_t" = xyes then : and64="/u_int64_t" else $as_nop and64="" fi ac_cv_stdint_result="(seen u_int32_t$and64 in $i)" break break; done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint u_int32_t" >&5 printf %s "checking for stdint u_int32_t... " >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint_u" >&5 printf "%s\n" "$ac_cv_header_stdint_u" >&6; } fi fi if test "_$ac_cv_header_stdint_x" = "_" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdint datatype model" >&5 printf %s "checking for stdint datatype model... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 printf "%s\n" "(..)" >&6; } # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of char" >&5 printf %s "checking size of char... " >&6; } if test ${ac_cv_sizeof_char+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (char))" "ac_cv_sizeof_char" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_char" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (char) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_char=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_char" >&5 printf "%s\n" "$ac_cv_sizeof_char" >&6; } printf "%s\n" "#define SIZEOF_CHAR $ac_cv_sizeof_char" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of short" >&5 printf %s "checking size of short... " >&6; } if test ${ac_cv_sizeof_short+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_short" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (short) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_short=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5 printf "%s\n" "$ac_cv_sizeof_short" >&6; } printf "%s\n" "#define SIZEOF_SHORT $ac_cv_sizeof_short" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 printf %s "checking size of int... " >&6; } if test ${ac_cv_sizeof_int+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_int" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_int=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 printf "%s\n" "$ac_cv_sizeof_int" >&6; } printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_int" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 printf %s "checking size of long... " >&6; } if test ${ac_cv_sizeof_long+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_long" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 printf "%s\n" "$ac_cv_sizeof_long" >&6; } printf "%s\n" "#define SIZEOF_LONG $ac_cv_sizeof_long" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of void*" >&5 printf %s "checking size of void*... " >&6; } if test ${ac_cv_sizeof_voidp+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void*))" "ac_cv_sizeof_voidp" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_voidp" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (void*) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_voidp=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_voidp" >&5 printf "%s\n" "$ac_cv_sizeof_voidp" >&6; } printf "%s\n" "#define SIZEOF_VOIDP $ac_cv_sizeof_voidp" >>confdefs.h ac_cv_char_data_model="" ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" ac_cv_long_data_model="" ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking data model" >&5 printf %s "checking data model... " >&6; } case "$ac_cv_char_data_model/$ac_cv_long_data_model" in 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; 222/*|333/*|444/*|666/*|888/*) : ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; *) ac_cv_data_model="none" ; n="very unusual model" ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_data_model ($ac_cv_long_data_model, $n)" >&5 printf "%s\n" "$ac_cv_data_model ($ac_cv_long_data_model, $n)" >&6; } fi if test "_$ac_cv_header_stdint_x" != "_" ; then ac_cv_header_stdint="$ac_cv_header_stdint_x" elif test "_$ac_cv_header_stdint_o" != "_" ; then ac_cv_header_stdint="$ac_cv_header_stdint_o" elif test "_$ac_cv_header_stdint_u" != "_" ; then ac_cv_header_stdint="$ac_cv_header_stdint_u" else ac_cv_header_stdint="stddef.h" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for extra inttypes in chosen header" >&5 printf %s "checking for extra inttypes in chosen header... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ($ac_cv_header_stdint)" >&5 printf "%s\n" "($ac_cv_header_stdint)" >&6; } unset ac_cv_type_int_least32_t unset ac_cv_type_int_fast32_t ac_fn_c_check_type "$LINENO" "int_least32_t" "ac_cv_type_int_least32_t" "#include <$ac_cv_header_stdint> " if test "x$ac_cv_type_int_least32_t" = xyes then : fi ac_fn_c_check_type "$LINENO" "int_fast32_t" "ac_cv_type_int_fast32_t" "#include<$ac_cv_header_stdint> " if test "x$ac_cv_type_int_fast32_t" = xyes then : fi ac_fn_c_check_type "$LINENO" "intmax_t" "ac_cv_type_intmax_t" "#include <$ac_cv_header_stdint> " if test "x$ac_cv_type_intmax_t" = xyes then : fi fi # shortcircuit to system "stdint.h" # ------------------ PREPARE VARIABLES ------------------------------ if test "$GCC" = "yes" ; then ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` else ac_cv_stdint_message="using $CC" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: make use of $ac_cv_header_stdint in $ac_stdint_h $ac_cv_stdint_result" >&5 printf "%s\n" "make use of $ac_cv_header_stdint in $ac_stdint_h $ac_cv_stdint_result" >&6; } # ----------------- DONE inttypes.h checks START header ------------- ac_config_commands="$ac_config_commands $ac_stdint_h" # Checks for functions and their arguments. ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" if test "x$ac_cv_func_clock_gettime" = xyes then : printf "%s\n" "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "dprintf" "ac_cv_func_dprintf" if test "x$ac_cv_func_dprintf" = xyes then : printf "%s\n" "#define HAVE_DPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes then : printf "%s\n" "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memset_explicit" "ac_cv_func_memset_explicit" if test "x$ac_cv_func_memset_explicit" = xyes then : printf "%s\n" "#define HAVE_MEMSET_EXPLICIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "explicit_memset" "ac_cv_func_explicit_memset" if test "x$ac_cv_func_explicit_memset" = xyes then : printf "%s\n" "#define HAVE_EXPLICIT_MEMSET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrandom" "ac_cv_func_getrandom" if test "x$ac_cv_func_getrandom" = xyes then : printf "%s\n" "#define HAVE_GETRANDOM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_aton" "ac_cv_func_inet_aton" 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" "memset_s" "ac_cv_func_memset_s" if test "x$ac_cv_func_memset_s" = xyes then : printf "%s\n" "#define HAVE_MEMSET_S 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" if test "x$ac_cv_func_snprintf" = xyes then : printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes then : printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf" if test "x$ac_cv_func_vsnprintf" = xyes then : printf "%s\n" "#define HAVE_VSNPRINTF 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5 printf %s "checking types of arguments for select... " >&6; } if test ${ac_cv_func_select_args+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_func_select_args='int,int *,struct timeval *' for ac_arg234 in 'fd_set *' 'int *' 'void *'; do for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif int main (void) { extern int select ($ac_arg1, $ac_arg234, $ac_arg234, $ac_arg234, $ac_arg5); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done done done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5 printf "%s\n" "$ac_cv_func_select_args" >&6; } ac_save_IFS=$IFS; IFS=',' set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` IFS=$ac_save_IFS shift printf "%s\n" "#define SELECT_TYPE_ARG1 $1" >>confdefs.h printf "%s\n" "#define SELECT_TYPE_ARG234 ($2)" >>confdefs.h printf "%s\n" "#define SELECT_TYPE_ARG5 ($3)" >>confdefs.h rm -rf conftest* # https://raw.githubusercontent.com/tmux/tmux/2dd9a4fb9cd73987bdca5b8b2f85ca8b1a6e4e73/configure.ac # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for b64_ntop" >&5 printf %s "checking for b64_ntop... " >&6; } if test "$cross_compiling" = yes then : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { b64_ntop(NULL, 0, NULL, 0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : found_b64_ntop=yes else $as_nop found_b64_ntop=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test "x$found_b64_ntop" = xno; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for b64_ntop with -lresolv" >&5 printf %s "checking for b64_ntop with -lresolv... " >&6; } OLD_LIBS="$LIBS" LIBS="$LIBS -lresolv" if test "$cross_compiling" = yes then : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { b64_ntop(NULL, 0, NULL, 0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : found_b64_ntop=yes else $as_nop found_b64_ntop=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test "x$found_b64_ntop" = xno; then LIBS="$OLD_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for b64_ntop with -lnetwork" >&5 printf %s "checking for b64_ntop with -lnetwork... " >&6; } OLD_LIBS="$LIBS" LIBS="-lnetwork" if test "$cross_compiling" = yes then : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { b64_ntop(NULL, 0, NULL, 0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : found_b64_ntop=yes else $as_nop found_b64_ntop=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test "x$found_b64_ntop" = xno; then LIBS="$OLD_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test "x$found_b64_ntop" = xyes; then printf "%s\n" "#define HAVE_BASE64 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else case " $LIBOBJS " in *" base64.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS base64.$ac_objext" ;; esac fi ac_func= for ac_item in $ac_func_c_list do if test $ac_func; then ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then echo "#define $ac_item 1" >> confdefs.h fi ac_func= else ac_func=$ac_item fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working mmap" >&5 printf %s "checking for working mmap... " >&6; } if test ${ac_cv_func_mmap_fixed_mapped+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : case "$host_os" in # (( # Guess yes on platforms where we know the result. linux*) ac_cv_func_mmap_fixed_mapped=yes ;; # If we don't know, assume the worst. *) ac_cv_func_mmap_fixed_mapped=no ;; esac else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default /* malloc might have been renamed as rpl_malloc. */ #undef malloc /* Thanks to Mike Haertel and Jim Avera for this test. Here is a matrix of mmap possibilities: mmap private not fixed mmap private fixed at somewhere currently unmapped mmap private fixed at somewhere already mapped mmap shared not fixed mmap shared fixed at somewhere currently unmapped mmap shared fixed at somewhere already mapped For private mappings, we should verify that changes cannot be read() back from the file, nor mmap's back from the file at a different address. (There have been systems where private was not correctly implemented like the infamous i386 svr4.0, and systems where the VM page cache was not coherent with the file system buffer cache like early versions of FreeBSD and possibly contemporary NetBSD.) For shared mappings, we should conversely verify that changes get propagated back to all the places they're supposed to be. Grep wants private fixed already mapped. The main things grep needs to know about mmap are: * does it exist and is it safe to write into the mmap'd area * how to use it (BSD variants) */ #include #include /* This mess was copied from the GNU getpagesize.h. */ #ifndef HAVE_GETPAGESIZE # ifdef _SC_PAGESIZE # define getpagesize() sysconf(_SC_PAGESIZE) # else /* no _SC_PAGESIZE */ # ifdef HAVE_SYS_PARAM_H # include # ifdef EXEC_PAGESIZE # define getpagesize() EXEC_PAGESIZE # else /* no EXEC_PAGESIZE */ # ifdef NBPG # define getpagesize() NBPG * CLSIZE # ifndef CLSIZE # define CLSIZE 1 # endif /* no CLSIZE */ # else /* no NBPG */ # ifdef NBPC # define getpagesize() NBPC # else /* no NBPC */ # ifdef PAGESIZE # define getpagesize() PAGESIZE # endif /* PAGESIZE */ # endif /* no NBPC */ # endif /* no NBPG */ # endif /* no EXEC_PAGESIZE */ # else /* no HAVE_SYS_PARAM_H */ # define getpagesize() 8192 /* punt totally */ # endif /* no HAVE_SYS_PARAM_H */ # endif /* no _SC_PAGESIZE */ #endif /* no HAVE_GETPAGESIZE */ int main (void) { char *data, *data2, *data3; const char *cdata2; int i, pagesize; int fd, fd2; pagesize = getpagesize (); /* First, make a file with some known garbage in it. */ data = (char *) malloc (pagesize); if (!data) return 1; for (i = 0; i < pagesize; ++i) *(data + i) = rand (); umask (0); fd = creat ("conftest.mmap", 0600); if (fd < 0) return 2; if (write (fd, data, pagesize) != pagesize) return 3; close (fd); /* Next, check that the tail of a page is zero-filled. File must have non-zero length, otherwise we risk SIGBUS for entire page. */ fd2 = open ("conftest.txt", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd2 < 0) return 4; cdata2 = ""; if (write (fd2, cdata2, 1) != 1) return 5; data2 = (char *) mmap (0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0L); if (data2 == MAP_FAILED) return 6; for (i = 0; i < pagesize; ++i) if (*(data2 + i)) return 7; close (fd2); if (munmap (data2, pagesize)) return 8; /* Next, try to mmap the file at a fixed address which already has something else allocated at it. If we can, also make sure that we see the same garbage. */ fd = open ("conftest.mmap", O_RDWR); if (fd < 0) return 9; if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0L)) return 10; for (i = 0; i < pagesize; ++i) if (*(data + i) != *(data2 + i)) return 11; /* Finally, make sure that changes to the mapped area do not percolate back to the file as seen by read(). (This is a bug on some variants of i386 svr4.0.) */ for (i = 0; i < pagesize; ++i) *(data2 + i) = *(data2 + i) + 1; data3 = (char *) malloc (pagesize); if (!data3) return 12; if (read (fd, data3, pagesize) != pagesize) return 13; for (i = 0; i < pagesize; ++i) if (*(data + i) != *(data3 + i)) return 14; close (fd); free (data); free (data3); return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_mmap_fixed_mapped=yes else $as_nop ac_cv_func_mmap_fixed_mapped=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_mmap_fixed_mapped" >&5 printf "%s\n" "$ac_cv_func_mmap_fixed_mapped" >&6; } if test $ac_cv_func_mmap_fixed_mapped = yes; then printf "%s\n" "#define HAVE_MMAP 1" >>confdefs.h fi rm -f conftest.mmap conftest.txt # Tcl version to recommend if no Tcl is found, and the site where it can be # found for download. tclrecommendver="8.6.X" tclrecommendsite="https://sourceforge.net/projects/tcl/files/Tcl/" # Tcl header filenames. tclheadernames="tcl.h" # We save the cache (if used) here to speed things up if we can't find Tcl. cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache # Misc Tcl checks. # Check whether --with-tcllib was given. if test ${with_tcllib+y} then : withval=$with_tcllib; tcllibname="$withval" fi # Check whether --with-tclinc was given. if test ${with_tclinc+y} then : withval=$with_tclinc; tclincname="$withval" fi WARN=0 # Make sure either both or neither $tcllibname and $tclincname are set if test "x$tcllibname" != x; then if test "x$tclincname" = x; then WARN=1 tcllibname="" TCLLIB="" TCLINC="" fi else if test "x$tclincname" != x; then WARN=1 tclincname="" TCLLIB="" TCLINC="" fi fi if test "$WARN" = 1; then cat << 'EOF' >&2 configure: WARNING: You must specify both --with-tcllib and --with-tclinc for either to work. configure will now attempt to autodetect both the Tcl library and header. EOF fi # Look for Tcl library: if $tcllibname is set, check there first if test "x$tcllibname" != x; then if test -f "$tcllibname" && test -r "$tcllibname"; then TCLLIB=`echo $tcllibname | sed 's%/[^/][^/]*$%%'` TCLLIBFN=`$BASENAME $tcllibname | cut -c4-` TCLLIBEXT=`echo $TCLLIBFN | $AWK '{j=split($1, i, "."); suffix=""; while (i[j] ~ /^[0-9]+$/) { suffix = "." i[j--] suffix; }; print "." i[j] suffix }'` TCLLIBFNS=`$BASENAME $tcllibname $TCLLIBEXT | cut -c4-` # Set default make as static for unshared Tcl library if test TCLLIBEXT = ".a"; then if test "$DEFAULT_MAKE" != static; then cat << 'EOF' >&2 configure: WARNING: Your Tcl library is not a shared lib. configure will now set default make type to static. EOF DEFAULT_MAKE="static" fi fi else cat << EOF >&2 configure: WARNING: The file '$tcllibname' given to option --with-tcllib is not valid. Specify the full path including the file name (e.g. /usr/lib/libtcl8.6.so) configure will now attempt to autodetect both the Tcl library and header. EOF tcllibname="" tclincname="" TCLLIB="" TCLLIBFN="" TCLINC="" TCLINCFN="" fi fi # Look for Tcl header: if $tclincname is set, check there first if test "x$tclincname" != x; then if test -f "$tclincname" && test -r "$tclincname"; then TCLINC=`echo $tclincname | sed 's%/[^/][^/]*$%%'` TCLINCFN=`$BASENAME $tclincname` else cat << EOF >&2 configure: WARNING: The file '$tclincname' given to option --with-tclinc is not valid. Specify the full path including the file name (e.g. /usr/include/tcl.h) configure will now attempt to autodetect both the Tcl library and header. EOF tcllibname="" tclincname="" TCLLIB="" TCLLIBFN="" TCLINC="" TCLINCFN="" fi fi if test "x$TCLLIBFN" = x; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Autoconfiguring Tcl with tclConfig.sh" >&5 printf "%s\n" "$as_me: Autoconfiguring Tcl with tclConfig.sh" >&6;} egg_tcl_changed="yes" # TEA extensions pass this us the version of TEA they think they # are compatible with. TEA_VERSION="3.10" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for correct TEA configuration" >&5 printf %s "checking for correct TEA configuration... " >&6; } if test x"${PACKAGE_NAME}" = x ; then as_fn_error $? " The PACKAGE_NAME variable must be defined by your TEA configure.ac" "$LINENO" 5 fi if test x""3.10"" = x ; then as_fn_error $? " TEA version not specified." "$LINENO" 5 elif test ""3.10"" != "${TEA_VERSION}" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: warning: requested TEA version \"\"3.10\"\", have \"${TEA_VERSION}\"" >&5 printf "%s\n" "warning: requested TEA version \"\"3.10\"\", have \"${TEA_VERSION}\"" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ok (TEA ${TEA_VERSION})" >&5 printf "%s\n" "ok (TEA ${TEA_VERSION})" >&6; } fi # If the user did not set CFLAGS, set it now to keep macros # like AC_PROG_CC and AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[],[]) from adding "-g -O2". if test "${CFLAGS+set}" != "set" ; then CFLAGS="" fi TEA_TK_EXTENSION=0 case "`uname -s`" in *win32*|*WIN32*|*MINGW32_*) # Extract the first word of "cygpath", so it can be a program name with args. set dummy cygpath; 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_prog_CYGPATH+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CYGPATH"; then ac_cv_prog_CYGPATH="$CYGPATH" # Let the user override the test. else 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_prog_CYGPATH="cygpath -m" 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 test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo" fi fi CYGPATH=$ac_cv_prog_CYGPATH if test -n "$CYGPATH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CYGPATH" >&5 printf "%s\n" "$CYGPATH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi EXEEXT=".exe" TEA_PLATFORM="windows" ;; *CYGWIN_*) EXEEXT=".exe" # CYGPATH and TEA_PLATFORM are determined later in LOAD_TCLCONFIG ;; *) CYGPATH=echo # Maybe we are cross-compiling.... case ${host_alias} in *mingw32*) EXEEXT=".exe" TEA_PLATFORM="windows" ;; *) EXEEXT="" TEA_PLATFORM="unix" ;; esac ;; esac # Check if exec_prefix is set. If not use fall back to prefix. # Note when adjusted, so that TEA_PREFIX can correct for this. # This is needed for recursive configures, since autoconf propagates # $prefix, but not $exec_prefix (doh!). if test x$exec_prefix = xNONE ; then exec_prefix_default=yes exec_prefix=$prefix fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&5 printf "%s\n" "$as_me: configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}" >&6;} # This package name must be replaced statically for AC_SUBST to work # Substitute STUB_LIB_FILE in case package creates a stub library too. # We AC_SUBST these here to ensure they are subst'ed, # in case the user doesn't call TEA_ADD_... # # Ok, lets find the tcl configuration # First, look for one uninstalled. # the alternative search directory is invoked by --with-tcl # if test x"${no_tcl}" = x ; then # we reset no_tcl in case something fails here no_tcl=true # Check whether --with-tcl was given. if test ${with_tcl+y} then : withval=$with_tcl; with_tclconfig="${withval}" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Tcl configuration" >&5 printf %s "checking for Tcl configuration... " >&6; } if test ${ac_cv_c_tclconfig+y} then : printf %s "(cached) " >&6 else $as_nop # First check to see if --with-tcl was specified. if test x"${with_tclconfig}" != x ; then case "${with_tclconfig}" in */tclConfig.sh ) if test -f "${with_tclconfig}"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&5 printf "%s\n" "$as_me: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&2;} with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" fi ;; esac if test -f "${with_tclconfig}/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" else as_fn_error $? "${with_tclconfig} directory doesn't contain tclConfig.sh" "$LINENO" 5 fi fi # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ../tcl \ `ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \ ../../tcl \ `ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \ ../../../tcl \ `ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi # on Darwin, check in Framework installation locations if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ~/Library/Frameworks 2>/dev/null` \ `ls -d /Library/Frameworks 2>/dev/null` \ `ls -d /Network/Library/Frameworks 2>/dev/null` \ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks/Tcl.framework 2>/dev/null` \ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Network/Library/Frameworks/Tcl.framework 2>/dev/null` \ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tcl.framework 2>/dev/null` \ `ls -d /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks 2>/dev/null` \ ; do if test -f "$i/Tcl.framework/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`" break fi done fi # TEA specific: on Windows, check in common installation locations if test "${TEA_PLATFORM}" = "windows" \ -a x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d C:/Tcl/lib 2>/dev/null` \ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few common install locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in `ls -d ${libdir} 2>/dev/null` \ `ls -d ${exec_prefix}/lib 2>/dev/null` \ `ls -d ${prefix}/lib 2>/dev/null` \ `ls -d /usr/local/lib 2>/dev/null` \ `ls -d /usr/contrib/lib 2>/dev/null` \ `ls -d /usr/pkg/lib 2>/dev/null` \ `ls -d /usr/lib 2>/dev/null` \ `ls -d /usr/lib64 2>/dev/null` \ `ls -d /usr/lib/tcl[8-9].[0-9] 2>/dev/null` \ `ls -d /usr/local/lib/tcl[8-9].[0-9] 2>/dev/null` \ `ls -d /usr/local/lib/tcl/tcl[8-9].[0-9] 2>/dev/null` \ ; do if test -f "$i/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i; pwd)`" break fi done fi # check in a few other private locations if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ ${srcdir}/../tcl \ `ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \ `ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null` ; do if test "${TEA_PLATFORM}" = "windows" \ -a -f "$i/win/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/win; pwd)`" break fi if test -f "$i/unix/tclConfig.sh" ; then ac_cv_c_tclconfig="`(cd $i/unix; pwd)`" break fi done fi fi if test x"${ac_cv_c_tclconfig}" = x ; then TCL_BIN_DIR="# no Tcl configs found" as_fn_error $? "Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh" "$LINENO" 5 else no_tcl= TCL_BIN_DIR="${ac_cv_c_tclconfig}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found ${TCL_BIN_DIR}/tclConfig.sh" >&5 printf "%s\n" "found ${TCL_BIN_DIR}/tclConfig.sh" >&6; } fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for existence of ${TCL_BIN_DIR}/tclConfig.sh" >&5 printf %s "checking for existence of ${TCL_BIN_DIR}/tclConfig.sh... " >&6; } if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: loading" >&5 printf "%s\n" "loading" >&6; } . "${TCL_BIN_DIR}/tclConfig.sh" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: could not find ${TCL_BIN_DIR}/tclConfig.sh" >&5 printf "%s\n" "could not find ${TCL_BIN_DIR}/tclConfig.sh" >&6; } fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" # If the TCL_BIN_DIR is the build directory (not the install directory), # then set the common variable name to the value of the build variables. # For example, the variable TCL_LIB_SPEC will be set to the value # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC # instead of TCL_BUILD_LIB_SPEC since it will work with both an # installed and uninstalled version of Tcl. if test -f "${TCL_BIN_DIR}/Makefile" ; then TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}" TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}" TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}" elif test "`uname -s`" = "Darwin"; then # If Tcl was built as a framework, attempt to use the libraries # from the framework at the given location so that linking works # against Tcl.framework installed in an arbitrary location. case ${TCL_DEFS} in *TCL_FRAMEWORK*) if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then for i in "`cd "${TCL_BIN_DIR}"; pwd`" \ "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}" break fi done fi if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}" TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}" fi ;; esac fi # eval is required to do the TCL_DBGX substitution eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking platform" >&5 printf %s "checking platform... " >&6; } hold_cc=$CC; CC="$TCL_CC" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifdef _WIN32 #error win32 #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : TEA_PLATFORM="unix" CYGPATH=echo else $as_nop TEA_PLATFORM="windows" # Extract the first word of "cygpath", so it can be a program name with args. set dummy cygpath; 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_prog_CYGPATH+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CYGPATH"; then ac_cv_prog_CYGPATH="$CYGPATH" # Let the user override the test. else 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_prog_CYGPATH="cygpath -m" 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 test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo" fi fi CYGPATH=$ac_cv_prog_CYGPATH if test -n "$CYGPATH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CYGPATH" >&5 printf "%s\n" "$CYGPATH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CC=$hold_cc { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TEA_PLATFORM" >&5 printf "%s\n" "$TEA_PLATFORM" >&6; } # The BUILD_$pkg is to define the correct extern storage class # handling when making this package printf "%s\n" "#define BUILD_${PACKAGE_NAME} /**/" >>confdefs.h # Do this here as we have fully defined TEA_PLATFORM now if test "${TEA_PLATFORM}" = "windows" ; then EXEEXT=".exe" CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp" fi # TEA specific: #-------------------------------------------------------------------- # On a few very rare systems, all of the libm.a stuff is # already in libc.a. Set compiler flags accordingly. # Also, Linux requires the "ieee" library for math to work # right (and it must appear before "-lm"). #-------------------------------------------------------------------- ac_fn_c_check_func "$LINENO" "sin" "ac_cv_func_sin" if test "x$ac_cv_func_sin" = xyes then : MATH_LIBS="" else $as_nop MATH_LIBS="-lm" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for main in -lieee" >&5 printf %s "checking for main in -lieee... " >&6; } if test ${ac_cv_lib_ieee_main+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lieee $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ieee_main=yes else $as_nop ac_cv_lib_ieee_main=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ieee_main" >&5 printf "%s\n" "$ac_cv_lib_ieee_main" >&6; } if test "x$ac_cv_lib_ieee_main" = xyes then : MATH_LIBS="-lieee $MATH_LIBS" fi #-------------------------------------------------------------------- # Interactive UNIX requires -linet instead of -lsocket, plus it # needs net/errno.h to define the socket-related error codes. #-------------------------------------------------------------------- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for main in -linet" >&5 printf %s "checking for main in -linet... " >&6; } if test ${ac_cv_lib_inet_main+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-linet $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { return main (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_inet_main=yes else $as_nop ac_cv_lib_inet_main=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_main" >&5 printf "%s\n" "$ac_cv_lib_inet_main" >&6; } if test "x$ac_cv_lib_inet_main" = xyes then : LIBS="$LIBS -linet" fi ac_fn_c_check_header_compile "$LINENO" "net/errno.h" "ac_cv_header_net_errno_h" "$ac_includes_default" if test "x$ac_cv_header_net_errno_h" = xyes then : printf "%s\n" "#define HAVE_NET_ERRNO_H 1" >>confdefs.h fi #-------------------------------------------------------------------- # Check for the existence of the -lsocket and -lnsl libraries. # The order here is important, so that they end up in the right # order in the command line generated by make. Here are some # special considerations: # 1. Use "connect" and "accept" to check for -lsocket, and # "gethostbyname" to check for -lnsl. # 2. Use each function name only once: can't redo a check because # autoconf caches the results of the last check and won't redo it. # 3. Use -lnsl and -lsocket only if they supply procedures that # aren't already present in the normal libraries. This is because # IRIX 5.2 has libraries, but they aren't needed and they're # bogus: they goof up name resolution if used. # 4. On some SVR4 systems, can't use -lsocket without -lnsl too. # To get around this problem, check for both libraries together # if -lsocket doesn't work by itself. #-------------------------------------------------------------------- tcl_checkBoth=0 ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect" if test "x$ac_cv_func_connect" = xyes then : tcl_checkSocket=0 else $as_nop tcl_checkSocket=1 fi if test "$tcl_checkSocket" = 1; then ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt" if test "x$ac_cv_func_setsockopt" = xyes then : else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5 printf %s "checking for setsockopt in -lsocket... " >&6; } if test ${ac_cv_lib_socket_setsockopt+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char setsockopt (); int main (void) { return setsockopt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_setsockopt=yes else $as_nop ac_cv_lib_socket_setsockopt=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5 printf "%s\n" "$ac_cv_lib_socket_setsockopt" >&6; } if test "x$ac_cv_lib_socket_setsockopt" = xyes then : LIBS="$LIBS -lsocket" else $as_nop tcl_checkBoth=1 fi fi fi if test "$tcl_checkBoth" = 1; then tk_oldLibs=$LIBS LIBS="$LIBS -lsocket -lnsl" ac_fn_c_check_func "$LINENO" "accept" "ac_cv_func_accept" if test "x$ac_cv_func_accept" = xyes then : tcl_checkNsl=0 else $as_nop LIBS=$tk_oldLibs fi fi ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname" if test "x$ac_cv_func_gethostbyname" = xyes then : else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5 printf %s "checking for gethostbyname in -lnsl... " >&6; } if test ${ac_cv_lib_nsl_gethostbyname+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gethostbyname (); int main (void) { return gethostbyname (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_nsl_gethostbyname=yes else $as_nop ac_cv_lib_nsl_gethostbyname=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5 printf "%s\n" "$ac_cv_lib_nsl_gethostbyname" >&6; } if test "x$ac_cv_lib_nsl_gethostbyname" = xyes then : LIBS="$LIBS -lnsl" fi fi # TEA specific: Don't perform the eval of the libraries here because # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}' # Overwrite TCL_LIBS again, which TCL_LOAD_TCLCONFIG unfortunately overwrites from tclConfig.sh # Also, use the Tcl linker idea to be compatible with their ldflags if test -r ${TCL_BIN_DIR}/tclConfig.sh; then . ${TCL_BIN_DIR}/tclConfig.sh SHLIB_LD=$TCL_SHLIB_LD { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Tcl linker" >&5 printf %s "checking for Tcl linker... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SHLIB_LD" >&5 printf "%s\n" "$SHLIB_LD" >&6; } else TCL_LIBS="${EGG_MATH_LIB}" fi TCL_PATCHLEVEL="${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_PATCH_LEVEL}" TCL_LIB_SPEC="${TCL_LIB_SPEC} ${TCL_LIBS}" else egg_tcl_changed="yes" if test -r ${TCLLIB}/tclConfig.sh; then . ${TCLLIB}/tclConfig.sh SHLIB_LD=$TCL_SHLIB_LD { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Tcl linker" >&5 printf %s "checking for Tcl linker... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SHLIB_LD" >&5 printf "%s\n" "$SHLIB_LD" >&6; } TCL_PATCHLEVEL="${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_PATCH_LEVEL}" TCL_LIB_SPEC="${TCL_LIB_SPEC} ${TCL_LIBS}" else TCL_LIB_SPEC="-L$TCLLIB -l$TCLLIBFNS ${EGG_MATH_LIB}" TCL_INCLUDE_SPEC="" TCL_VERSION=`grep TCL_VERSION $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/"/, "", $3); print $3}'` TCL_PATCHLEVEL=`grep TCL_PATCH_LEVEL $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/"/, "", $3); print $3}'` TCL_MAJOR_VERSION=`echo $TCL_VERSION | cut -d. -f1` TCL_MINOR_VERSION=`echo $TCL_VERSION | cut -d. -f2` if test $TCL_MAJOR_VERSION -gt 8 || test $TCL_MAJOR_VERSION -eq 8 -a $TCL_MINOR_VERSION -ge 6; then TCL_LIB_SPEC="$TCL_LIB_SPEC -lz" fi fi fi if test -z "$ac_cv_lib_dlopen"; then TCL_LIB_SPEC=$(echo $TCL_LIB_SPEC | sed -- 's/-ldl//g') fi if test "$SUNOS_GCC" = yes; then SHLIB_LD=$(echo $SHLIB_LD | sed -- 's/-z text//') { printf "%s\n" "$as_me:${as_lineno-$LINENO}: SunOS found, SHLIB_LD = $SHLIB_LD" >&5 printf "%s\n" "$as_me: SunOS found, SHLIB_LD = $SHLIB_LD" >&6;} fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Tcl version" >&5 printf %s "checking for Tcl version... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TCL_PATCHLEVEL" >&5 printf "%s\n" "$TCL_PATCHLEVEL" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Tcl library flags" >&5 printf %s "checking for Tcl library flags... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TCL_LIB_SPEC" >&5 printf "%s\n" "$TCL_LIB_SPEC" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Tcl header flags" >&5 printf %s "checking for Tcl header flags... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TCL_INCLUDE_SPEC" >&5 printf "%s\n" "$TCL_INCLUDE_SPEC" >&6; } if test "x$TCL_MAJOR_VERSION" = x || test "x$TCL_MINOR_VERSION" = x || test $TCL_MAJOR_VERSION -lt 8 || test $TCL_MAJOR_VERSION -eq 8 -a $TCL_MINOR_VERSION -lt 5; then cat << EOF >&2 configure: error: Your Tcl version is much too old for Eggdrop to use. You should download and compile a more recent version. The most reliable current version is $tclrecommendver and can be downloaded from ${tclrecommendsite}. We require at least Tcl 8.5. See doc/COMPILE-GUIDE's 'Tcl Detection and Installation' section for more information. EOF exit 1 fi if test "x$TCLINCFN" != x; then egg_tclinc="\\\"$TCLINC/$TCLINCFN\\\"" else egg_tclinc="" fi ac_config_commands="$ac_config_commands replace-if-changed" # Debug options # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; enable_debug="$enableval" else $as_nop enable_debug="auto" fi # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; enable_debug="$enableval" else $as_nop enable_debug="auto" fi # Check whether --enable-debug-assert was given. if test ${enable_debug_assert+y} then : enableval=$enable_debug_assert; enable_debug_assert="$enableval" else $as_nop enable_debug_assert="auto" fi # Check whether --enable-debug-assert was given. if test ${enable_debug_assert+y} then : enableval=$enable_debug_assert; enable_debug_assert="$enableval" else $as_nop enable_debug_assert="auto" fi # Check whether --enable-debug-mem was given. if test ${enable_debug_mem+y} then : enableval=$enable_debug_mem; enable_debug_mem="$enableval" else $as_nop enable_debug_mem="auto" fi # Check whether --enable-debug-mem was given. if test ${enable_debug_mem+y} then : enableval=$enable_debug_mem; enable_debug_mem="$enableval" else $as_nop enable_debug_mem="auto" fi # Check whether --enable-debug-dns was given. if test ${enable_debug_dns+y} then : enableval=$enable_debug_dns; enable_debug_dns="$enableval" else $as_nop enable_debug_dns="auto" fi # Check whether --enable-debug-dns was given. if test ${enable_debug_dns+y} then : enableval=$enable_debug_dns; enable_debug_dns="$enableval" else $as_nop enable_debug_dns="auto" fi # Check whether --enable-debug-context was given. if test ${enable_debug_context+y} then : enableval=$enable_debug_context; enable_debug_context="$enableval" else $as_nop enable_debug_context="auto" fi # Check whether --enable-debug-context was given. if test ${enable_debug_context+y} then : enableval=$enable_debug_context; enable_debug_context="$enableval" else $as_nop enable_debug_context="auto" fi # Defaults: # make: 'eggdrop' or 'static' default_std_debug="no" default_std_debug_assert="no" default_std_debug_mem="no" default_std_debug_context="yes" default_std_debug_dns="no" # make: 'debug' or 'sdebug' default_deb_debug="yes" default_deb_debug_assert="yes" default_deb_debug_mem="yes" default_deb_debug_context="yes" default_deb_debug_dns="yes" if test "$DEFAULT_MAKE" = eggdrop || test "$DEFAULT_MAKE" = static; then default_debug="$default_std_debug" default_debug_assert="$default_std_debug_assert" default_debug_mem="$default_std_debug_mem" default_debug_context="$default_std_debug_context" default_debug_dns="$default_std_debug_dns" else default_debug="$default_deb_debug" default_debug_assert="$default_deb_debug_assert" default_debug_mem="$default_deb_debug_mem" default_debug_context="$default_deb_debug_context" default_debug_dns="$default_deb_debug_dns" fi debug_options="debug debug_assert debug_mem debug_dns" debug_cflags_debug="-g3 -DDEBUG" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler accepts -Og" >&5 printf %s "checking whether the C compiler accepts -Og... " >&6; } if test ${ax_cv_check_cflags___Og+y} then : printf %s "(cached) " >&6 else $as_nop ax_check_save_flags=$CFLAGS if test x"$GCC" = xyes ; then add_gnu_werror="-Werror" fi CFLAGS="$CFLAGS -Og $add_gnu_werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ax_cv_check_cflags___Og=yes else $as_nop ax_cv_check_cflags___Og=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Og" >&5 printf "%s\n" "$ax_cv_check_cflags___Og" >&6; } if test "x$ax_cv_check_cflags___Og" = xyes then : debug_cflags_debug="-Og $debug_cflags_debug" else $as_nop : fi debug_cflags_debug_assert="-DDEBUG_ASSERT" debug_cflags_debug_mem="-DDEBUG_MEM" debug_cflags_debug_dns="-DDEBUG_DNS" debug_stdcflags_debug="" debug_stdcflags_debug_assert="" debug_stdcflags_debug_mem="" debug_stdcflags_debug_dns="" debug_debcflags_debug="" debug_debcflags_debug_assert="" debug_debcflags_debug_mem="" debug_debcflags_debug_dns="" for enable_option in $debug_options; do eval enable_value=\$enable_$enable_option # Check if either --enable- or --disable- was used if test "$enable_value" != auto; then # Make sure an invalid option wasn't passed as --enable-=foo if test "$enable_value" != yes && test "$enable_value" != no; then opt_name=`echo $enable_option | sed 's/_/-/g'` eval opt_default=\$default_$enable_option { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Invalid option '$enable_value' passed to --enable-${opt_name}, defaulting to '$opt_default'" >&5 printf "%s\n" "$as_me: WARNING: Invalid option '$enable_value' passed to --enable-${opt_name}, defaulting to '$opt_default'" >&2;} eval enable_$enable_option="auto" fi fi if test "$enable_value" = auto; then # Note: options generally should not end up in both std and deb but # there may be options in the future where this behavior is desired. if test `eval echo '${'default_std_$enable_option'}'` = yes; then eval `echo debug_stdcflags_$enable_option`=\$debug_cflags_$enable_option fi if test `eval echo '${'default_deb_$enable_option'}'` = yes; then eval `echo debug_debcflags_$enable_option`=\$debug_cflags_$enable_option fi else if test "$enable_value" = yes; then # If option defaults to 'yes' for debug, always put it in stdcflags # when the option is forced on because someone may want it enabled # for a non-debug build. if test `eval echo '${'default_deb_$enable_option'}'` = yes; then eval `echo debug_stdcflags_$enable_option`=\$debug_cflags_$enable_option else # option defaulted to 'no' so put it in debcflags eval `echo debug_debcflags_$enable_option`=\$debug_cflags_$enable_option fi fi fi done for cflg_option in $debug_options; do eval stdcflg_value=\$debug_stdcflags_$cflg_option if test "x$stdcflg_value" != x; then if test "x$CFLGS" = x; then CFLGS="$stdcflg_value" else CFLGS="$CFLGS $stdcflg_value" fi fi eval debcflg_value=\$debug_debcflags_$cflg_option if test "x$debcflg_value" != x; then if test "x$DEBCFLGS" = x; then DEBCFLGS="$debcflg_value" else DEBCFLGS="$DEBCFLGS $debcflg_value" fi fi done # Disable debug symbol stripping if compiled with --enable-debug # This will result in core dumps that are actually useful. if test "x$debug_stdcflags_debug" != x; then STRIP="touch" MOD_STRIP="touch" SHLIB_STRIP="touch" fi # Check if either --enable-debug-context or --disable-debug-context was used if test "$enable_debug_context" != auto; then # Make sure an invalid option wasn't passed as --enable-debug-context=foo if test "$enable_debug_context" != yes && test "$enable_debug_context" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Invalid option '$enable_debug_context' passed to --enable-debug-context, defaulting to '$default_debug_context'" >&5 printf "%s\n" "$as_me: WARNING: Invalid option '$enable_debug_context' passed to --enable-debug-context, defaulting to '$default_debug_context'" >&2;} enable_debug_context="$default_debug_context" fi else enable_debug_context="$default_debug_context" fi if test "$enable_debug_context" = yes; then printf "%s\n" "#define DEBUG_CONTEXT 1" >>confdefs.h else cat << 'EOF' >&2 configure: WARNING: You have disabled context debugging. Eggdrop will not be able to provide context information if it crashes. Bug reports without context are less helpful when tracking down bugs. EOF fi # Let the user set the handlen with a configure option # Check whether --with-handlen was given. if test ${with_handlen+y} then : withval=$with_handlen; if test -n $withval && test $withval -ge 9 && test $withval -le 32; then printf "%s\n" "#define EGG_HANDLEN $withval" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Invalid handlen given (must be a number between 9 and 32), defaulting to 9." >&5 printf "%s\n" "$as_me: WARNING: Invalid handlen given (must be a number between 9 and 32), defaulting to 9." >&2;} fi fi # Check for IPv6 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for system IPv6 support" >&5 printf %s "checking for system IPv6 support... " >&6; } if test ${egg_cv_var_ipv6_supported+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { int s = socket(AF_INET6, SOCK_STREAM, 0); if (s != -1) close(s); return((s == -1)); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : egg_cv_var_ipv6_supported="yes" else $as_nop egg_cv_var_ipv6_supported="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { int s = socket(AF_INET6, SOCK_STREAM, 0); if (s != -1) close(s); return((s == -1)); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : egg_cv_var_ipv6_supported="yes" else $as_nop egg_cv_var_ipv6_supported="no" fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_ipv6_supported" >&5 printf "%s\n" "$egg_cv_var_ipv6_supported" >&6; } ipv6_enabled="no" # Check whether --enable-ipv6 was given. if test ${enable_ipv6+y} then : enableval=$enable_ipv6; enable_ipv6="$enableval" else $as_nop enable_ipv6="$egg_cv_var_ipv6_supported" fi # Check whether --enable-ipv6 was given. if test ${enable_ipv6+y} then : enableval=$enable_ipv6; enable_ipv6="$enableval" fi if test "$enable_ipv6" = "yes"; then if test "$egg_cv_var_ipv6_supported" = "no"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: You have enabled IPv6 but your system doesn't seem to support it." >&5 printf "%s\n" "$as_me: WARNING: You have enabled IPv6 but your system doesn't seem to support it." >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Eggdrop will compile but will be limited to IPv4 on this host." >&5 printf "%s\n" "$as_me: WARNING: Eggdrop will compile but will be limited to IPv4 on this host." >&2;} fi printf "%s\n" "#define IPV6 1" >>confdefs.h ipv6_enabled="yes" fi if test "$enable_ipv6" = "yes"; then ac_fn_c_check_type "$LINENO" "struct in6_addr" "ac_cv_type_struct_in6_addr" " #include #include " if test "x$ac_cv_type_struct_in6_addr" = xyes then : printf "%s\n" "#define HAVE_STRUCT_IN6_ADDR 1" >>confdefs.h egg_cv_var_have_in6_addr="yes" else $as_nop egg_cv_var_have_in6_addr="no" fi if test "$egg_cv_var_have_in6_addr" = "yes"; then # Check for in6addr_any { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the in6addr_any constant" >&5 printf %s "checking for the in6addr_any constant... " >&6; } if test ${egg_cv_var_have_in6addr_any+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct in6_addr i6 = in6addr_any; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : egg_cv_var_have_in6addr_any="yes" else $as_nop egg_cv_var_have_in6addr_any="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_have_in6addr_any" >&5 printf "%s\n" "$egg_cv_var_have_in6addr_any" >&6; } if test "$egg_cv_var_have_in6addr_any" = "yes"; then printf "%s\n" "#define HAVE_IN6ADDR_ANY 1" >>confdefs.h fi # Check for in6addr_loopback { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the in6addr_loopback constant" >&5 printf %s "checking for the in6addr_loopback constant... " >&6; } if test ${egg_cv_var_have_in6addr_loopback+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct in6_addr i6 = in6addr_loopback; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : egg_cv_var_have_in6addr_loopback="yes" else $as_nop egg_cv_var_have_in6addr_loopback="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_have_in6addr_loopback" >&5 printf "%s\n" "$egg_cv_var_have_in6addr_loopback" >&6; } if test "$egg_cv_var_have_in6addr_loopback" = "yes"; then printf "%s\n" "#define HAVE_IN6ADDR_LOOPBACK 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "struct sockaddr_in6" "ac_cv_type_struct_sockaddr_in6" " #include #include " if test "x$ac_cv_type_struct_sockaddr_in6" = xyes then : printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_IN6 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: no in6_addr found, skipping dependent checks. Custom definitions will be used." >&5 printf "%s\n" "$as_me: no in6_addr found, skipping dependent checks. Custom definitions will be used." >&6;} fi fi # Check for TLS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable TLS support" >&5 printf %s "checking whether to enable TLS support... " >&6; } # Check whether --enable-tls was given. if test ${enable_tls+y} then : enableval=$enable_tls; tls_enabled="$enableval" else $as_nop tls_enabled="yes" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $tls_enabled" >&5 printf "%s\n" "$tls_enabled" >&6; } save_LIBS="$LIBS" # Check whether --with-sslinc was given. if test ${with_sslinc+y} then : withval=$with_sslinc; if test "$tls_enabled" != "no"; then if test -d "$withval"; then save_CC="$CC" save_CPP="$CPP" CC="$CC -I$withval" CPP="$CPP -I$withval" for ac_header in openssl/ssl.h openssl/x509v3.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " #ifdef CYGWIN_HACKS # ifndef __int64 # define __int64 long long # endif #endif " if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF sslinc="-I$withval" else $as_nop as_fn_error $? "Invalid path to OpenSSL headers. $withval/openssl/ doesn't contain the required files." "$LINENO" 5 sslinc="" break fi done SSL_INCLUDES=$sslinc CC="$save_CC" CPP="$save_CPP" else as_fn_error $? "Invalid path to OpenSSL headers. $withval is not a directory." "$LINENO" 5 fi fi fi # Check whether --with-ssllib was given. if test ${with_ssllib+y} then : withval=$with_ssllib; if test "$tls_enabled" != "no"; then if test -d "$withval"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for X509_digest in -lcrypto" >&5 printf %s "checking for X509_digest in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_X509_digest+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto -L$withval -lssl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char X509_digest (); int main (void) { return X509_digest (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_X509_digest=yes else $as_nop ac_cv_lib_crypto_X509_digest=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_X509_digest" >&5 printf "%s\n" "$ac_cv_lib_crypto_X509_digest" >&6; } if test "x$ac_cv_lib_crypto_X509_digest" = xyes then : printf "%s\n" "#define HAVE_LIBCRYPTO 1" >>confdefs.h LIBS="-lcrypto $LIBS" else $as_nop havessllib="no" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_accept in -lssl" >&5 printf %s "checking for SSL_accept in -lssl... " >&6; } if test ${ac_cv_lib_ssl_SSL_accept+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lssl -L$withval -lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char SSL_accept (); int main (void) { return SSL_accept (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ssl_SSL_accept=yes else $as_nop ac_cv_lib_ssl_SSL_accept=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_accept" >&5 printf "%s\n" "$ac_cv_lib_ssl_SSL_accept" >&6; } if test "x$ac_cv_lib_ssl_SSL_accept" = xyes then : printf "%s\n" "#define HAVE_LIBSSL 1" >>confdefs.h LIBS="-lssl $LIBS" else $as_nop havessllib="no" fi if test "$havessllib" = "no"; then as_fn_error $? "Invalid path to OpenSSL libs. $withval doesn't contain the required files." "$LINENO" 5 else SSL_LIBS=-L$withval LDFLAGS="${LDFLAGS} -L$withval" fi else as_fn_error $? "You have specified an invalid path to OpenSSL libs. $withval is not a directory." "$LINENO" 5 fi fi fi if test "$tls_enabled" != "no"; then if test -z "$SSL_INCLUDES"; then for ac_header in openssl/ssl.h openssl/x509v3.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " #ifdef CYGWIN_HACKS # ifndef __int64 # define __int64 long long # endif #endif " if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop havesslinc="no" fi done fi if test -z "$SSL_LIBS"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for X509_digest in -lcrypto" >&5 printf %s "checking for X509_digest in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_X509_digest+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto -lssl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char X509_digest (); int main (void) { return X509_digest (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_X509_digest=yes else $as_nop ac_cv_lib_crypto_X509_digest=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_X509_digest" >&5 printf "%s\n" "$ac_cv_lib_crypto_X509_digest" >&6; } if test "x$ac_cv_lib_crypto_X509_digest" = xyes then : printf "%s\n" "#define HAVE_LIBCRYPTO 1" >>confdefs.h LIBS="-lcrypto $LIBS" else $as_nop havessllib="no" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_accept in -lssl" >&5 printf %s "checking for SSL_accept in -lssl... " >&6; } if test ${ac_cv_lib_ssl_SSL_accept+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lssl -lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char SSL_accept (); int main (void) { return SSL_accept (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ssl_SSL_accept=yes else $as_nop ac_cv_lib_ssl_SSL_accept=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_accept" >&5 printf "%s\n" "$ac_cv_lib_ssl_SSL_accept" >&6; } if test "x$ac_cv_lib_ssl_SSL_accept" = xyes then : printf "%s\n" "#define HAVE_LIBSSL 1" >>confdefs.h LIBS="-lssl $LIBS" else $as_nop havessllib="no" fi fi for ac_func in EVP_sha1 a2i_IPADDRESS do : as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF else $as_nop havessllib="no" break fi done ac_fn_c_check_func "$LINENO" "EVP_md5" "ac_cv_func_EVP_md5" if test "x$ac_cv_func_EVP_md5" = xyes then : printf "%s\n" "#define HAVE_EVP_MD5 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "OPENSSL_buf2hexstr" "ac_cv_func_OPENSSL_buf2hexstr" if test "x$ac_cv_func_OPENSSL_buf2hexstr" = xyes then : else $as_nop ac_fn_c_check_func "$LINENO" "hex_to_string" "ac_cv_func_hex_to_string" if test "x$ac_cv_func_hex_to_string" = xyes then : printf "%s\n" "#define OPENSSL_buf2hexstr hex_to_string" >>confdefs.h else $as_nop havessllib="no" break fi fi ac_fn_c_check_func "$LINENO" "OPENSSL_hexstr2buf" "ac_cv_func_OPENSSL_hexstr2buf" if test "x$ac_cv_func_OPENSSL_hexstr2buf" = xyes then : else $as_nop ac_fn_c_check_func "$LINENO" "string_to_hex" "ac_cv_func_string_to_hex" if test "x$ac_cv_func_string_to_hex" = xyes then : printf "%s\n" "#define OPENSSL_hexstr2buf string_to_hex" >>confdefs.h else $as_nop havessllib="no" break fi fi if test "$havesslinc" = "no"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find OpenSSL headers." >&5 printf "%s\n" "$as_me: WARNING: Cannot find OpenSSL headers." >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Please specify the path to the openssl include dir using --with-sslinc=path" >&5 printf "%s\n" "$as_me: WARNING: Please specify the path to the openssl include dir using --with-sslinc=path" >&2;} fi if test "$havessllib" = "no"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find OpenSSL library 0.9.8 or newer." >&5 printf "%s\n" "$as_me: WARNING: Cannot find OpenSSL library 0.9.8 or newer." >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Please specify the path to libssl and libcrypto using --with-ssllib=path" >&5 printf "%s\n" "$as_me: WARNING: Please specify the path to libssl and libcrypto using --with-ssllib=path" >&2;} fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for OpenSSL" >&5 printf %s "checking for OpenSSL... " >&6; } if test "$havesslinc" = "no" || test "$havessllib" = "no"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: * We tried, but couldn't find TLS libraries. If installed, please specify their paths using the configure flags above" >&5 printf "%s\n" "* We tried, but couldn't find TLS libraries. If installed, please specify their paths using the configure flags above" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: * While not recommended, you can continue without TLS protection by specifying the --disable-tls configure flag" >&5 printf "%s\n" "* While not recommended, you can continue without TLS protection by specifying the --disable-tls configure flag" >&6; } as_fn_error $? "TLS Libraries not found" "$LINENO" 5 else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } if test "$EGG_CYGWIN" = "yes"; then ac_fn_c_check_type "$LINENO" "__int64" "ac_cv_type___int64" "$ac_includes_default" if test "x$ac_cv_type___int64" = xyes then : else $as_nop printf "%s\n" "#define __int64 long long" >>confdefs.h fi fi printf "%s\n" "#define TLS 1" >>confdefs.h ac_fn_c_check_func "$LINENO" "ASN1_STRING_get0_data" "ac_cv_func_ASN1_STRING_get0_data" if test "x$ac_cv_func_ASN1_STRING_get0_data" = xyes then : printf "%s\n" "#define egg_ASN1_string_data ASN1_STRING_get0_data" >>confdefs.h else $as_nop printf "%s\n" "#define egg_ASN1_string_data ASN1_STRING_data" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_PKEY_get1_EC_KEY" "ac_cv_func_EVP_PKEY_get1_EC_KEY" if test "x$ac_cv_func_EVP_PKEY_get1_EC_KEY" = xyes then : printf "%s\n" "#define HAVE_EVP_PKEY_GET1_EC_KEY 1" >>confdefs.h fi save_CC="$CC" save_CPP="$CPP" CC="$CC $sslinc" CPP="$CPP $sslinc" for ac_header in openssl/md5.h do : ac_fn_c_check_header_compile "$LINENO" "openssl/md5.h" "ac_cv_header_openssl_md5_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_md5_h" = xyes then : printf "%s\n" "#define HAVE_OPENSSL_MD5_H 1" >>confdefs.h for ac_func in MD5_Init MD5_Update MD5_Final do : as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF else $as_nop havesslmd5="no" fi done fi done if test "$havesslmd5" != "no"; then printf "%s\n" "#define HAVE_OPENSSL_MD5 1" >>confdefs.h fi CC="$save_CC" CPP="$save_CPP" fi fi # Threaded DNS core # Check whether --enable-tdns was given. if test ${enable_tdns+y} then : enableval=$enable_tdns; tdns_enabled="$enableval" else $as_nop tdns_enabled="yes" fi if test "x$ax_pthread_ok" = "xno"; then tdns_enabled="no (pthread.h not found)" else if test "$tdns_enabled" = "yes"; then printf "%s\n" "#define EGG_TDNS 1" >>confdefs.h fi fi # Check for Python # Check whether --with-python-config was given. if test ${with_python_config+y} then : withval=$with_python_config; if test -d "$withval" || test -x "$withval"; then egg_with_python_config="$withval" else egg_with_python_config="no" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Invalid path to python-config. $withval is not a directory and not an executable." >&5 printf "%s\n" "$as_me: WARNING: Invalid path to python-config. $withval is not a directory and not an executable." >&2;} fi fi # Substitute Makefile variables. EGGVERSION=`grep '^ *# *define *EGG_STRINGVER ' $srcdir/src/version.h | $AWK '{gsub(/(")/, "", $NF); print $NF}'` egg_version_num=`echo $EGGVERSION | $AWK 'BEGIN {FS = "."} {printf("%d%02d%02d", $1, $2, $3)}'` printf "%s\n" "#define EGG_VERSION $egg_version_num" >>confdefs.h if test "x$DEST" = x; then DEST=\${prefix} fi case "$srcdir" in [\\/]* | ?:[\\/]*) MOD_UPDIR="" ;; *) MOD_UPDIR="../" ;; esac ac_config_commands="$ac_config_commands catch-make-rebuild" if test "$cross_compiling" = "yes" then : EGG_CROSS_COMPILING=yes else $as_nop EGG_CROSS_COMPILING=no fi # Create Makefiles ac_config_files="$ac_config_files Makefile doc/Makefile scripts/Makefile src/Makefile src/compat/Makefile src/md5/Makefile src/mod/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) 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 test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by Eggdrop $as_me 1.10.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to . Eggdrop home page: ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ Eggdrop config.status 1.10.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # # variables for create stdint.h replacement PACKAGE="$PACKAGE" VERSION="$VERSION" ac_stdint_h="$ac_stdint_h" _ac_stdint_h=`printf "%s\n" "_$PACKAGE-$ac_stdint_h" | $as_tr_cpp` ac_cv_stdint_message="$ac_cv_stdint_message" ac_cv_header_stdint_t="$ac_cv_header_stdint_t" ac_cv_header_stdint_x="$ac_cv_header_stdint_x" ac_cv_header_stdint_o="$ac_cv_header_stdint_o" ac_cv_header_stdint_u="$ac_cv_header_stdint_u" ac_cv_type_uint64_t="$ac_cv_type_uint64_t" ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" ac_cv_char_data_model="$ac_cv_char_data_model" ac_cv_long_data_model="$ac_cv_long_data_model" ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" ac_cv_type_intmax_t="$ac_cv_type_intmax_t" egg_tclinc="${egg_tclinc}" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "$ac_stdint_h") CONFIG_COMMANDS="$CONFIG_COMMANDS $ac_stdint_h" ;; "replace-if-changed") CONFIG_COMMANDS="$CONFIG_COMMANDS replace-if-changed" ;; "catch-make-rebuild") CONFIG_COMMANDS="$CONFIG_COMMANDS catch-make-rebuild" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "src/compat/Makefile") CONFIG_FILES="$CONFIG_FILES src/compat/Makefile" ;; "src/md5/Makefile") CONFIG_FILES="$CONFIG_FILES src/md5/Makefile" ;; "src/mod/Makefile") CONFIG_FILES="$CONFIG_FILES src/mod/Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 printf "%s\n" "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "$ac_stdint_h":C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_stdint_h : $_ac_stdint_h" >&5 printf "%s\n" "$as_me: creating $ac_stdint_h : $_ac_stdint_h" >&6;} ac_stdint=$tmp/_stdint.h echo "#ifndef" $_ac_stdint_h >$ac_stdint echo "#define" $_ac_stdint_h "1" >>$ac_stdint echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint if test "_$ac_cv_header_stdint_t" != "_" ; then echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint echo "#include " >>$ac_stdint echo "#endif" >>$ac_stdint echo "#endif" >>$ac_stdint else cat >>$ac_stdint < #else #include /* .................... configured part ............................ */ STDINT_EOF echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint if test "_$ac_cv_header_stdint_x" != "_" ; then ac_header="$ac_cv_header_stdint_x" echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint else echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint fi echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint if test "_$ac_cv_header_stdint_o" != "_" ; then ac_header="$ac_cv_header_stdint_o" echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint else echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint fi echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint if test "_$ac_cv_header_stdint_u" != "_" ; then ac_header="$ac_cv_header_stdint_u" echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint else echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint fi echo "" >>$ac_stdint if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then echo "#include <$ac_header>" >>$ac_stdint echo "" >>$ac_stdint fi fi echo "/* which 64bit typedef has been found */" >>$ac_stdint if test "$ac_cv_type_uint64_t" = "yes" ; then echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint else echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint fi if test "$ac_cv_type_u_int64_t" = "yes" ; then echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint else echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint fi echo "" >>$ac_stdint echo "/* which type model has been detected */" >>$ac_stdint if test "_$ac_cv_char_data_model" != "_" ; then echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint else echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint fi echo "" >>$ac_stdint echo "/* whether int_least types were detected */" >>$ac_stdint if test "$ac_cv_type_int_least32_t" = "yes"; then echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint else echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint fi echo "/* whether int_fast types were detected */" >>$ac_stdint if test "$ac_cv_type_int_fast32_t" = "yes"; then echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint else echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint fi echo "/* whether intmax_t type was detected */" >>$ac_stdint if test "$ac_cv_type_intmax_t" = "yes"; then echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint else echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint fi echo "" >>$ac_stdint cat >>$ac_stdint <= 199901L #define _HAVE_UINT64_T #define _HAVE_LONGLONG_UINT64_T typedef long long int64_t; typedef unsigned long long uint64_t; #elif !defined __STRICT_ANSI__ #if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ #define _HAVE_UINT64_T typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ /* note: all ELF-systems seem to have loff-support which needs 64-bit */ #if !defined _NO_LONGLONG #define _HAVE_UINT64_T #define _HAVE_LONGLONG_UINT64_T typedef long long int64_t; typedef unsigned long long uint64_t; #endif #elif defined __alpha || (defined __mips && defined _ABIN32) #if !defined _NO_LONGLONG typedef long int64_t; typedef unsigned long uint64_t; #endif /* compiler/cpu type to define int64_t */ #endif #endif #endif #if defined _STDINT_HAVE_U_INT_TYPES /* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ typedef u_int8_t uint8_t; typedef u_int16_t uint16_t; typedef u_int32_t uint32_t; /* glibc compatibility */ #ifndef __int8_t_defined #define __int8_t_defined #endif #endif #ifdef _STDINT_NEED_INT_MODEL_T /* we must guess all the basic types. Apart from byte-addressable system, */ /* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ /* (btw, those nibble-addressable systems are way off, or so we assume) */ #if defined _STDINT_BYTE_MODEL #if _STDINT_LONG_MODEL+0 == 242 /* 2:4:2 = IP16 = a normal 16-bit system */ typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #ifndef __int8_t_defined #define __int8_t_defined typedef char int8_t; typedef short int16_t; typedef long int32_t; #endif #elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 /* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ /* 4:4:4 = ILP32 = a normal 32-bit system */ typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #ifndef __int8_t_defined #define __int8_t_defined typedef char int8_t; typedef short int16_t; typedef int int32_t; #endif #elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 /* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ /* 4:8:8 = LP64 = a normal 64-bit system */ typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #ifndef __int8_t_defined #define __int8_t_defined typedef char int8_t; typedef short int16_t; typedef int int32_t; #endif /* this system has a "long" of 64bit */ #ifndef _HAVE_UINT64_T #define _HAVE_UINT64_T typedef unsigned long uint64_t; typedef long int64_t; #endif #elif _STDINT_LONG_MODEL+0 == 448 /* LLP64 a 64-bit system derived from a 32-bit system */ typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #ifndef __int8_t_defined #define __int8_t_defined typedef char int8_t; typedef short int16_t; typedef int int32_t; #endif /* assuming the system has a "long long" */ #ifndef _HAVE_UINT64_T #define _HAVE_UINT64_T #define _HAVE_LONGLONG_UINT64_T typedef unsigned long long uint64_t; typedef long long int64_t; #endif #else #define _STDINT_NO_INT32_T #endif #else #define _STDINT_NO_INT8_T #define _STDINT_NO_INT32_T #endif #endif /* * quote from SunOS-5.8 sys/inttypes.h: * Use at your own risk. As of February 1996, the committee is squarely * behind the fixed sized types; the "least" and "fast" types are still being * discussed. The probability that the "fast" types may be removed before * the standard is finalized is high enough that they are not currently * implemented. */ #if defined _STDINT_NEED_INT_LEAST_T typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; #ifdef _HAVE_UINT64_T typedef int64_t int_least64_t; #endif typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; #ifdef _HAVE_UINT64_T typedef uint64_t uint_least64_t; #endif /* least types */ #endif #if defined _STDINT_NEED_INT_FAST_T typedef int8_t int_fast8_t; typedef int int_fast16_t; typedef int32_t int_fast32_t; #ifdef _HAVE_UINT64_T typedef int64_t int_fast64_t; #endif typedef uint8_t uint_fast8_t; typedef unsigned uint_fast16_t; typedef uint32_t uint_fast32_t; #ifdef _HAVE_UINT64_T typedef uint64_t uint_fast64_t; #endif /* fast types */ #endif #ifdef _STDINT_NEED_INTMAX_T #ifdef _HAVE_UINT64_T typedef int64_t intmax_t; typedef uint64_t uintmax_t; #else typedef long intmax_t; typedef unsigned long uintmax_t; #endif #endif #ifdef _STDINT_NEED_INTPTR_T #ifndef __intptr_t_defined #define __intptr_t_defined /* we encourage using "long" to store pointer values, never use "int" ! */ #if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 typedef unsigned int uintptr_t; typedef int intptr_t; #elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 typedef unsigned long uintptr_t; typedef long intptr_t; #elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T typedef uint64_t uintptr_t; typedef int64_t intptr_t; #else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ typedef unsigned long uintptr_t; typedef long intptr_t; #endif #endif #endif /* The ISO C99 standard specifies that in C++ implementations these should only be defined if explicitly requested. */ #if !defined __cplusplus || defined __STDC_CONSTANT_MACROS #ifndef UINT32_C /* Signed. */ # define INT8_C(c) c # define INT16_C(c) c # define INT32_C(c) c # ifdef _HAVE_LONGLONG_UINT64_T # define INT64_C(c) c ## L # else # define INT64_C(c) c ## LL # endif /* Unsigned. */ # define UINT8_C(c) c ## U # define UINT16_C(c) c ## U # define UINT32_C(c) c ## U # ifdef _HAVE_LONGLONG_UINT64_T # define UINT64_C(c) c ## UL # else # define UINT64_C(c) c ## ULL # endif /* Maximal type. */ # ifdef _HAVE_LONGLONG_UINT64_T # define INTMAX_C(c) c ## L # define UINTMAX_C(c) c ## UL # else # define INTMAX_C(c) c ## LL # define UINTMAX_C(c) c ## ULL # endif /* literalnumbers */ #endif #endif /* These limits are merrily those of a two complement byte-oriented system */ /* Minimum of signed integral types. */ # define INT8_MIN (-128) # define INT16_MIN (-32767-1) # define INT32_MIN (-2147483647-1) # define INT64_MIN (-__INT64_C(9223372036854775807)-1) /* Maximum of signed integral types. */ # define INT8_MAX (127) # define INT16_MAX (32767) # define INT32_MAX (2147483647) # define INT64_MAX (__INT64_C(9223372036854775807)) /* Maximum of unsigned integral types. */ # define UINT8_MAX (255) # define UINT16_MAX (65535) # define UINT32_MAX (4294967295U) # define UINT64_MAX (__UINT64_C(18446744073709551615)) /* Minimum of signed integral types having a minimum size. */ # define INT_LEAST8_MIN INT8_MIN # define INT_LEAST16_MIN INT16_MIN # define INT_LEAST32_MIN INT32_MIN # define INT_LEAST64_MIN INT64_MIN /* Maximum of signed integral types having a minimum size. */ # define INT_LEAST8_MAX INT8_MAX # define INT_LEAST16_MAX INT16_MAX # define INT_LEAST32_MAX INT32_MAX # define INT_LEAST64_MAX INT64_MAX /* Maximum of unsigned integral types having a minimum size. */ # define UINT_LEAST8_MAX UINT8_MAX # define UINT_LEAST16_MAX UINT16_MAX # define UINT_LEAST32_MAX UINT32_MAX # define UINT_LEAST64_MAX UINT64_MAX /* shortcircuit*/ #endif /* once */ #endif #endif STDINT_EOF fi if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_stdint_h is unchanged" >&5 printf "%s\n" "$as_me: $ac_stdint_h is unchanged" >&6;} else ac_dir=`$as_dirname -- "$ac_stdint_h" || $as_expr X"$ac_stdint_h" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_stdint_h" : 'X\(//\)[^/]' \| \ X"$ac_stdint_h" : 'X\(//\)$' \| \ X"$ac_stdint_h" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_stdint_h" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p rm -f $ac_stdint_h mv $ac_stdint $ac_stdint_h fi ;; "replace-if-changed":C) egg_replace_file="lush.h" cat > conftest.out << EOF /* Ignore me but do not erase me. I am a kludge. */ #include ${egg_tclinc} EOF if test -f "$egg_replace_file" && cmp -s conftest.out $egg_replace_file; then echo "lush.h is unchanged" else echo "creating lush.h" mv conftest.out $egg_replace_file fi rm -f conftest.out ;; "catch-make-rebuild":C) if test -f .modules; then $srcdir/misc/modconfig --top_srcdir="$srcdir/src" Makefile fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi # Display configure end message { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Operating System: $egg_cv_var_system_type $egg_cv_var_system_release" >&5 printf "%s\n" "Operating System: $egg_cv_var_system_type $egg_cv_var_system_release" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: IPv6 Support: $ipv6_enabled" >&5 printf "%s\n" "IPv6 Support: $ipv6_enabled" >&6; } ADD="" if test "x$TCL_THREADS" = "x1"; then ADD=" (threaded)" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Tcl version: $TCL_PATCHLEVEL$ADD" >&5 printf "%s\n" "Tcl version: $TCL_PATCHLEVEL$ADD" >&6; } ADD="" if test "x$tls_enabled" = "xyes"; then tmpout="tmpfile" if test ! -e "tmp.c" && test ! -e "$tmpout"; then cat >tmp.c < #include int main(void) { printf("%s\n", OPENSSL_VERSION_TEXT); return 0; } EOF $CC $SSL_INCLUDES tmp.c -o "$tmpout" >/dev/null 2>&1 if test -x "./$tmpout"; then tls_version=$("./$tmpout") tls_versionf= fi rm -f "$tmpout" tmp.c >/dev/null 2>&1 fi if test "x$tls_version" != "x"; then ADD=" ($tls_version)" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: SSL/TLS Support: $tls_enabled$ADD" >&5 printf "%s\n" "SSL/TLS Support: $tls_enabled$ADD" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Threaded DNS core: $tdns_enabled" >&5 printf "%s\n" "Threaded DNS core: $tdns_enabled" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } if test "$UNKNOWN_OS" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: WARNING:" >&5 printf "%s\n" "WARNING:" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Unknown Operating System: $egg_cv_var_system_type $egg_cv_var_system_release" >&5 printf "%s\n" " Unknown Operating System: $egg_cv_var_system_type $egg_cv_var_system_release" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Module support has been disabled for this build." >&5 printf "%s\n" " Module support has been disabled for this build." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Please let us know what type of system this is by e-mailing" >&5 printf "%s\n" " Please let us know what type of system this is by e-mailing" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: bugs@eggheads.org. The output of uname -a, and some other basic" >&5 printf "%s\n" " bugs@eggheads.org. The output of uname -a, and some other basic" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: information about the OS should be included." >&5 printf "%s\n" " information about the OS should be included." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } else if test "$WEIRD_OS" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: WARNING:" >&5 printf "%s\n" "WARNING:" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: The operating system you are using has not yet had a great" >&5 printf "%s\n" " The operating system you are using has not yet had a great" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: deal of testing with Eggdrop. For this reason, this compile" >&5 printf "%s\n" " deal of testing with Eggdrop. For this reason, this compile" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: will default to \"make static\"." >&5 printf "%s\n" " will default to \"make static\"." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: To enable module support, type \"make eggdrop\" instead of just" >&5 printf "%s\n" " To enable module support, type \"make eggdrop\" instead of just" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"make\" after you run \"make config\" (or \"make iconfig\")." >&5 printf "%s\n" " \"make\" after you run \"make config\" (or \"make iconfig\")." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: As we have not done a sufficient amount of testing on this" >&5 printf "%s\n" " As we have not done a sufficient amount of testing on this" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: OS, your feedback is greatly appreciated. Please let us know" >&5 printf "%s\n" " OS, your feedback is greatly appreciated. Please let us know" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: at bugs@eggheads.org if there are any problems compiling with" >&5 printf "%s\n" " at bugs@eggheads.org if there are any problems compiling with" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: module support, or if you got it to work :)" >&5 printf "%s\n" " module support, or if you got it to work :)" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: If you experience any problems compiling Eggdrop, please read the" >&5 printf "%s\n" "If you experience any problems compiling Eggdrop, please read the" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: compile guide, found in doc/COMPILE-GUIDE." >&5 printf "%s\n" "compile guide, found in doc/COMPILE-GUIDE." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Type 'make config' to configure the modules, or type 'make iconfig'" >&5 printf "%s\n" "Type 'make config' to configure the modules, or type 'make iconfig'" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: to interactively choose which modules to compile." >&5 printf "%s\n" "to interactively choose which modules to compile." >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5 printf "%s\n" "" >&6; } eggheads-eggdrop-9c00fe1/configure.ac000066400000000000000000000113111524730042700176060ustar00rootroot00000000000000dnl configure.ac: this file is processed by autoconf to produce ./configure. AC_PREREQ([2.71]) AC_INIT([Eggdrop],[1.10.2],[bugs@eggheads.org],[eggdrop],[https://www.eggheads.org]) AC_COPYRIGHT([Copyright (C) 1999 - 2025 Eggheads Development Team]) AC_LANG([C]) AC_REVISION([m4_esyscmd([misc/getcommit])]) AC_CONFIG_SRCDIR(src/eggdrop.h) AC_CONFIG_AUX_DIR(misc) AC_CONFIG_HEADERS([config.h]) AC_PREFIX_DEFAULT([\${HOME}/eggdrop]) AC_PRESERVE_HELP_ORDER dnl config.h stuff AH_TOP([/* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_CONFIG_H #define _EGG_CONFIG_H]) AH_BOTTOM([#endif /* !_EGG_CONFIG_H */]) # Print start message EGG_MSG_CONFIGURE_START EGG_SAVE_PARAMETERS # Check for a working C compiler AC_PROG_CC([gcc cc clang]) EGG_CHECK_CC EGG_CHECK_CC_C99 # These 3 need to be done before any AC_COMPILE_IFELSE()'s. AC_USE_SYSTEM_EXTENSIONS AC_SEARCH_LIBS([strerror],[cposix]) AC_USE_SYSTEM_EXTENSIONS # Check C compiler characteristics. EGG_CHECK_ICC EGG_CHECK_CCPIPE EGG_CHECK_CCWALL AX_CHECK_COMPILE_FLAG([-std=gnu99], [CFLAGS="-std=gnu99 $CFLAGS"]) # Check for executable extension. EGG_EXEEXT # Checks for programs. AC_PROG_INSTALL AC_CHECK_PROGS(MAKE, make gmake) AC_PROG_MAKE_SET AC_PROG_MKDIR_P # Replace relative path with absolute path MKDIR_P=$(echo "$MKDIR_P" | sed "s|^./misc|${ac_pwd}/misc|") AC_PROG_RANLIB AC_PROG_LN_S EGG_PROG_HEAD_1 EGG_PROG_STRIP EGG_PROG_AWK EGG_PROG_BASENAME AC_CHECK_PROG(UNAME,uname,uname) # Check if we will strip the binary # Do this *before* EGG_CHECK_OS EGG_ENABLE_STRIP # Optional: build with gcov instrumentation for the test suite. EGG_ENABLE_COVERAGE # Checks for system libraries. EGG_CHECK_LIBS # Test the OS and set the module linking settings. EGG_OS_VERSION EGG_CHECK_MODULE_SUPPORT EGG_CHECK_OS # Checks for header files. EGG_HEADER_STDC AC_HEADER_DIRENT 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. AC_C_CONST AC_C_BIGENDIAN AC_C_INLINE AC_C_FLEXIBLE_ARRAY_MEMBER AC_TYPE_INTPTR_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_STRUCT_TIMEZONE AC_C_VOLATILE AX_TYPE_SOCKLEN_T # Create stdint.h C99 compatibility header. AX_CREATE_STDINT_H([eggint.h]) # Checks for functions and their arguments. AC_CHECK_FUNCS([clock_gettime 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 # Tcl version to recommend if no Tcl is found, and the site where it can be # found for download. tclrecommendver="8.6.X" tclrecommendsite="https://sourceforge.net/projects/tcl/files/Tcl/" # Tcl header filenames. tclheadernames="tcl.h" # We save the cache (if used) here to speed things up if we can't find Tcl. AC_CACHE_SAVE # Misc Tcl checks. EGG_TCL_ARG_WITH EGG_TCL_WITH_TCLLIB EGG_TCL_WITH_TCLINC EGG_TCL_TCLCONFIG EGG_TCL_CHECK_VERSION EGG_TCL_LUSH # Debug options EGG_DEBUG_ENABLE EGG_DEBUG_DEFAULTS EGG_DEBUG_OPTIONS EGG_DEBUG_CFLAGS EGG_ENABLE_DEBUG_CONTEXT # Let the user set the handlen with a configure option EGG_ARG_HANDLEN # Check for IPv6 EGG_IPV6_STATUS EGG_IPV6_ENABLE EGG_IPV6_COMPAT # Check for TLS EGG_TLS_ENABLE EGG_TLS_WITHSSL EGG_TLS_DETECT # Threaded DNS core EGG_TDNS_ENABLE # Check for Python EGG_PYTHON_WITHCONFIG # Substitute Makefile variables. EGG_SUBST_EGGVERSION EGG_SUBST_DEST EGG_SUBST_MOD_UPDIR EGG_CATCH_MAKEFILE_REBUILD dnl If we are cross-compiling, make a note of it so we can skip some tests in the Makefile AS_IF([test "$cross_compiling" = "yes"], [ EGG_CROSS_COMPILING=yes ], [ EGG_CROSS_COMPILING=no ]) AC_SUBST(EGG_CROSS_COMPILING) # Create Makefiles AC_CONFIG_FILES([Makefile doc/Makefile scripts/Makefile src/Makefile src/compat/Makefile src/md5/Makefile src/mod/Makefile]) AC_OUTPUT # Display configure end message AC_MSG_RESULT EGG_MSG_SUMMARY EGG_MSG_WEIRDOS EGG_MSG_CONFIGURE_END eggheads-eggdrop-9c00fe1/disabled_modules000066400000000000000000000005401524730042700205440ustar00rootroot00000000000000# disabled_modules -- File which lists all Eggdrop modules that are # disabled by default. # # Note: # - Lines which start with a '#' character are ignored. # - Every module name needs to be on its own line webui # Woobie only serves as an example for module programming. No need to # compile it for normal bots ... woobie eggheads-eggdrop-9c00fe1/doc/000077500000000000000000000000001524730042700160705ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/ABOUT000066400000000000000000000053021524730042700166650ustar00rootroot00000000000000About Eggdrop Last revised: July 27, 2010 About Eggdrop Eggdrop was created around December 1993 to help stop the incessant wars on #gayteen. It spawned from another bot that was in the process of being written at the time called "Unrest". The purpose of Unrest was to answer help requests from other bots. The first public release of Eggdrop was version 0.6, and since then, it has grown into what you have before you. Eggdrop is the World's most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms. An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc. One of the features that makes Eggdrop stand out from other bots is module and Tcl scripting support. With scripts and modules, you can make the bot perform almost any task you want. They can do anything from preventing floods to greeting users and banning advertisers from channels. You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently, and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban lists, exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet. Eggdrop is always being improved and adjusted because there are bugs to be fixed and features to be added (if the users demand them, and they make actually sense). In fact, it existed for several years as v0.7 -v0.9 before finally going 1.0. This version of Eggdrop is part of the 1.9 tree. A valiant effort has been made to chase down and destroy bugs. To use Eggdrop, you need: - Some sort of Unix account - A pretty good knowledge of IRC and Unix, including how to compile programs, how to read, and what DCC chat is, at absolute minimum. - About 5-7 MB of disk space. The Eggdrop tarball is about 5.4 MB unpacked. - Tcl -- Eggdrop cannot compile without Tcl installed on your shell. Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/ACCOUNTS000066400000000000000000000161061524730042700172360ustar00rootroot00000000000000Account tracking in Eggdrop In Eggdrop 1.9.3, Eggdrop added the ability to associate nicknames with the service accounts they are logged into. It is IMPORTANT to note that Eggdrop's ability to do this is dependent on an IRC server's implementation of three features- the IRCv3 extended-join capability, the IRCv3 account-notify capability, and WHOX support. All three of these features must be supported by the server and, in the case of extended-join and account-notify, requested by Eggdrop in order for Eggdrop to maintain "perfect" association between nicknames and account statuses. REQUIRED SERVER CAPABILITIES You're going to see this repeated a lot- the IRC server must support three features in order for Eggdrop to accurately associate accounts with nicknames. These three features allow Eggdrop to always know the current association between an account and a nickname by getting account statuses of users already on a channel when it joins, new users joining a channel, and users who authenticate while on a channel. extended-join extended-join is an IRCv3-defined capability that adds the account name of a user to the JOIN message sent by the IRC server, alerting clients that a new member has joined a channel. Enabling this capability allows Eggdrop to immediately determine the account name associated with a user joining a channel account-notify account-notify is an IRCv3-defined capability that sends a message to a channel when a member of the channel either authenticates or deauthenticates from their account. Enabling this capability allows Eggdrop to immediately associate an account to a channel member when they authenticate or deauthenticate. WHOX 'WHOX `_ is a server feature that allows a client to request custom fields to be returned in a WHO response. If a server supports this capability, Eggdrop sends a WHOX query to the server when it joins a channel, allowing it to immediately determine accounts associated with channel members when Eggdrop joins a channel. ENABLING EGGDROP ACCOUNT TRACKING By default, the Eggdrop config file will attempt to enable all the capabilities required for account tracking. There are two settings to pay attention to : # To request the account-notify feature via CAP, set this to 1 set account-notify 1 # To request the extended-join feature via CAP, set this to 1 set extended-join 1 The ability of a server to support WHOX queries is determined via a server's ISUPPORT (005) reply. If a server supports WHOX queries, Eggdrop will automatically enable this feature. CHECKING ACCOUNT-TRACKING STATUS While Eggdrop is running, join the partyline and type .status. If account-tracking is enabled (both the server supports and Eggdrop has requested), you'll see this line : Loaded module information: #eggdroptest : (not on channel) Channels: #eggdroptest (trying) Account tracking: Enabled <--- This line Online as: BeerBot (BeerBot) Otherwise, the prompt will tell you which required capability is missing/not enabled : Loaded module information: #eggdroptest : 2 members, enforcing "+tn" (greet) Channels: #eggdroptest (need ops) Account tracking: Best-effort (Missing capabilities: extended-join, see .status all for details) <---- This line Online as: Eggdrop (Eggdrop) DETERMINING IF A SERVER SUPPORTS ACCOUNT CAPABILITIES A server announces the capabilities it supports via a CAP request. If you have Tcl enabled on the partyline (or via a raw message from a client), you can send .tcl cap ls and see if the extended-join and account-notify capabilities are supported by the server. If they are not listed, the server does not support it. A server announces if it supports WHOX via its ISUPPORT (005) announcement. If you have Tcl enabled on the partyline, you can send .tcl issupport isset WHOX and if it returns '1', WHOX is supported by the server. BEST-EFFORT ACCOUNT TRACKING If a server only supports some, but not all, of the required capabilities, Eggdrop will switch to 'best effort' account tracking. This means Eggdrop will update account statuses whenever it sees account information, but in this mode Eggdrop cannot guarantee that all account associations are up to date. If a server does not support extended-join, Eggdrop will not be able to determine the account associated with a user when they join. Eggdrop can update this information by sending a WHOX to the server. If a server does not support account-notify, Eggdrop will not be able to determine the account associated with a user if they authenticate/deauthenticate from their account after joining a channel. Eggdrop can update this information by sending a WHOX to the server. If a server does not support WHOX, Eggdrop will not be able to determine the accounts associated with users already on a channel before Eggdrop joined. There is no reliable way to update this information. One workaround to significantly increase the accuracy of account tracking for scripts in a 'best effort' scenario would be to issue a WHOX query (assuming the server supports it), wait for the reply from the server, and then query for the account information. account-tag One supplementary capability that can assist a best-effort account tracking scenario is the IRCv3-defined account-tag capability. The account-tag capability attaches a tag with the account name associated with the user sending a command. Enabling this capability allows Eggdrop to update its account tracking every time a user talks in channel, sets a mode, sends a kick, etc. While still not able to offer the same level of accuracy as enabling the "main three" account tracking features, it can increase the overall accuracy of the account list. Additionally, binds that react to user activity (pub, kick, mode, etc) containing account-tag will update the internal account list prior to executing the associated callback, so looking up the account name in the callback can be considered accurate. USING ACCOUNTS WITH TCL SCRIPTS The Eggdrop Tcl ACCOUNT bind is triggered whenever an existing account record stored by Eggdrop is modified, such as a user de/authenticating to their account while in a channel, or information such as an account-tag being seen that updates an existing user. However, the ACCOUNT bind will NOT be triggered for the creation of a new user record, such as a user joining a channel. The bind is triggered for every channel the user is seen on- this means if a user is present with Eggdrop on four channels, the bind will be executed four times, each time with a different channel variable being passed to the associated Tcl procedure. Additionally, in a best-effort account tracking situation, Eggdrop will update the account associated with a user on all channels, not just the channel the event is seen on (and thus resulting in a bind trigger for each channel the user is on). In order to trigger Tcl script events to cover all instances where a user logs in, you need to pair an ACCOUNT bind with a JOIN bind. This will allow you to execute account-based events when a user joins as well as if they authenticate after joining. eggheads-eggdrop-9c00fe1/doc/AUTOSCRIPTS000066400000000000000000000237611524730042700176440ustar00rootroot00000000000000Eggdrop Autoscripts Since it's inception, users have needed to load a Tcl script into Eggdrop by downloading a Tcl file, editing the file to customize settings, and then sourceing that file in the config file. In v1.10, the Autoscripts system was added to make this process a little more user-friendly. The autoscripts system helps by: - Centralizing commonly-used scripts in a single location - Allowing scripts to be downloaded via the partyline - Allowing script settings to be configured via the partyline - Allowing user-written scripts to be managed by the autoscripts system - Providing a documented API to write autoscripts-compatible scripts AUTOSCRIPTS USAGE To view available autoscript commands, type .autoscript on the partyline. This will open up a special Eggdrop console that doesn't require you to prefix commands with a '.' . The following sub-commands are available for use with script: remote This command will list scripts hosted on the Eggdrop website that are available to be downloaded and installed on your Eggdrop. fetch

About Eggdrop Last revised: July 27, 2010

About Eggdrop

Eggdrop was created around December 1993 to help stop the incessant wars on #gayteen. It spawned from another bot that was in the process of being written at the time called “Unrest”. The purpose of Unrest was to answer help requests from other bots. The first public release of Eggdrop was version 0.6, and since then, it has grown into what you have before you.

Eggdrop is the World’s most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms.

An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc.

One of the features that makes Eggdrop stand out from other bots is module and Tcl scripting support. With scripts and modules, you can make the bot perform almost any task you want. They can do anything from preventing floods to greeting users and banning advertisers from channels.

You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently, and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban lists, exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet.

Eggdrop is always being improved and adjusted because there are bugs to be fixed and features to be added (if the users demand them, and they make actually sense). In fact, it existed for several years as v0.7 - v0.9 before finally going 1.0. This version of Eggdrop is part of the 1.9 tree. A valiant effort has been made to chase down and destroy bugs.

To use Eggdrop, you need:

  • Some sort of Unix account

  • A pretty good knowledge of IRC and Unix, including how to compile programs, how to read, and what DCC chat is, at absolute minimum.

  • About 5-7 MB of disk space. The Eggdrop tarball is about 5.4 MB unpacked.

  • Tcl – Eggdrop cannot compile without Tcl installed on your shell.

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/about/legal.html000066400000000000000000000213551524730042700221260ustar00rootroot00000000000000 Boring legal stuff — Eggdrop 1.10.2 documentation
eggheads-eggdrop-9c00fe1/doc/html/index.html000066400000000000000000001035611524730042700210370ustar00rootroot00000000000000 Eggdrop, an open source IRC bot — Eggdrop 1.10.2 documentation

Eggdrop, an open source IRC bot

Eggdrop is a free, open source software program built to assist in managing an IRC channel. It is the world’s oldest actively-maintained IRC bot and was designed to be easily used and expanded on via it’s ability to run Tcl scripts. Eggdrop can join IRC channels and perorm automated tasks such as protecting the channel from abuse, assisting users obtain their op/voice status, provide information and greetings, host games, etc.

Some things you can do with Eggdrop

Eggdrop has a large number of features, such as:

How to get Eggdrop

The Eggdrop project source code is hosted at https://github.com/eggheads/eggdrop. You can clone it via git, or alternatively a copy of the current stable snapshot is located at https://geteggdrop.com. Additional information can be found on the official Eggdrop webpage at https://www.eggheads.org. For more information, see Installing Eggdrop

How to install Eggdrop

Installation Pre-requisites

Eggdrop requires Tcl (and its development header files) to be present on the system it is compiled on. It is also strongly encouraged to install openssl (and its development header files) to enable TLS-protected network communication.

Installation

A guide to quickly installing Eggdrop can be found here.

Where to find more help

The Eggheads development team can be found lurking on #eggdrop on the Libera network (irc.libera.chat).

Using Eggdrop

eggheads-eggdrop-9c00fe1/doc/html/install/000077500000000000000000000000001524730042700205025ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/html/install/install.html000066400000000000000000000436511524730042700230470ustar00rootroot00000000000000 Installing Eggdrop — Eggdrop 1.10.2 documentation

Installing Eggdrop

This is the quick install guide; if you have had little or no experience with UNIX or Eggdrop, READ THE README FILE NOW! This file is best for experienced users.

For more information on compiling Eggdrop, see the Compile Guide in doc/COMPILE-GUIDE (and of course, the README FILE).

Quick Startup

Eggdrop uses the GNU autoconfigure scripts to make things easier.

  1. Type ‘./configure’ from the Eggdrop directory. The configure script

    will determine how your system is set up and figure out how to correctly compile Eggdrop. It will also try to find Tcl, which is required to compile.

  2. Type either ‘make config’ or ‘make iconfig’ to determine which

    modules will be compiled. ‘make config’ compiles the default modules (everything but woobie.mod). If you want to choose which modules to compile, use ‘make iconfig’.

  3. Type ‘make’ from the Eggdrop directory, or to force a statically

    linked module bot, type ‘make static’. Otherwise, the Makefile will compile whatever type of bot the configure script determined your system will support. Dynamic is always the better way to go if possible. There are also the ‘debug’ and ‘sdebug’ (static-debug) options, which will give more detailed output on a (highly unlikely :) crash. This will help the development team track down the crash and fix the bug. Debug and sdebug will take a little longer to compile and will enlarge the binary a bit, but it’s worth it if you want to support Eggdrop development.

  4. Eggdrop must be installed in a directory somewhere. This is

    accomplished by entering the UNIX command:

    make install
    

    This will install the Eggdrop in your home directory in a directory called ‘eggdrop’ (i.e. /home/user/eggdrop).

    If you want to install to a different directory, use:

    make install DEST=<directory>
    

    For example:

    make install DEST=/home/user/otherdir
    

    Note that you must use full path for every file to be correctly installed.

  5. Since version 1.8, Eggdrop can use SSL to protect botnet links. If you intend on protecting botnet traffic between Eggdrops, you must generate SSL certificates by running:

        make sslcert
    
     Or, if you installed your eggdrop to a different directory in step 4, you
     will want to run:
    
       make sslcert DEST=<directory>
    
    For those using scripts to install Eggdrop, you can non-interactively
    generate a key and certificate by running:
    
       make sslsilent
    
     Read docs/TLS for more info on this process.
    

[The following steps are performed in the directory you just installed Eggdrop into from the previous step]

  1. Edit your config file completely.

  2. Start the bot with the “-m” option to create a user file, i.e.

    ./eggdrop -m LamestBot.conf
    
  3. When starting the bot in the future, drop the “-m”. If you have edited

    your config file correctly, you can type:

    chmod u+x <my-config-file-name>
    

    For example:

    chmod u+x LamestBot.conf
    

    From then on, you will be able to use your config file as a shell script. You can just type “./LamestBot.conf” from your shell prompt to start up your bot. For this to work, the top line of your script MUST contain the correct path to the Eggdrop executable.

  4. It’s advisable to run your bot via crontab, so that it will

    automatically restart if the machine goes down or (heaven forbid) the bot should crash. Eggdrop includes a helper script to auto-generate either a systemd or crontab entry. To add a systemd job, run:

    ./scripts/autobotchk [yourconfig.conf] -systemd
    

    or to add a crontab job, run:

    ./scripts/autobotchk [yourconfig.conf]
    
  5. Smile. You have an Eggdrop!

Cygwin Requirements (Windows)

Eggdrop requires the following packages to be added from the Cygwin installation tool prior to compiling:

Interpreters: tcl, tcl-devel
Net:          openssl, libssl-devel
Devel:        autoconf, gcc-core, git, make
Libs:         zlib-devel

Modules

Modules are small pieces of code that can either be compiled into the binary or can be compiled separately into a file. This allows for a much smaller binary.

If there are any modules that you have made or downloaded, you can add them to the bot by placing them in the /src/mod directory with a mod extension. They will be automatically compiled during make for you. They must have a valid Makefile and, of course, be compatible with the rest of the Eggdrop source.

If you wish to add a module at a later time, follow the same steps in paragraph 2. After you have moved the appropriate files, you will only need to type ‘make modules’ to compile only the modules portion of the bot.

This is the end. If you read to this point, hopefully you have also read the README file. If not, then READ IT!&@#%@!

Have fun with Eggdrop!

Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/install/readme.html000066400000000000000000000562631524730042700226410ustar00rootroot00000000000000 README — Eggdrop 1.10.2 documentation

README

Please, at least SKIM this document before asking questions. In fact, READ IT if you’ve never successfully set up an Eggdrop bot before.

Notice

Make SURE that you select your +n (owner) users wisely. They have 100% access to your bot and account. ONLY GIVE THIS POWER TO SOMEONE YOU TRUST COMPLETELY!

What is Eggdrop?

Eggdrop is the world’s most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature-rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms.

An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc.

One of the features that makes Eggdrop stand out from other bots is module and Tcl and Python scripting support. With scripts and modules you can make the bot perform almost any task you want. They can do anything: from preventing floods to greeting users and banning advertisers from channels.

You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban/exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet.

Eggdrop is always being improved and adjusted because there are bugs to be fixed and features to be added (if the users demand them and they make actually sense). In fact, it existed for several years as v0.7 - v0.9 before finally going 1.0. This version of Eggdrop is part of the 1.9 tree. A valiant effort has been made to chase down and destroy bugs.

This README file contains information about how to get Eggdrop, command line options for Eggdrop, what you may need to do when upgrading from older versions, a list of frequently asked questions, how to set up a crontab, some boring legal stuff, some basics about git usage and some channels where you might get help with Eggdrop.

How to Get Eggdrop

There are two official methods to download Eggdrop source code. Alternately, Eggdrop also comes as a docker image.

FTP

The latest Eggdrop stable source code is always located at https://geteggdrop.com. You can also download the current stable, previous stable, and development snapshot at https://ftp.eggheads.org/pub/eggdrop/source

Git Development Snapshot

Eggdrop developers use git to manage the Eggdrop codebase for development. If you are interested in trying out the VERY LATEST updates to Eggdrop, you can use git to obtain most recent code from the Eggheads repository. BE WARNED, the development branch of Eggdrop is not to be considered stable and may (haha) contain significant bugs still being worked on.

To obtain Eggdrop via the git repository (hosted by GitHub), you can either clone the repository via git or download a development snapshot.

To clone the repository, simply type:

git clone https://github.com/eggheads/eggdrop.git

Otherwise, you can download the development snapshot as a tar archive from:

Docker

You can pull the official Eggdrop Docker image via:

docker pull eggdrop:latest

Additional Eggdrop Docker documentation can be found at https://hub.docker.com/_/eggdrop

System Pre-Requisites

Before you can compile Eggdrop, Tcl must be installed on your system. Many systems have Tcl installed on them by default (you can check by trying the command “tclsh”; if you are given a ‘%’ for a prompt, it is, and you can type ‘exit’ to exit the Tcl shell. However, Eggdrop also requires the Tcl development header files to be installed. They can often be installed via an OS package manager, usually called something similar to ‘tcl-dev’ for the package name. You can also download Tcl source from https://www.tcl.tk/software/tcltk/download.html.

Eggdrop also requires openssl (and its development headers) in order to enable SSL/TLS protection of network data. The header files are often called something similar to ‘libssl-dev’. While not advised, this requirement can be removed by compiling using ./configure --disable-tls, but you will not be able to connect to TLS-protected IRC servers nor utilize secure botnet communication.

Minimum Requirements

Some components of Eggdrop relies on a variety of third-party libraries, documented here.

Functionality

Package

Minimum Version

Tcl interpreter (required)

Tcl Dev Library

8.5.0

Secure communication

OpenSSL

0.9.8

Python module

Python

3.8.0

Compression module

zlib

Any

Quick Startup

Please see the Install file after you finish reading this file.

Upgrading

The upgrade process for Eggdrop is very simple, simply download the new source code and repeat the compile process. You will want to read the NEWS for any new configuration file settings you want to add. Please see Upgrading for full details.

Command Line

Eggdrop has some command line options - not many, because most things should be defined through the config file. However, sometimes you may want to start up the bot in a different mode and the command line options let you do that. Basically, the command line for Eggdrop is:

./eggdrop [options] [config-file]

The options available are:

-t: Don’t background, use terminal. Your console will drop into an

interactive partyline session, similar to a DCC chat with the bot. This is useful for troubleshooting connection issues with the bot.

-c: Don’t background, show channel info. Every 10 seconds your screen

will clear and you will see the current channel status, sort of like “top”.

-m: Create userfile. If you don’t have a userfile, this will make Eggdrop

create one and give owner status to the first person that introduces himself or herself to it. You’ll need to do this when you first set up your bot.

-h: Show help, then quit.

-v: Show version info, then quit.

Most people never use any of the options except -m and you usually only need to use that once.

Auto-starting Eggdrop

Systems go down from time to time, taking your Eggdrop along with it. You may not be not around to restart it manually, so you can instead use features of the operating system to automatically restart Eggdrop should it quit for any reason. Eggdrop comes with an autobotchk shell script that can create either a systemd or crontab entry. The systemd option will monitor your Eggdrop and a) start it when the machine boots and b) restart the Eggdrop if it crashes for any reason. The (older) crontab option will check (by default) every 10 minutes to see if your Eggdrop is still running, and attempt to restart it if it is not.

To auto-generate a systemd job, from the Eggdrop install directory, simply run:

./scripts/autobotchk <Eggdrop config file> -systemd

To auto-geneerate a script to check Eggdrop’s status and run it via a crontab entry, simply run:

./scripts/autobotchk <Eggdrop config file>

This will crontab your bot using the default setup. If you want a list of autobotchk options, type ‘./autobotchk’. A crontab example with options would be:

./scripts/autobotchk <Eggdrop config file> -noemail -5

This would setup crontab to run the botchk every 5 minutes and not send you an email saying that it restarted your bot.

Documentation

We’re trying to keep the documentation up to date. If you feel that anything is missing here or that anything should be added, etc, please create an issue, or better yet a pull request, at https://www.github.com/eggheads/eggdrop Thank you!

Obtaining Help

You can obtain help with Eggdrop in the following IRC channels:

  • Libera Chat - #eggdrop (official channel), #eggheads (development discussion)

  • DALnet - #eggdrop

  • EFnet - #egghelp

  • IRCnet - #eggdrop

  • QuakeNet - #eggdrop.support

  • Undernet - #eggdrop

If you plan to ask questions in any of the above channels, you should be familiar with and follow IRC etiquette:

  • Don’t type using CAPITAL letters, colors or bold.

  • Don’t use “!” and “?” excessively.

  • Don’t /msg people without their permission.

  • Don’t repeat or paste more than 4 lines of text to the channel.

  • 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 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/install/upgrading.html000066400000000000000000000316041524730042700233540ustar00rootroot00000000000000 Upgrading Eggdrop — Eggdrop 1.10.2 documentation

Upgrading Eggdrop

It is easy to upgrade Eggdrop to a new version! To have a full picture of the changes made since your last upgrade, we recommend reading the NEWS file. Upgrades from the 1.8 and 1.9 lines of Eggdrop should take place with little to no issues. While the config file, user files, and channel files can all be reused, it is recommended to review the NEWS files for each release to identify new configuration settings that have been added.

For support, feel free to visit us on Libera #eggdrop.

How to Upgrade

To upgrade to a newer version of Eggdrop, first backup your .config file, .user file, and .chan file. While they are saved and should not be overwritten by the upgrade process, it never hurts to make anothere copy :)

Next, view the NEWS file to see what changes have been made, paying particular attention to the config file section to see what new/different commands have been added. Update your config file appropriately.

Then, simply follow the same steps you followed to install Eggdrop previously. Download the source, unzip/untar it, and run the ‘./configure’, ‘make config’, ‘make’, and ‘make install’ commands. Restart your Eggdrop and you will be up and running with the latest version of Eggdrop.

Must-read changes for Eggdrop v1.10

These are NOT all the changes or new settings; rather just the “killer” changes that may directly affect Eggdrop’s previous performance without modification.

Config file changes

To migrate from a 1.8 to a 1.10 Eggdrop, some changes are suggested to be made in your configuration file:

  • Eggdrop has deprecated the blowfish module for password hashing in favor of the PBKDF2 module. This is a BIG change which, if done carelessly, has the potential to render stored passwords useless. Please see doc/PBKDF2 for information on how to properly migrate your userfiles and passwords to the new module.

  • Eggdrop 1.9 switched from the “set servers {}” syntax to the “server add” command. For example, if your configuration file previously had:

    set servers {
      my.server.com:6667
    }
    

    you should now instead use:

    server add my.server.com 6667
    

    Please read the config file for additional examples

  • Eggdrop no longer requires the ‘-n’ flag to start Eggdrop in terminal mode.

Modules

While most 3rd party modules that worked on older Eggdrop versions should still work with Eggdrop 1.10, many of them contain a version check that only allows them to run on 1.6.x bots. We have removed the version check from some of the more popular modules provide them at https://ftp.eggheads.org/pub/eggdrop/modules/1.10/

Scripts

All 3rd party Tcl scripts that worked with Eggdrop versions as early as v1.6 should still fully work with Eggdrop 1.10.

Botnet

In Eggdrop v1.8, Eggdrop bots would automatically attempt to upgrade any botnet link to an SSL/TLS connection. Since v1.9, the user is required to explicitly request an SSL/TLS connection by prefixing the port with a ‘+’. If you wish your botnet to take advantage of encryption, use the .chaddr command to update your ports to start with a ‘+’.

Tcl Commands

A lot of backwards-compatible additions and changes have been made to Tcl commands. Please look at doc/tcl-commands.doc to see them.

Documentation

Documentation has been updated to reflect new and removed commands and variables.

eggheads-eggdrop-9c00fe1/doc/html/modules/000077500000000000000000000000001524730042700205045ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/html/modules/included.html000066400000000000000000000554601524730042700231730ustar00rootroot00000000000000 Modules included with Eggdrop — Eggdrop 1.10.2 documentation

Modules included with Eggdrop

Assoc Module

This module provides assoc support, i.e. naming channels on the botnet.

Blowfish Module

This module has been deprecated in favor of the pbkdf2 module for hashing purposes, such as passwords in the userfile. However, it is still required for encrypting/decrypting strings.

Channels Module

This module provides channel related support for the bot. Without it, you won’t be able to make the bot join a channel or save channel specific userfile information.

Compress Module

This module provides support for file compression. This allows the bot to transfer compressed user files and, therefore, save a significant amount of bandwidth.

Console Module

This module provides storage of console settings when you exit the bot or type .store on the partyline.

CTCP Module

This module provides the normal ctcp replies that you’d expect. Without it loaded, CTCP CHAT will not work.

DNS Module

This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections.

Filesys Module

This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server.

Ident Module

This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon.

IRC Module

This module provides basic IRC support for your bot. You have to load this if you want your bot to come on IRC.

Notes Module

This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval.

PBKDF2 Module

This modules updates Eggdrop to use PBKDF2 for hashing purposes, such as for userfile passwords. It was specifically designed to work with the blowfish module to make the transition from blowfish to pbkdf2 password hashing as easy as possible. If you are transitioning a userfile from 1.8 or earlier, you should load this AND the blowfish module. By doing so, Eggdrop will seamlessly update the old blowfish hashes to the new PBKDF2 hashes once a user logs in for the first time, and allow you to (eventually) remove the blowfish module altogether. For new bots, you should load this module by itself and not use the blowfish module. The blowfish module is still required if you use Tcl to encrypt/decrypt strings in Tcl (ie, some scripts). Eggdrop will not start without a password-hashing module loaded.

Python Module

This module adds a python interpreter to Eggdrop, enabling you to run python scripts on your Eggdrop!

Seen Module

This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot’s userlist. If you are looking for a better and more advanced seen module, try the gseen module, originally written by G’Quann and forked/updated for modern Eggdrop by mortmann. You can find it at https://github.com/michaelortmann/gseen.mod

Server Module

This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define.

Share Module

This module provides userfile sharing support between two directly linked bots.

Transfer Module

The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing.

Twitch Module

The Twitch module modifies Eggdrop to interact with the Twitch service. Twitch uses a modified implementation of IRC so not all functionality will be present. Please read doc/TWITCH for specifics on how to best use the Tiwtch module.

Uptime Module

This module reports uptime statistics to the uptime contest web site at https://www.eggheads.org/uptime. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn’t listed, try again later. See doc/settings/mod.uptime for more information, including details on what information is sent to the uptime server.

Woobie Module

This is for demonstrative purposes only. If you are looking for starting point in writing modules, woobie is the right thing.

eggheads-eggdrop-9c00fe1/doc/html/modules/index.html000066400000000000000000000255401524730042700225070ustar00rootroot00000000000000 Eggdrop Module Information — Eggdrop 1.10.2 documentation

Eggdrop Module Information Last revised: Jul 25, 2016

Eggdrop Module Information

What are modules?

Modules are independent portions of code that are loaded separately from the main core code of Eggdrop. This allows users to only implement the features they desire without adding the extra overhead or “bloat” of those they don’t, or even write their own module to add an enhancement not currently implemented by the Eggdrop development team. For example, the transfer module provides the ability to transfer files to and from the Eggdrop, and the ident module provides the ability to run an ident server to answer ident requests.

How to install a module

Please note that these are only basic instructions for compiling and installing a module. Please read any and all directions included with the module you wish to install.

  1. Download and un-tar the Eggdrop source code.

  2. Place the new module in its own directory (in the format of (modulename).mod) in src/mod.

  3. Run ./configure (from eggdrop-1.10.2/).

  4. Type ‘make config’ or ‘make iconfig’.

  5. Type ‘make’.

  6. Copy the compiled module file (modulename.so) into your bot’s modules folder.

  7. Add ‘loadmodule modulename’ to your eggdrop.conf file (do not add the .so suffix).

  8. Rehash or restart your bot.

To view your currently loaded modules, type ‘.module’.

Can I compile Eggdrop without dynamic modules? (Static compile)

Yes, you can. If the configure script detects that your system CAN’T run modules, it will setup ‘make’ to link the modules in statically for you. You can choose this option yourself by using ‘make static’. You can also try to compile dynamic modules on a static-only system by using ‘make eggdrop’.

Do I still need to ‘loadmodule’ modules?

YES, when you compile statically, all the modules are linked into the main executable. HOWEVER, they are not enabled until you use loadmodule to enable them, hence you get nearly the same functionality with static modules as with dynamic modules.

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/internals.html000066400000000000000000001441251524730042700234000ustar00rootroot00000000000000 Eggdrop Bind Internals — Eggdrop 1.10.2 documentation

Eggdrop Bind Internals

This document is intended for C developers who want to understand how Eggdrop’s Tcl binds or C binds work.

For documentation purposes the “dcc” bind type is used as an example.

It already exists and is suitable to illustrate the details of bind handling in Eggdrop.

Note: All code snippets are altered for brevity and simplicity, see original source code for the full and current versions.

General Workflow To Create a New Bind

To create a new type of bind, there are generally three steps. First, you must add the new bind type to the Bind Table. Once you have registered the bind type with the bind table, you must create a C Function that will be called to perform the functionality you wish to occur when the bind is triggered. Finally, once the C code supporting the new bind has been finished, the new Tcl binding is ready to be used in a Tcl script.

Adding a New Bind Type to the Bind Table

The bind is added to the bind table is by calling, either at module initialization or startup

/* Global symbol, available to other C files with
 * extern p_tcl_bind_list H_dcc;
 */
p_tcl_bind_list H_dcc;

/* Creating the bind table:
 * @param[in] const char *name Limited in length, see tclhash.h
 * @param[in] int flags        HT_STACKABLE or 0
 * @param[in] IntFunc          Function pointer to C handler
 * @return    p_tcl_bind_list  aka (tcl_bind_list_t *)
 */
H_dcc = add_bind_table("dcc", 0, builtin_dcc);

What the C handler does is explained later, because a lot happens before it is actually called. IntFunc is a generic function pointer that returns an int with arbitrary arguments.

H_dcc can be exported from core and imported into modules as any other variable or function. That should be explained in a separate document.

Stackable Binds: HT_STACKABLE

HT_STACKABLE means that multiple binds can exist for the same mask. An example of what happens when NOT using this flag shown in the code block below.

bind dcc - test proc1; # not stackable
bind dcc - test proc2; # overwrites the first one, only proc2 will be called

To enable this feature, you must set the second argument to add_bind_table() with HT_STACKABLE. Using HT_STACKABLE does not automatically call all the binds that match, see the bind flags listed in Triggering any Bind section for details on the partner flags BIND_STACKABLE and BIND_WANTRET used in check_tcl_bind().

Adding Bind Functionality

A C function must created that will be called when the bind is triggered. Importantly, the function is designed to accept specific arguments passed by Tcl.

int check_tcl_dcc(const char *cmd, int idx, const char *args) {
  struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
  int x;
  char s[11];

  get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
  egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
  Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0);
  Tcl_SetVar(interp, "_dcc2", (char *) s, 0);
  Tcl_SetVar(interp, "_dcc3", (char *) args, 0);
  x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3",
                  MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS);
  /* snip ..., return code handling */
  return 0;
}

The global Tcl variables $_dcc1 $_dcc2 $_dcc3 are used as temporary string variables and passed as arguments to the registered Tcl proc.

This shows which arguments the callbacks in Tcl get:

  • the nickname of the DCC chat user (handle of the user)

  • the IDX (socket id) of the partyline so [putdcc] can respond back

  • another string argument that depends on the caller

The call to check_tcl_dcc can be found in the DCC parsing in src/dcc.c.

Using the Bind in Tcl

After the bind table is created with add_bind_table, Tcl procs can already be registered to this bind by calling

bind dcc -|- test myproc
proc myproc {args} {
  putlog "myproc was called, argument list: '[join $args ',']'"
  return 0
}

Of course it is not clear so far:

  • If flags -|- matter for this bind at all and what they are checked against

  • If channel flags have a meaning or global/bot only

  • What test is matched against to see if the bind should trigger

  • Which arguments myproc receives, the example just accepts all arguments

Triggering any Bind

check_tcl_bind is used by all binds and does the following

/* Generic function to call one/all matching binds
 * @param[in] tcl_bind_list_t *tl      Bind table (e.g. H_dcc)
 * @param[in] const char *match        String to match the bind-masks against
 * @param[in] struct flag_record *atr  Flags of the user calling the bind
 * @param[in] const char *param        Arguments to add to the bind callback proc (e.g. " $_dcc1 $_dcc2 $_dcc3")
 * @param[in] int match_type           Matchtype and various flags
 * @returns   int                      Match result code
 */

/* Source code changed, only illustrative */
int check_tcl_bind(tcl_bind_list_t *tl, const char *match, struct flag_record *atr, const char *param, int match_type) {
  int x = BIND_NOMATCH;
  for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) {
    /* Check if bind mask matches */
    if (!check_bind_match(match, tm->mask, match_type))
      continue;
    for (tc = tm->first; tc; tc = tc->next) {
      /* Check if the provided flags suffice for this command. */
      if (check_bind_flags(&tc->flags, atr, match_type)) {
        tc->hits++;
        /* not much more than Tcl_Eval(interp, "<procname> <arguments>"); and grab the result */
        x = trigger_bind(tc->func_name, param, tm->mask);
      }
    }
  }
  return x;
}

Bind Flags

The last argument to check_tcl_bind in check_tcl_dcc sets additional configurations for the bind. These are the allowed defined values:

Value

Description

MATCH_PARTIAL

Check the triggering value against the beginning of the bind mask, ie DIR triggers a mask for DIRECTORY (case insensitive)

MATCH_EXACT

Check the triggering value exactly against the bind mask value (case insensitive)

MATCH_CASE

Check the triggering value exactly against the bind mask value (case sensitive)

MATCH_MASK

Check if the bind mask is matched against the triggering value as a wildcarded value

MATCH_MODE

Special mode for bind mode similar to MATCH_MASK. This uses case-insensitive matching before the first space in the mask, (the channel), and then case sensitive after the first space (the modes)

MATCH_CRON

Check the triggering value against a bind mask formatted as a cron entry, ie “30 7 6 7 5 “ triggers a mask for “30 7 * * * “

BIND_USE_ATTR

Check the flags of the user match the flags required to trigger the bind

BIND_STACKABLE

Allow one mask to be re-used to call multiple Tcl proc. Must be used with HT_STACKABLE

BIND_WANTRET

With stacked binds, if the called Tcl proc called returns a ‘1’, halt processing any further binds triggered by the action

BIND_STACKRET

Used with BIND_WANTRET; allow stacked binds to continue despite receiving a ‘1’

Bind Return Values

The value returned by the bind is often matched against a desired value to return a ‘1’ (often used with BIND_WANTRET and BIND_STACKRET) to the calling function.

Value

Description

BIND_NOMATCH

The bind was not triggered due to not meeting the criteria set for the bind

BIND_AMBIGUOUS

The triggering action matched multiple non-stackable binds

BIND_MATCHED

The bind criteria was met, but the Tcl proc it tried to call could not be found

BIND_EXECUTED

The bind criteria was met and the Tcl proc was called

BIND_EXEC_LOG

The bind criteria was met, the Tcl proc was called, and Eggdrop logged the bind being called

BIND_QUIT

Sentinel value to signal that quit was triggered by the target leaving the partyline or filesys area. (Virtual bind to CMD_LEAVE)

Note: For a bind type to be stackable it needs to be registered with HT_STACKABLE AND check_tcl_bind must be called with BIND_STACKABLE.

C Binding

To create a C function that is called by the bind, Eggdrop provides the add_builtins function.

/* Add a list of C function callbacks to a bind
 * @param[in] tcl_bind_list_t *  the bind type (e.g. H_dcc)
 * @param[in] cmd_t *            a NULL-terminated table of binds:
 * cmd_t *mycmds = {
 *   {char *name, char *flags, IntFunc function, char *tcl_name},
 *   ...,
 *   {NULL, NULL, NULL, NULL}
 * };
 */
void add_builtins(tcl_bind_list_t *tl, cmd_t *cc) {
  char p[1024];
  cd_tcl_cmd tclcmd;

  tclcmd.name = p;
  tclcmd.callback = tl->func;
  for (i = 0; cc[i].name; i++) {
    /* Create Tcl command with automatic or given names *<bindtype>:<funcname>, e.g.
     * - H_raw {"324", "", got324, "irc:324"} => *raw:irc:324
     * - H_dcc {"boot", "t", cmd_boot, NULL} => *dcc:boot
     */
    egg_snprintf(p, sizeof p, "*%s:%s", tl->name, cc[i].funcname ? cc[i].funcname : cc[i].name);
    /* arbitrary void * can be included, we include C function pointer */
    tclcmd.cdata = (void *) cc[i].func;
    add_cd_tcl_cmd(tclcmd);
    bind_bind_entry(tl, cc[i].flags, cc[i].name, p);
  }
}

It automatically creates Tcl commands (e.g. *dcc:cmd_boot) that will call the C handler from add_bind_table in the first section Bind Table and it gets a context (void *) argument with the C function it is supposed to call (e.g. cmd_boot()).

Now we can actually look at the C function handler for dcc as an example and what it has to implement.

C Handler

The example handler for DCC looks as follows

/* Typical Tcl_Command arguments, just like e.g. tcl_putdcc is a Tcl/C command for [putdcc] */
static int builtin_dcc (ClientData cd, Tcl_Interp *irp, int argc, char *argv[]) {
  int idx;
  /* F: The C function we want to call, if the bind is okay, e.g. cmd_boot() */
  Function F = (Function) cd;

  /* Task of C function: verify argument count and syntax as any Tcl command */
  BADARGS(4, 4, " hand idx param");

  /* C Macro only used in C handlers for bind types, sanity checks the Tcl proc name
   * for *<bindtype>:<name> and that we are in the right C handler
   */
  CHECKVALIDITY(builtin_dcc);

  idx = findidx(atoi(argv[2]));
  if (idx < 0) {
      Tcl_AppendResult(irp, "invalid idx", NULL);
      return TCL_ERROR;
  }

  /* Call the desired C function, e.g. cmd_boot() with their arguments */
  F(dcc[idx].user, idx, argv[3]);
  Tcl_ResetResult(irp);
  Tcl_AppendResult(irp, "0", NULL);
  return TCL_OK;
}

This is finally the part where we see the arguments a C function gets for a DCC bind as opposed to a Tcl proc.

F(dcc[idx].user, idx, argv[3]):

  • User information as struct userrec *

  • IDX as int

  • The 3rd string argument from the Tcl call to *dcc:cmd_boot, which was $_dcc3 which was args to check_tcl_dcc which was everything after the dcc command

So this is how we register C callbacks for binds with the correct arguments

/* We know the return value is ignored because the return value of F
 * in builtin_dcc is ignored, so it can be void, but for other binds
 * it could be something else and used in the C handler for the bind.
 */
void cmd_boot(struct userrec *u, int idx, char *par) { /* snip */ }

cmd_t *mycmds = {
  {"boot", "t", (IntFunc) cmd_boot, NULL /* automatic name: *dcc:boot */},
  {NULL, NULL, NULL, NULL}
};
add_builtins(H_dcc, mycmds);

Summary

In summary, this is how the dcc bind is called:

  • check_tcl_dcc() creates Tcl variables $_dcc1 $_dcc2 $_dcc3 and lets check_tcl_bind call the binds

  • Tcl binds are done at this point

  • C binds mean the Tcl command associated with the bind is *dcc:boot which calls builtin_dcc which gets cmd_boot as ClientData cd argument

  • gbuildin_dcc performs some sanity checking to avoid crashes and then calls cmd_boot() aka F() with the arguments it wants C callbacks to have

Example edited and annotated gdb backtrace in :code::cmd_boot after doing .boot test on the partyline as user thommey with typical owner flags.

#0  cmd_boot (u=0x55e8bd8a49b0, idx=4, par=0x55e8be6a0010 "test") at cmds.c:614
    *u = {next = 0x55e8bd8aec90, handle = "thommey", flags = 8977024, flags_udef = 0, chanrec = 0x55e8bd8aeae0, entries = 0x55e8bd8a4a10}
#1  builtin_dcc (cd=0x55e8bbf002d0 <cmd_boot>, irp=0x55e8bd59b1c0, argc=4, argv=0x55e8bd7e3e00) at tclhash.c:678
    idx = 4
    argv = {0x55e8be642fa0 "*dcc:boot", 0x55e8be9f6bd0 "thommey", 0x55e8be7d9020 "4", 0x55e8be6a0010 "test", 0x0}
    F = 0x55e8bbf002d0 <cmd_boot>
#5  Tcl_Eval (interp=0x55e8bd59b1c0, script = "*dcc:boot $_dcc1 $_dcc2 $_dcc3") from /usr/lib/x86_64-linux-gnu/libtcl8.6.so
    Tcl: return $_dcc1 = "thommey"
    Tcl: return $_dcc2 = "4"
    Tcl: return $_dcc3 = "test"
    Tcl: return $lastbind = "boot" (set automatically by trigger_bind)
#8  trigger_bind (proc=proc@entry=0x55e8bd5efda0 "*dcc:boot", param=param@entry=0x55e8bbf4112b " $_dcc1 $_dcc2 $_dcc3", mask=mask@entry=0x55e8bd5efd40 "boot") at tclhash.c:742
#9  check_tcl_bind (tl=0x55e8bd5eecb0 <H_dcc>, match=match@entry=0x7ffcf3f9dac1 "boot", atr=atr@entry=0x7ffcf3f9d100, param=param@entry=0x55e8bbf4112b " $_dcc1 $_dcc2 $_dcc3", match_type=match_type@entry=80) at tclhash.c:942
    proc = 0x55e8bd5efda0 "*dcc:boot"
    mask = 0x55e8bd5efd40 "boot"
    brkt = 0x7ffcf3f9dac6 "test"
#10 check_tcl_dcc (cmd=cmd@entry=0x7ffcf3f9dac1 "boot", idx=idx@entry=4, args=0x7ffcf3f9dac6 "test") at tclhash.c:974
    fr = {match = 5, global = 8977024, udef_global = 0, bot = 0, chan = 0, udef_chan = 0}
#11 dcc_chat (idx=idx@entry=4, buf=<optimized out>, i=<optimized out>) at dcc.c:1068
    v = 0x7ffcf3f9dac1 "boot"
eggheads-eggdrop-9c00fe1/doc/html/modules/mod/000077500000000000000000000000001524730042700212635ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/html/modules/mod/assoc.html000066400000000000000000000236441524730042700232720ustar00rootroot00000000000000 Assoc Module — Eggdrop 1.10.2 documentation eggheads-eggdrop-9c00fe1/doc/html/modules/mod/blowfish.html000066400000000000000000000241471524730042700237760ustar00rootroot00000000000000 Blowfish Module — Eggdrop 1.10.2 documentation

Last revised: January 28, 2003

Blowfish Module

Eggdrop can encrypt your userfile, so users can have secure passwords. Please note that when you change your encryption method later (i.e. using other modules like a md5 module), you can’t use your current userfile anymore. Eggdrop will not start without an encryption module.

This module requires: none

Put this line into your Eggdrop configuration file to load the blowfish module:

loadmodule blowfish

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/channels.html000066400000000000000000000656741524730042700237660ustar00rootroot00000000000000 Channels Module — Eggdrop 1.10.2 documentation

Last revised: October 25, 2010

Channels Module

This module provides channel related support for the bot. Without it, you won’t be able to make the bot join a channel or save channel specific userfile information.

This module requires: none

Put this line into your Eggdrop configuration file to load the channels module:

loadmodule channels

There are also some variables you can set in your config file:

set chanfile “LamestBot.chan”

Enter here the filename where dynamic channel settings are stored.

set force-expire 0

Set this setting to 1 if you want your bot to expire bans/exempts/invites set by other opped bots on the channel.

set share-greet 0

Set this setting to 1 if you want your bot to share user greets with other bots on the channel if sharing user data.

set use-info 1

Set this setting to 1 if you want to allow users to store an info line.

set allow-ps 0

Set this setting to 1 if you want to allow both +p and +s channel modes to be enforced at the same time by the chanmode channel setting. Make sure your server supports +ps mixing or you may get endless mode floods.

channel add #channel { SETTINGS }

This command adds a static channel to your bot (it cannot be removed via the partyline- we recommend using the .+chan command on the partyline instead). This command will add the channel preconfigured with the specified settings. A full list of settings is defined in Channel Settings.

Channel Settings

There are two types of channel settings: value-based settings (where you configure a setting with a number or string), and enable/disable-based settings (where you turn a setting on or off). These settings can be configured via Tcl using the ‘channel set’ command:

channel set <chan> <setting>

This command modifies a specific channel setting for a channel. There are many different options for channels which you can define. Some settings are enabled or disabled by a plus or minus in front of them, and others directly take text or integer values.

Value-based Channel Settings

chanmode +/-<modes>

This setting makes the bot enforce channel modes. It will always enforce the +<modes> and remove the -<modes> modes.

idle-kick 0

This setting will make the bot check every minute for idle users. Set this to 0 to disable idle check.

stopnethack-mode 0

This setting will make the bot de-op anyone who enters the channel with serverops. There are seven different modes for this settings:

0

turn off

1

isoptest (allow serverop if registered op)

2

wasoptest (allow serverop if user had op before split)

3

allow serverop if isop or wasop

4

allow serverop if isop and wasop.

5

If the channel is -bitch, see stopnethack-mode 3

If the channel is +bitch, see stopnethack-mode 1

6

If the channel is -bitch, see stopnethack-mode 2

If the channel is +bitch, see stopnethack-mode 4

revenge-mode 0

This settings defines how the bot should punish bad users when revenging. There are four possible settings:

0

Deop the user.

1

Deop the user and give them the +d flag for the channel.

2

Deop the user, give them the +d flag for the channel, and kick them.

3

Deop the user, give them the +d flag for the channel, kick, and ban them.

ban-type 3

This setting defines what type of bans should eggdrop place for +k users or when revenge-mode is 3. Available types are:

0 *!user@host

1 *!*user@host

2 *!*@host

3 *!*user@*.host

4 *!*@*.host

5 nick!user@host

6 nick!*user@host

7 nick!*@host

8 nick!*user@*.host

9 nick!*@*.host

You can also specify types from 10 to 19 which correspond to types 0 to 9, but instead of using a * wildcard to replace portions of the host, only numbers in hostnames are replaced with the ‘?’ wildcard. Same is valid for types 20-29, but instead of ‘?’, the ‘*’ wildcard will be used. Types 30-39 set the host to ‘*’.

ban-time 120

Set here how long temporary bans will last (in minutes). If you set this setting to 0, the bot will never remove them.

exempt-time 60

Set here how long temporary exempts will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the exempts every X minutes, but will not remove the exempt if a ban is set on the channel that matches that exempt. Once the ban is removed, then the exempt will be removed the next time the bot checks. Please note that this is an IRCnet feature.

invite-time 60

Set here how long temporary invites will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the invites every X minutes, but will not remove the invite if a channel is set to +i. Once the channel is -i then the invite will be removed the next time the bot checks. Please note that this is an IRCnet feature.

aop-delay (minimum:maximum)

This is used for autoop, autohalfop, autovoice. If an op or voice joins a channel while another op or voice is pending, the bot will attempt to put both modes on one line.

aop-delay 0

No delay is used.

aop-delay X

An X second delay is used.

aop-delay X:Y

A random delay between X and Y is used.

need-op { putserv “PRIVMSG #lamest :op me cos i’m lame!” }

This setting will make the bot run the script enclosed in braces if it does not have ops. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don’t need to set this setting.

need-invite { putserv “PRIVMSG #lamest :let me in!” }

This setting will make the bot run the script enclosed in braces if it needs an invite to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don’t need to set this setting.

need-key { putserv “PRIVMSG #lamest :let me in!” }

This setting will make the bot run the script enclosed in braces if it needs the key to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don’t need to set this setting.

need-unban { putserv “PRIVMSG #lamest :let me in!” }

This setting will make the bot run the script enclosed in braces if it needs to be unbanned on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don’t need to set this setting.

need-limit { putserv “PRIVMSG #lamest :let me in!” }

This setting will make the bot run the script enclosed in braces if it needs the limit to be raised on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don’t need to set this setting.

flood-chan 15:60

Set here how many channel messages in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables text flood protection for the channel, where X is an integer >= 0.

flood-deop 3:10

Set here how many deops in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables deop flood protection for the channel, where X is an integer >= 0.

flood-kick 3:10

Set here how many kicks in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables kick flood protection for the channel, where X is an integer >= 0.

flood-join 5:60

Set here how many joins in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables join flood protection for the channel, where X is an integer >= 0.

flood-ctcp 3:60

Set here how many channel ctcps in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables ctcp flood protection for the channel, where X is an integer >= 0.

flood-nick 5:60

Set here how many nick changes in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables nick flood protection for the channel, where X is an integer >= 0.

Enable/Disable Channel Settings

These settings should be preceded by a + or - to enable or disable the setting, respctively.

enforcebans

When a ban is set, kick people who are on the channel and match the ban

dynamicbans

Only activate bans on the channel when necessary? This keeps the channel’s ban list from getting excessively long. The bot still remembers every ban, but it only activates a ban on the channel when it sees someone join who matches that ban.

userbans

Allow bans to be made by users directly? If turned off, the bot will require all bans to be made through the bot’s console.

dynamicexempts

Only activate exempts on the channel when necessary? This keeps the channel’s exempt list from getting excessively long. The bot still remembers every exempt, but it only activates a exempt on the channel when it sees a ban set that matches the exempt. The exempt remains active on the channel for as long as the ban is still active.

userexempts

Allow exempts to be made by users directly? If turned off, the bot will require all exempts to be made through the bot’s console.

dynamicinvites

Only activate invites on the channel when necessary? This keeps the channel’s invite list from getting excessively long. The bot still remembers every invite, but the invites are only activated when the channel is set to invite only and a user joins after requesting an invite. Once set, the invite remains until the channel goes to -i.

userinvites

Allow invites to be made by users directly? If turned off, the bot will require all invites to be made through the bot’s console.

autoop

Op users with the +o flag as soon as they join the channel This is insecure and not recommended.

autohalfop

Halfop users with the +l flag as soon as they join the channel? This is insecure and not recommended.

bitch

Only let users with the +o flag have op on the channel

greet

Say a user’s info line when they join the channel

protectops

Re-op a user with the +o flag if they get deopped?

protecthalfops

Re-halfop a user with the +l flag if they get dehalfopped

protectfriends

Re-op a user with the +f flag if they get deopped

statuslog

Log the channel status line every 5 minutes? This shows the bot’s status on the channel (op, voice, etc.), the channel’s modes, and the total number of members, ops, voices, regular users, and +b, +e, and +I modes on the channel. A sample status line follows:

[01:40] @#lamest (+istn) : [m/1 o/1 v/4 n/7 b/1 e/5 I/7]

revenge

Remember people who deop/kick/ban the bot, valid ops, or friends and punish them? Users with the +f flag are exempt from revenge.

revengebot

This is similar to to the ‘revenge’ option, but it only triggers if a bot gets deopped, kicked or banned.

autovoice

Voice users with the +v flag when they join the channel?

secret

Prevent this channel from being listed on the botnet?

shared

Share channel-related user info for this channel?

cycle

Cycle the channel when it has no ops?

dontkickops

Do you want the bot not to be able to kick users who have the +o flag, letting them kick-flood for instance to protect the channel against clone attacks?

inactive

This prevents the bot from joining the channel (or makes it leave the channel if it is already there). It can be useful to make the bot leave a channel without losing its settings, channel-specific user flags, channel bans, and without affecting sharing.

seen

Respond to seen requests in the channel? The seen module must be loaded for this to work.

nodesynch

Allow non-ops to perform channel modes? This can stop the bot from fighting with services such as ChanServ, or from kicking IRCops when setting channel modes without having ops.

static

Allow only permanent owners to remove the channel

Default Channel Values

The following settings are used as default values when you .+chan #chan or .tcl channel add #chan. You can modify these default values by adding the below values to your config fil. Look in the Channel Settings for an explanation of each option.

set default-flood-chan 15:60

set default-flood-deop 3:10

set default-flood-kick 3:10

set default-flood-join 5:60

set default-flood-ctcp 3:60

set default-flood-nick 5:60

set default-aop-delay 5:30

set default-idle-kick 0

set default-chanmode “nt”

set default-stopnethack-mode 0

set default-revenge-mode 0

set default-ban-type 3

set default-ban-time 120

set default-exempt-time 60

set default-invite-time 60

set default-chanset {
-autoop
-autovoice
-bitch
+cycle
+dontkickops
+dynamicbans
+dynamicexempts
+dynamicinvites
-enforcebans
+greet
-inactive
-nodesynch
-protectfriends
+protectops
-revenge
-revengebot
-secret
-seen
+shared
-statuslog
+userbans
+userexempts
+userinvites
-protecthalfops
-autohalfop
-static

}

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/compress.html000066400000000000000000000246511524730042700240140ustar00rootroot00000000000000 Compress Module — Eggdrop 1.10.2 documentation

Last revised: May 27, 2004

Compress Module

This module provides support for file compression. It can be used to compress files via Tcl or to transfer the userfile compressed during the share process, saving bandwidth.

This module requires: share

Put this line into your Eggdrop configuration file to load the compress module:

loadmodule compress

There are also some variables you can set in your config file:

set share-compressed 1

Allow compressed sending of user files? The user files are compressed with the compression level defined in ‘compress-level’.

set compress-level 9

This is the default compression level used. These levels are the same as those used by GNU gzip.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/console.html000066400000000000000000000246771524730042700236330ustar00rootroot00000000000000 Console Module — Eggdrop 1.10.2 documentation

Last revised: January 1, 2002

Console Module

This module provides storage of console settings when you exit the bot or type .store on the partyline.

This module requires: none

Put this line into your Eggdrop configuration file to load the console module:

loadmodule console

There are also some variables you can set in your config file:

set console-autosave 1

Save users console settings automatically? Otherwise, they have to use the .store command.

set force-channel 0

If a user doesn’t have any console settings saved, which channel do you want them automatically put on?

set info-party 0

Enable this setting if a user’s global info line should be displayed when they join a botnet channel.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/ctcp.html000066400000000000000000000254101524730042700231040ustar00rootroot00000000000000 CTCP Module — Eggdrop 1.10.2 documentation

Last revised: February 12, 2002

CTCP Module

This module provides the normal ctcp replies that you would expect. Without it loaded, CTCP CHAT will not work.

This module requires: server

Put this line into your Eggdrop configuration file to load the ctcp module:

loadmodule ctcp

There are also some variables you can set in your config file:

set ctcp-mode 0

Set here how the ctcp module should answer ctcps. There are 3 possible operating modes:

0

Normal behavior is used.

1

The bot ignores all ctcps, except for CHAT and PING requests by users with the +o flag.

2

Normal behavior is used, however the bot will not answer more than X ctcps in Y seconds (defined by ‘set flood-ctcp’).

There are also several variables to help make your bot less noticeable. They are: ctcp-version, ctcp-finger, and ctcp-userinfo. You can use set to set them to values you’d like.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/dns.html000066400000000000000000000262051524730042700227420ustar00rootroot00000000000000 DNS Module — Eggdrop 1.10.2 documentation

Last revised: September 26, 2010

DNS Module

This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections.

This module requires: none

Put this line into your Eggdrop configuration file to load the dns module:

loadmodule dns

There are also some variables you can set in your config file:

set dns-servers “8.8.8.8 8.8.4.4”

In case your bot has trouble finding dns servers or you want to use specific ones, you can set them here. The value is a list of dns servers. The relative order doesn’t matter. You can also specify a non-standard port. The default is to use the system specified dns servers. You don’t need to modify this normally.

set dns-cache 86400

Specify how long should the DNS module cache replies at maximum. The value must be in seconds. Note that it will respect the TTL of the reply and this is just an upper boundary.

set dns-negcache 600

Specify how long should the DNS module cache negative replies (NXDOMAIN, DNS Lookup failed). The value must be in seconds.

set dns-maxsends 4

How many times should the DNS module resend the query for a given domain if it receives no reply?

set dns-retrydelay 3

Specify how long should the DNS module wait for a reply before resending the query. The value must be in seconds.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/filesys.html000066400000000000000000000476721524730042700236470ustar00rootroot00000000000000 Filesys Module — Eggdrop 1.10.2 documentation

Last revised: Dec 30, 2017

Filesys Module

This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server.

This module requires: transfer

Config file setup

Put this line into your Eggdrop configuration file to load the filesys module:

loadmodule filesys

There are also some variables you can set in your config file:

set files-path “/home/mydir/eggdrop/filesys”

Set here the ‘root’ directory for the file system.

set incoming-path “/home/mydir/eggdrop/filesys/incoming”

If you want to allow uploads, set this to the directory uploads should be put into. Set this to “” if you don’t want people to upload files to your bot.

set upload-to-pwd 0

If you don’t want to have a central incoming directory, but instead want uploads to go to the current directory that a user is in, set this setting to 1.

set filedb-path “”

Eggdrop creates a ‘.filedb’ file in each subdirectory of your file area to keep track of its own file system information. If you can’t do that (for example, if the dcc path isn’t owned by you, or you just don’t want it to do that) specify a path here where you’d like all of the database files to be stored instead.

set max-file-users 20

Set here the maximum number of people that can be in the file area at once. Setting this to 0 makes it effectively infinite.

set max-filesize 1024

Set here the maximum allowable file size that will be received (in KB). Setting this to 0 makes it effectively infinite.

Partyline usage

.files

Moves you into the file transfer sub-system, if it has been enabled on this bot. From there you can browse through the files online and use dcc file transfers to download and upload.

.cancel <file> [file] …

Tells the bot to stop sending a file that is pending (either queued, waiting, or in the process of being transferred).

.cd <directory>

Changes your current directory if possible. this works exactly like the unix command.

.cp <source> <dst>

Copies a file or group of files from one place to another.

.desc <file> <description>

Changes the description for a file. if you are a master or file janitor, you can change the description for any file. otherwise you can only change the descriptions for files you have uploaded.

The description is restricted to 3 lines of 60 characters each, and is broken up between words. you can force a line break by putting a ‘|’ in the comment.

.filestats <user> [clear]

Reports on the users upload & download statistics. Optional argument ‘clear’ clears a users upload and download statistics.

.stats

Clears a users upload & download statistics.

.get <filename> [nickname]

Sends you the file(s) requested, over IRC. you should get a DCC SEND notice on IRC, and have your client accept it. if your nickname on IRC is different than the one you use on the bot, you should specify a nickname too. you can also use that to send files to other people. if a file you want is actually on another bot, it may take a little bit longer so be patient. if that bot isn’t on the botnet right now, it will say the file isn’t available.

There is a limit to the number of transfers you can have going simultaneously, so if you go over this limit, the remainder of your file requests will be queued. As the first files finish transferring, the queued files will be sent.

.hide <file> [files] …

Marks a file as hidden, so that normal users can’t see it. Only a master or file janitor using %b’lsa’%b can see hidden files.

.ln <bot:filepath> <localfile>

Creates a link to a file on another bot. The filepath has to be complete, like ‘/gifs/uglyman.gif’. If the bot is not connected to the botnet, nobody will be able to download the file until that bot connects again. The local filename can be anything you want.

Example: ln Snowbot:/gifs/uglyman.gif ugly.gif

.ls [filemask]

Displays the files in the current directory. Subdirectories are shown with “<DIR>” next to them, and other files will display their size (typically in kilobytes), who uploaded them (and when), and how many times each file has been downloaded. If a description of the file exists, it is displayed below the filename. You can restrict the file listing by specifying a mask, just like in unix.

.mkdir <dir> [flags [channel]]

Creates a subdirectory from this one, with the given name. If flags are specified, then those flags are required to enter or even see the directory. You can even specify a channel that the flags are matched against. You can use the %b’mkdir’%b command again to alter or remove those flags.

.mv <source> <dest>

Moves a file or group of files from one place to another (it can also be used to rename files).

.pending

Gives you a listing of every file you’ve requested which is still waiting, queued, or in the process of transferring. It shows you the nickname on IRC that the file is being sent to, and, if the transfer is in progress, tells you how far along the transfer is.

.pwd

Tells you what your current directory is.

.quit

Exits the file system.

rm <file> [files] …

Erase a file for good.

.rmdir <dir>

Removes an existing directory, if there are no files in it.

.share <file> [files] …

Marks a file as shared. This means that other bots can get the file remotely for users on their file systems. By default, files are marked as unshared.

.optimize

Cleans up the current directory’s database. If you have a large directory with many files you may want to use this command if you experience slow-downs/delays over time. Normally, the db should clean up itself though.

.unhide

Makes a file be not hidden any more.

.unshare <file> [file] …

Removes the shared tag from a file.

.filesys module

This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server.

The following commands are provided by the filesys module:

For filesystem users:

files

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/ident.html000066400000000000000000000341201524730042700232540ustar00rootroot00000000000000 Ident Module — Eggdrop 1.10.2 documentation

Last revised: June 27, 2019

Ident Module

This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon.

Traditionally, an IRC server queries port 113 on an irc client’s host to determine the ident for the client. The client’s host normally replies with the username of the account. If the host machine is running oident as its ident service, Eggdrop can now interact with oident to instead reply with a custom ident specified by Eggdrop (oidentd is an external service not specific to Eggdrop nor maintained by Eggheads developers, thus no support for it can be given).

Alternatively, this module can also allow Eggdrop to act as its own ident daemon, answering queries from the IRC server directly. While this option may be of use to a very specific group of people, it requires additional external configuration via root access. Because most modern OSs do not allow user-level programs to bind to port 113, required by this option, a user must either:

  • Allow Eggdrop to bind to ports <1024 via the command:

    sudo setcap "cap_net_bind_service=+ep" /path/to/eggdrop
    

    Warning: this will allow Eggdrop to any port below 1024, potentially allowing a user to impersonate a standard system service with Eggdrop

  • Use iptables to re-route ident queries destined for port 113 to a >1024 port that Eggdrop can bind to, for example by running:

    sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport \
    113 -j REDIRECT --to-port <ident port in config>'
    

Additionally, using Eggdrop as a built-in ident daemon can cause issues when running multiple bots from the same account. The ident server is only activated and bound to the configured ident port while Eggdrop is connecting to an IRC server, but only one Eggdrop can bind to port 113 at a time. If multiple bots were to start at the same time, such as after a netsplit or host reboot, only one bot can bind to port 113 at a time, creating ident conflicts. Thus, this option is best suited for single-bot environments.

This module requires:
  • oident to be running on your shell host, configured to allow user spoofing, for the oidentd method.

Put this line into your Eggdrop configuration file to load the ident module:

loadmodule ident

There are also some variables you can set in your config file:

set ident-method 0
This sets which ident method you wish to use:
0 = oidentd / Your bot will overwrite $HOME/.oidentd.conf right before

opening the socket to the IRC server with the global reply.

1 = Builtin / Your bot will automatically turn its builtin identd on and

off when needed so it shouldn’t conflict with other identds that do the same. Ident port 113 is normally restricted to privileged processes on UNIX systems.

set ident-port 113

This sets the port which port Eggdrop will attempt to bind to for option 1 of ident-method. Most OSs do not allow non-privileged programs such as Eggdrop to bind to ports <1024; please read the beginning of this document for potential ways to implement this setting.

Copyright (C) 2019 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/irc.html000066400000000000000000000400601524730042700227260ustar00rootroot00000000000000 IRC Module — Eggdrop 1.10.2 documentation

Last revised: August 21, 2004

IRC Module

This module controls the bots interaction on IRC. It allows the bot to join channels, etc. You have to load this if you want your bot to come on irc.

This module requires: server, channels

Put this line into your Eggdrop configuration file to load the irc module:

loadmodule irc

There are also some variables you can set in your config file:

set bounce-bans 1

Set this to 1 if you want to bounce all server bans.

set bounce-exempts 0

Set this to 1 if you want to bounce all server exemptions (+e modes). This is disabled if use-exempts is disabled.

set bounce-invites 0

Set this to 1 if you want to bounce all server invitations (+I modes). This is disabled if use-invites is disabled.

set bounce-modes 0

Set this to 1 if you want to bounce all server modes.

set max-modes 30

There is a global limit for +b/+e/+I modes. This limit should be set to the same value as max-bans for networks that do not support +e/+I.

set max-bans 30

Set here the maximum number of bans you want the bot to set on a channel. Eggdrop will not place any more bans if this limit is reached. Undernet currently allows 45 bans, IRCnet allows 30, EFnet allows 100, and DALnet allows 100.

set max-exempts 20

Set here the maximum number of exempts you want Eggdrop to set on a channel. Eggdrop will not place any more exempts if this limit is reached.

set max-invites 20

Set here the maximum number of invites you want Eggdrop to set on a channel. Eggdrop will not place any more invites if this limit is reached.

set use-exempts 0
set use-invites 0

These settings should be left commented unless the default values are being overridden. By default, exempts and invites are on for EFnet and IRCnet, but off for all other large networks. This behavior can be modified with the following 2 flags. If your network doesn’t support +e/+I modes then you will be unable to use these features.

set learn-users 0

If you want people to be able to add themselves to the bot’s userlist with the default userflags (defined above in the config file) via the ‘hello’ msg command, set this to 1.

set wait-split 600

Set here the time (in seconds) to wait for someone to return from a netsplit (i.e. wasop will expire afterwards). Set this to 1500 on IRCnet since its nick delay stops after 30 minutes.

set wait-info 180

Set here the time (in seconds) that someone must have been off-channel before re-displaying their info line.

set mode-buf-length 200

Set this to the maximum number of bytes to send in the arguments of modes sent to the server. Most servers default this to 200.

unbind msg - hello *msg:hello
bind msg - myword *msg:hello

Many IRCops find bots by seeing if they reply to ‘hello’ in a msg. You can change this to another word by un-commenting these two lines and changing “myword” to the word wish to use instead of’hello’. It must be a single word.

unbind msg - ident *msg:ident
unbind msg - addhost *msg:addhost Many takeover attempts occur due to lame users blindly /msg ident’ing to

the bot and attempting to guess passwords. We now unbind this command by default to discourage them. You can enable this command by un-commenting these two lines.

set opchars “@”
#set opchars “@&~”

Some IRC servers are using some non-standard op-like channel prefixes/modes. Define them here so the bot can recognize them. Just “@” should be fine for most networks. Un-comment the second line for some UnrealIRCds.

set no-chanrec-info 0

If you are so lame you want the bot to display peoples info lines, even when you are too lazy to add their chanrecs to a channel, set this to 1. NOTE This means every user with an info line will have their info line displayed on EVERY channel they join (provided they have been gone longer than wait-info).

These were the core irc module settings. There are more settings for ‘net-type’ IRCnet and Efnet. net-type has to be set in the server module config section.

Use the following settings only if you set ‘net-type’ to IRCnet!

set prevent-mixing 1

At the moment, the current IRCnet IRCd version (2.10) doesn’t support the mixing of b, o and v modes with e and I modes. This might be changed in the future, so use 1 at the moment for this setting.

Use the following settings only if you set ‘net-type’ to Efnet!

set kick-method 1

If your network supports more users per kick command then 1, you can change this behavior here. Set this to the number of users to kick at once, or set this to 0 for all at once.

set modes-per-line 3

Some networks allow you to stack lots of channel modes into one line. They’re all guaranteed to support at least 3, so that’s the default. If you know your network supports more, you may want to adjust this. This setting is limited to 6, although if you want to use a higher value, you can modify this by changing the value of MODES_PER_LINE_MAX in src/chan.h and recompiling the bot.

set include-lk 1

Some networks don’t include the +l limit and +k or -k key modes in the modes-per-line (see above) limitation. Set include-lk to 0 for these networks.

set use-354 0

Set this to 1 if your network uses IRCu2.10.01 specific /who requests. Eggdrop can, therefore, ask only for exactly what’s needed.

set rfc-compliant 1

If your network doesn’t use rfc 1459 compliant string matching routines, set this to 0.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/notes.html000066400000000000000000000255611524730042700233120ustar00rootroot00000000000000 Notes Module — Eggdrop 1.10.2 documentation

Last revised: January 1, 2002

Notes Module

This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval.

This module requires: none

Put this line into your Eggdrop configuration file to load the notes module:

loadmodule notes

There are also some variables you can set in your config file:

set notefile “LamestBot.notes”

Set here the filename where private notes between users are stored.

set max-notes 50

Set here the maximum number of notes to allow to be stored for each user (to prevent flooding).

set note-life 60

Set here how long (in days) to store notes before expiring them.

set allow-fwd 0

Set this to 1 if you want to allow users to specify a forwarding address for forwarding notes to another account on another bot.

set notify-users 1

Set this to 1 if you want the bot to let people know hourly if they have any notes.

set notify-onjoin 1

Set this to 1 if you want the bot to let people know on join if they have any notes.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/pbkdf2.html000066400000000000000000000262541524730042700233320ustar00rootroot00000000000000 PBKDF2 Module — Eggdrop 1.10.2 documentation

Last revised: October 28, 2020

PBKDF2 Module

Eggdrop encrypts its userfile, so users can have secure passwords. Eggdrop will not start without an encryption module.

As of Eggdrop 1.9.0, a new encryption module was added, the PBKDF2 module. The PBKDF2 module was designed to work with the previous encryption module (blowfish) to make the transition easier. If this is a new bot, you can safely load just the PBKDF2 module. If you are transitioning a userfile from 1.8 or earlier, you should load this AND the blowfish module. By doing so, Eggdrop will seamlessly update the old blowfish hashes to the new PBKDF2 hashes once a user logs in for the first time, and allow you to (eventually) remove the blowfish module altogether.

Outside of the specific case noted above, please note that if you change your encryption method later (i.e. using other modules like a rijndael module), you can’t use your current userfile anymore.

Put this line into your Eggdrop configuration file to load the server module::

loadmodule pbkdf2

There are also some variables you can set in your config file:

set pbkdf2-method “SHA256”

Cryptographic hash function used. Use openssl list -digest-algorithms to view all available options

set pbkdf2-rounds 1600

Number of rounds. The higher the number, the longer hashing takes; but also generally the higher the protection against brute force attacks.

This module requires: none

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/python.html000066400000000000000000000324601524730042700234770ustar00rootroot00000000000000 Python Module — Eggdrop 1.10.2 documentation

Last revised: November 03, 2023

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

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

Partyline Commands

python <expression>

You can run a python command from the partyline with the .python command, such as:

.python 1 + 1
Python: 2
.python from eggdrop.tcl import putmsg; putmsg('#chan', 'Hello world!')
Python: None

.binds python

The python module extends the core .binds partyline command by adding a python mask. This command will list all binds for python scripts.

Tcl Commands

pysource <path/to/file>

The pysource command is analogous to the Tcl source command, except that it loads a Python script into Eggdrop instead of a Tcl script.

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/seen.html000066400000000000000000000243251524730042700231110ustar00rootroot00000000000000 Seen Module — Eggdrop 1.10.2 documentation

Last revised: Mar 14, 2023

Seen Module

This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot’s userlist. If you are looking for a better and more advanced seen module, try the gseen module originally written by G’Quann and forked/updated to work with modern Eggdrop by mortmann. You can find it at https://github.com/michaelortmann/gseen.mod.

This module requires: none

Put this line into your Eggdrop configuration file to load the seen module:

loadmodule seen

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/server.html000066400000000000000000000530511524730042700234630ustar00rootroot00000000000000 Server Module — Eggdrop 1.10.2 documentation

Last revised: October 25, 2010

Server Module

This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define.

This module requires: none

Put this line into your Eggdrop configuration file to load the server module:

loadmodule server

There are also some variables you can set in your config file:

set net-type Efnet

What is your network? Possible allowed values are Efnet, IRCnet, Undernet, DALnet, Libera, freenode, Quakenet, Rizon, Other. If the network you use is not listed, using “Other” is a good sane choice and can be customized with settings both here and in the IRC module sections of the config file.

set nick “LamestBot”

Set the nick the bot uses on IRC, and on the botnet unless you specify a separate botnet-nick, here.

set altnick “L?m?stB?t”

Set the alternative nick which the bot uses on IRC if the nick specified by ‘set nick’ is unavailable. All ‘?’ characters will be replaced by random numbers.

set keep-nick 1

This setting makes the bot try to get his original nickname back if its primary nickname is already in use.

set realname “/msg LamestBot hello”

Set here what to display in the real-name field for the bot.

bind evnt - init-server evnt:init_server
proc evnt:init_server {type} {
  global botnick
  putquick "MODE $botnick +i-ws"
}

… is Tcl scripting that is run immediately after connecting to a server.

set init-server { putquick "MODE $botnick +i-ws" }

… is Tcl scripting that is run immediately after connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc).

set connect-server { putlog "Connecting to server." }

… is Tcl scripting that is run immediately before connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc).

set disconnect-server { putlog "Disconnected from server." }

… is a Tcl script that is run immediately after disconnecting from a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc).

set servers {
  you.need.to.change.this:6667
  another.example.com:7000:password
  [2001:db8:618:5c0:263::]:6669:password
  ssl.example.net:+6697
}

… is the bot’s server list. The bot will start at the first server listed, and cycle through them whenever it gets disconnected. You need to change these servers to YOUR network’s servers.

The format is:

server[:port[:password]]

Prefix the port with a plus sign to attempt a SSL connection:

server:+port[:password]

Both the port and password fields are optional; however, if you want to set a password or use SSL you must also set a port. If a port isn’t specified it will default to your default-port setting.

set default-port 6667

Set the default port which should be used if none is specified with ‘.jump’ or in ‘set servers’.

set msg-rate 2

Number of seconds to wait between transmitting queued lines to the server. Lower this value at your own risk. ircd is known to start flood control at 512 bytes/2 seconds.

set ssl-verify-servers 0

Control certificate verification for servers. You can set this by adding together the numbers for all exceptions you want to enable. By default certificate verification is disabled and all certificates are assumed to be valid. The numbers are the following:

The numbers are the following:

0

disable verification

1

enable certificate verification

2

allow self-signed certificates

4

don’t check peer common or alt names

8

allow expired certificates

16

allow certificates which are not valid yet

32

allow revoked certificates

set server-cycle-wait 60

This setting defines how long Eggdrop should wait before moving from one server to another on disconnect. If you set 0 here, Eggdrop will not wait at all and will connect instantly. Setting this too low could result in your bot being K:Lined.

set server-timeout 60

Set here how long Eggdrop should wait for a response when connecting to a server before giving up and moving on to next server.

set check-stoned 1

Set this to 1 if Eggdrop should check for stoned servers? (where the server connection has died, but Eggdrop hasn’t been notified yet).

set serverror-quit 1

If you want your bot to exit the server if it receives an ERROR message, set this to 1.

set max-queue-msg 300

Set here the maximum number of lines to queue to the server. If you’re going to dump large chunks of text to people over IRC, you will probably want to raise this. 300 is fine for most people though.

set quiet-reject 1

This setting makes the bot squelch the error message when rejecting a DCC CHAT, SEND or message command. Normally, Eggdrop notifies the user that the command has been rejected because they don’t have access. Note that sometimes IRC server operators detect bots that way.

set flood-msg 5:60

Set here how many msgs in how many seconds from one host constitutes a flood. If you set this to 0:0, msg flood protection will be disabled.

set flood-ctcp 3:60

Set here how many ctcps in how many seconds from one host are recognized as a flood. Not specifying or using number = 0 will not check against ctcp floods.

set answer-ctcp 3

Set how many ctcps should be answered at once here.

set lowercase-ctcp 0

If you want your bot to answer lower case ctcp requests (non rfc- compliant), set this setting to 1. mIRC will do this, most other clients will not.

set trigger-on-ignore 0

If you want Eggdrop to trigger binds for ignored users, set this to 1.

set exclusive-binds 0

This setting configures PUBM and MSGM binds to be exclusive of PUB and MSG binds. This means if a MSGM bind with the mask “help” exists and is triggered, any MSG bindings with “help” in their mask will not be triggered. Don’t enable this unless you know what you are doing!

set double-mode 0

Allow identical messages in the mode queue?

set double-server 0

Allow identical messages in the server queue?

set double-help 0

Allow identical messages in the help queue?

set use-penalties 1 (default on net-type IRCnet)

This enables Eggdrop’s penalty calculation. Every command Eggdrop sends to the IRC server raises its penalty points. If Eggdrop reaches a server limit, it gets disconnected with “excess flood” message. Eggdrop is able to count internal those penalty points, too and take measures against excess flooding. Note: it’s highly advised to turn this on!

set optimize-kicks 1

This optimizes the kick queue. It also traces nick changes and parts in the channel and changes the kick queue accordingly. There are three different options for this setting:

0

Turn it off.

1

Optimize the kick queue by summarizing kicks.

2

Trace nick changes and parts on the channel and change the queue accordingly. For example, bot will not try to kick users who have already parted the channel.

ATTENTION: Setting 2 is very CPU intensive.

There are additional settings for ‘net-type’ Efnet.

net-type 5 specific features:

Attention: Use this settings only if you set ‘net-type’ to Efnet!

set check-mode-r 1

This settings defines how umode +r is understood by Eggdrop. Some networks use +r to indicate a restricted connection. If this is your case, and you want your bot to leave restricted servers and jump to the next server on its list, then set it to 1. Please note, this setting is automatically set to 0 for net-type of Efnet, Undernet, and DALnet, and set to 1 for net-type IRCNet.

set nick-len 9

This setting allows you to specify the maximum nick-length supported by your network. The default setting is 9. The maximum supported length by Eggdrop is 32.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/share.html000066400000000000000000000267101524730042700232610ustar00rootroot00000000000000 Share Module — Eggdrop 1.10.2 documentation

Last revised: November 27, 2003

Share Module

This module provides userfile sharing support between two directly linked bots.

This module requires: transfer, channels

Put this line into your Eggdrop configuration file to load the share module:

loadmodule share

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.

set resync-time 900

This setting specifies how long to hold another bots resync data before flushing it. This is a setting for allow-resync.

set private-global 0

When sharing user lists, DON’T ACCEPT global flag changes from other bots? NOTE: The bot will still send changes made on the bot, it just won’t accept any global flag changes from other bots. This overrides the private-globals setting (below).

set private-globals “mnot”

When sharing user lists, if private-global isn’t set, which global flag changes from other bots should be ignored?

set private-user 0

When sharing user lists, don’t accept ANY userfile changes from other bots? Paranoid people should use this feature on their hub bot. This will force all userlist changes to be made via the hub.

set override-bots 0

This setting makes the bot discard its own bot records in favor of the ones sent by the hub. NOTE: No passwords or botflags are shared, only ports and address are added to sharing procedure. This only works with hubs that are v1.5.1 or higher.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/transfer.html000066400000000000000000000253721524730042700240060ustar00rootroot00000000000000 Transfer Module — Eggdrop 1.10.2 documentation

Last revised: November 29, 2023

Transfer Module

The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing.

This module requires: none

Put this line into your Eggdrop configuration file to load the transfer module:

loadmodule transfer

There are also some variables you can set in your config file:

set max-dloads 3

Set here the maximum number of simultaneous downloads to allow for each user.

set dcc-block 0

Set here the block size for dcc transfers. ircII uses 512 bytes, but admits that may be too small. 1024 is standard these days. Set this to 0 to use turbo-dcc (recommended).

set xfer-timeout 30

Set here the time (in seconds) to wait before an inactive transfer times out.

set sharefail-unlink 1

By default, Eggdrop will abort the linking process if userfile sharing is enabled but the userfile transfer fails. Set this to 0 to keep the bots linked if the userfile transfer fails and retry every minute (both bots must be v1.9.0 or higher).

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/twitch.html000066400000000000000000000306061524730042700234600ustar00rootroot00000000000000 Twitch Module — Eggdrop 1.10.2 documentation

Last revised: April 20, 2020

Twitch Module

This module attempts to provide connectivity with the Twitch gaming platform. While Twitch provides an IRC gateway to connect with it’s streaming service, it does not claim to (and certainly does not) follow the IRC RFC in any meaningful way. The intent of this module is not to provide the full spectrum of management functions typically associated with Eggdrop; instead it focuses around the following key functions:

  • Logging of general and Twitch-specific events (raids, blocks, bit donations)

  • Tracking userstate and roomstate values

  • Adding Tcl event binds for a number of Twitch events

This module requires: irc.mod

Put this line into your Eggdrop configuration file to load the twitch module:

loadmodule twitch

and set net-type “Twitch” in your config file.

Limitations

There are a few things you should know about how Twitch provides service through the IRC gateway that affects how well Eggdrop can function: * Twitch does not broadcast JOINs or PARTs for channels over 1,000 users. This renders tracking users on a channel unreliable. * Twitch does not broadcast MODE changes for moderator status. This (mostly) renders tracking the status of users infeasible. (See Tcl below section for workaround) * Twitch stores bans on its servers (and does not accept MODE +b), making the Eggdrop ban list (and exempts/invites) mostly useless * Twitch does not allow clients to issue MODE +o/-o commands, preventing Eggdrop from op’ing users through the traditional method

… but the good news is, we have extended much of the Twitch functionality to Tcl!

Tcl API

That last section was a little bit of a downer, but don’t worry, we added a TON of functionality to the Tcl API. This module adds binds for the following Twitch events:

  • CLEARCHAT

  • CLEARMSG

  • HOSTTARGET

  • WHISPER

  • ROOMSTATE

  • USERSTATE

  • USERNOTICE

Partyline commands

This module adds the following commands to the partyline: * userstate - Lists current userstate on a channel * roomsstate - Lists current roomstate for a channel * twcmd - Issues a traditional Twitch web interface command to the Twitch server (/ban, /block, /host, etc)

Copyright (C) 2020 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/uptime.html000066400000000000000000000250321524730042700234560ustar00rootroot00000000000000 Uptime Module — Eggdrop 1.10.2 documentation

Last revised: Mar 14, 2023

Uptime Module

This module reports uptime statistics to the uptime contest web site at https://www.eggheads.org/uptime. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn’t listed, try again later.

Information sent to the server includes the bot’s uptime, botnet-nick, server, version, and IP address. This information is stored in a temporary logfile for debugging purposes only. The only publicly available information will be the bot’s botnet-nick, version and uptime. If you do not wish for this information to be sent, comment out the ‘loadmodule uptime’ line in your bot’s config file.

This module requires: server

Put this line into your Eggdrop configuration file to load the uptime module:

loadmodule uptime

Copyright (C) 2001 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/modules/mod/woobie.html000066400000000000000000000236271524730042700234470ustar00rootroot00000000000000 Woobie Module — Eggdrop 1.10.2 documentation eggheads-eggdrop-9c00fe1/doc/html/modules/writing.html000066400000000000000000001060531524730042700230620ustar00rootroot00000000000000 How to Write an Eggdrop Module — Eggdrop 1.10.2 documentation

How to Write an Eggdrop Module

Note: This is for a simple module of 1 source file.

  1. Create a src/mod/MODULE.mod directory in your Eggdrop directory (where MODULE is the module name) and cd to it.

  2. Copy the file ‘Makefile’ from src/mod/woobie.mod and replace all occurrences of ‘woobie’ with your module name. This should ensure that your module gets compiled.

  3. Next, you want to create a file called MODULE.c (MODULE is the module name again).

  4. You MUST include the following in your source code:

    #define MODULE_NAME "module-name"
    

This should be defined to the same name you will be using when you load your module.

#define MAKING_MODULENAME

MODULENAME is the name of your module (MODULE_NAME), but in all caps.

#include "../module.h"

This provides access to Eggdrop’s global function table. Examine src/mod/module.h closely to find a list of functions available.

#include any other standard c header files you might need.

Note that stdio.h, string.h, stdlib.h, and sys/types.h are already included.

Function *global;

This variable provides access to all the Eggdrop functions; without it, you can’t call any Eggdrop functions (the module won’t even load).

Module requirements

In most modules, all functions/variables (except global and MODULE_start) should be static. This will drastically reduce the size of modules on decent systems.

Throughout this step, MODULE refers to the module name. Note that “MODULE_NAME” should literally be “MODULE_NAME”.

MODULE_start

char *MODULE_start(Function *func_table)

This function is called when the module is first loaded. There are several things that need to be done in this function

global = func_table;

This allows you to make calls to the global function table.

module_register(MODULE_NAME, MODULE_table, MAJOR, MINOR);

This records details about the module for other modules and Eggdrop itself to access. MAJOR and MINOR are ints, where MAJOR is the module’s major version number and MINOR is a minor version number. MODULE_table is a function table (see below).

module_depend(MODULE_NAME, "another-module", MAJOR, MINOR);

This lets Eggdrop know that your module NEEDS “another-module” of major version ‘MAJOR’ and at least minor version ‘MINOR’ to run, and hence should try to load it if it’s not already loaded. This will return 1 on success, or 0 if it can’t be done (at which stage you should return an error).

Any other initialization stuff you desire should also be included in this function. See below for various things you can do.

You also will need to return a value. Returning NULL implies the module loaded successfully. Returning a non-NULL STRING is an error message. The module (and any other dependent modules) will stop loading and an error will be returned.

MODULE_table

static Function *MODULE_table = {
       MODULE_start,
       MODULE_close,
       MODULE_expmem,
       MODULE_report,
       any_other_functions,
       you_want_to_export
};

This is a table of functions which any other module can access. The first 4 functions are FIXED. You MUST have them; they provide important module information.

MODULE_close ()

static char *MODULE_close ()

This is called when the module is unloaded. Apart from tidying any relevant data (I suggest you be thorough, we don’t want any trailing garbage from modules), you MUST do the following:

module_undepend(MODULE_NAME);

This lets Eggdrop know your module no longer depends on any other modules.

Return a value. NULL implies success; any non-NULL STRING implies that the module cannot be unloaded for some reason, and hence the bot should not unload it (see the blowfish module for an example).

MODULE_expmem

static int MODULE_expmem ()

This should tally all memory you allocate/deallocate within the module
(using nmalloc, nfree, etc) in bytes. It's used by memory debugging to
track memory faults, and it is used by .status to total up memory usage.

MODULE_report

static void MODULE_report (int idx)

This should provide a relatively short report of the module’s status (for the module and status commands).

These functions are available to modules. MANY more available functions can be found in src/mod/module.h.

Additional functions

nmalloc

void *nmalloc(int j);

This allocates j bytes of memory.

nfree

void nfree(void *a);

This frees an nmalloc’d block of memory.

Context

Context;

Actually a macro – records the current position in execution (for debugging). Using Context is no longer recommended, because it uses too many resources and a core file provides much more information.

dprintf

void dprintf(int idx, char *format, ...)

This acts like a normal printf() function, but it outputs to log/socket/idx. idx is a normal dcc idx, or if < 0 is a sock number.

Other destinations:

DP_LOG    - send to log file
DP_STDOUT - send to stdout
DP_MODE   - send via mode queue to the server
DP_SERVER - send via normal queue to the server
DP_HELP   - send via help queue to server

module_entry

const module_entry *module_find(char *module_name, int major, int minor);

Searches for a loaded module (matching major, >= minor), and returns info about it.

Members of module_entry:

char *name;      - module name
int major;       - real major version
int minor;       - real minor version
Function *funcs; - function table (see above)

module_rename

void module_rename(char *old_module_name, char *new_module_name)

This renames a module from old_module_name to new_module_name.

add_hook/del_hook

void add_hook(int hook_num, Function *funcs)
void del_hook(int hook_num, Function *funcs)

These are used for adding or removing hooks to/from Eggdrop code that are triggered on various events. Valid hooks are:

HOOK_SECONDLY   - called every second
HOOK_MINUTELY   - called every minute
HOOK_5MINUTELY  - called every 5 minutes
HOOK_HOURLY     - called every hour (hourly-updates minutes past)
HOOK_DAILY      - called when the logfiles are switched

HOOK_READ_USERFILE - called when the userfile is read
HOOK_USERFILE      - called when the userfile is written
HOOK_PRE_REHASH    - called just before a rehash
HOOK_REHASH        - called just after a rehash
HOOK_IDLE          - called whenever the dcc connections have been
                     idle for a whole second
HOOK_BACKUP        - called when a user/channel file backup is done
HOOK_LOADED        - called when Eggdrop is first loaded
HOOK_DIE           - called when Eggdrop is about to die

module_load/unload

char *module_unload (char *module_name);
char *module_load (char *module_name);

Tries to load or unload the specified module; returns 0 on success, or an error message.

add/rem_tcl_commands

void add_tcl_commands(tcl_cmds *tab);
void rem_tcl_commands(tcl_cmds *tab);

Provides a quick way to create and remove a table of Tcl commands. The table is in the form of:

{char *func_name, Function *function_to_call}

Use { NULL, NULL } to indicate the end of the list.

add/rem_tcl_ints

void add_tcl_ints(tcl_ints *);
void rem_tcl_ints(tcl_ints *);

Provides a quick way to create and remove a table of links from C int variables to Tcl variables (add_tcl_ints checks to see if the Tcl variable exists and copies it over the C one). The format of table is:

{char *variable_name, int *variable, int readonly}

Use {NULL, NULL, 0} to indicate the end of the list.

add/rem_tcl_strings

void add_tcl_strings(tcl_strings *);
void rem_tcl_strings(tcl_strings *);

Provides a quick way to create and remove a table of links from C string variables to Tcl variables (add_tcl_ints checks to see if the Tcl variable exists and copies it over the C one). The format of table is:

{char *variable_name, char *string, int length, int flags}

Use {NULL, NULL, 0, 0} to indicate the end of the list. Use 0 for length if you want a const string. Use STR_DIR for flags if you want a ‘/’ constantly appended; use STR_PROTECT if you want the variable set in the config file, but not during normal usage.

add/rem_builtins

void add_builtins(p_tcl_hash_list table, cmd_t *cc);
void rem_builtins(p_tcl_hash_list table, cmd_t *cc);

This adds binds to one of Eggdrop’s bind tables. The format of the table is:

{char *command, char *flags, Function *function, char *displayname}

Use {NULL, NULL, NULL, NULL} to indicate the end of the list.

This works EXACTLY like the Tcl ‘bind’ command. displayname is what Tcl sees this function’s proc name as (in .binds all).

function is called with exactly the same args as a Tcl binding is with type conversion taken into account (e.g. idx’s are ints). Return values are much the same as Tcl bindings. Use int 0/1 for those which require 0/1, or char * for those which require a string (auch as filt). Return nothing if no return value is required.

putlog

void putlog (int logmode, char *channel, char *format, ...)

Adds text to a logfile (determined by logmode and channel). This text will also output to any users’ consoles if they have the specified console mode enabled.

What to do with a module?

If you have written a module and feel that you wish to share it with the rest of the Eggdrop community, find us in #eggdrop on Libera. Make sure you have a nice descriptive text (modulename.desc) to describe it, and make sure to mention in your text file which version Eggdrop the module is written for.

eggheads-eggdrop-9c00fe1/doc/html/objects.inv000066400000000000000000000032711524730042700212060ustar00rootroot00000000000000# Sphinx inventory version 2 # Project: Eggdrop # Version: 1.10.2 # The remainder of this file is compressed using zlib. xڕXn:+4˪( 8iMqEQ4EDdJ 8;|D&g>gx HCTp0۩}&fmDW-n(cN0E>pl*,%kP״հ} -Ҹ Zد]T(oGCk avP/P]odB9nچ/FOo4S:vtb'!ik7&~պ&w~s<A:~RI=:#n:Vd9,GY]{U\#], x'Gjc>{ x譋w-5{M{ J_N,ة:O~nzZ.,n*PK6n{/ƕhW\Qz0G]Ow FNVӶʒ./:%|qfOs c ,>ACdX7:-;L+jMv&jʭJ(6[ˤRpO?=VP#U 4->nZ/Wm~W0L;vY CC8.ш80/`&w3q$w4Lh X(jy8CG:Sッ i*UTcszSHhF9(1%2SVb-)IF4DAS2Տ"זDY&BR{&#&%x5SIi힔"U~~)Z\qZ7!}z-՗Ϸf9BEC62!)-2h?,ZPQkg09Żle\n~K9&X4ױ +/z^ ǡ֬a㧍pS͞,;GOQ݁ <ᚃ%=Ydt^~P ! ȚWZE_(m-w 6'Qn  2y Y[KO2f `wܘy^ȷ<+`G icY dfU$%kD_DN" b#l*#hSpr  \%72~CYtWnȂ3},1hk[(/^RtJA8U cq0GB; t:Z? j4 I9nY7}; Search — Eggdrop 1.10.2 documentation eggheads-eggdrop-9c00fe1/doc/html/searchindex.js000066400000000000000000003251731524730042700217020ustar00rootroot00000000000000Search.setIndex({"alltitles":{".binds python":[[21,"binds-python"]],".cancel [file] \u2026":[[16,"cancel-file-file"]],".cd ":[[16,"cd-directory"]],".cp ":[[16,"cp-source-dst"]],".desc ":[[16,"desc-file-description"]],".files":[[16,"files"]],".filestats [clear]":[[16,"filestats-user-clear"]],".filesys module":[[16,"id1"]],".get [nickname]":[[16,"get-filename-nickname"]],".hide [files] \u2026":[[16,"hide-file-files"]],".ln ":[[16,"ln-bot-filepath-localfile"]],".ls [filemask]":[[16,"ls-filemask"]],".mkdir [flags [channel]]":[[16,"mkdir-dir-flags-channel"]],".mv ":[[16,"mv-source-dest"]],".optimize":[[16,"optimize"]],".pending":[[16,"pending"]],".pwd":[[16,"pwd"]],".quit":[[16,"quit"]],".rmdir ":[[16,"rmdir-dir"]],".share [files] \u2026":[[16,"share-file-files"]],".stats":[[16,"stats"]],".unhide":[[16,"unhide"]],".unshare [file] \u2026":[[16,"unshare-file-file"]],"About":[[43,"about"],[44,"about"],[51,"about"]],"About Eggdrop":[[0,null],[2,null]],"Account tracking in Eggdrop":[[37,null]],"Add a Host to a User":[[31,"add-a-host-to-a-user"]],"Add a User":[[31,"add-a-user"]],"Adding Bind Functionality":[[8,"adding-bind-functionality"]],"Adding a New Bind Type to the Bind Table":[[8,"adding-a-new-bind-type-to-the-bind-table"]],"Adding a Partyline Command":[[32,"adding-a-partyline-command"]],"Adding a Tcl Bind":[[32,"adding-a-tcl-bind"]],"Adding a Tcl Command":[[32,"adding-a-tcl-command"]],"Adding and linking bots":[[40,"adding-and-linking-bots"]],"Additional Information":[[35,"additional-information"]],"Additional functions":[[29,"additional-functions"]],"Advanced Settings":[[41,"advanced-settings"]],"Advanced Tips":[[52,null]],"Assign Permission Flags":[[31,"assign-permission-flags"]],"Assoc Module":[[9,null],[49,"assoc-module"]],"Authenticating with NickServ":[[31,"authenticating-with-nickserv"]],"Auto-starting Eggdrop":[[4,"auto-starting-eggdrop"]],"Automatically restarting an Eggdrop":[[31,"automatically-restarting-an-eggdrop"]],"Autoscripts File Structure":[[38,"autoscripts-file-structure"]],"Autoscripts usage":[[38,"autoscripts-usage"]],"Background":[[47,"background"]],"Bans, Invites, and Exempts":[[39,null]],"Basic Settings":[[41,"basic-settings"]],"Best-Effort Account Tracking":[[37,"best-effort-account-tracking"]],"Bind Flags":[[8,"bind-flags"]],"Bind Return Values":[[8,"bind-return-values"]],"Bind Types":[[49,"bind-types"],[53,"bind-types"]],"Binds":[[49,"binds"],[53,"binds"]],"Blowfish Module":[[10,null]],"Boring legal stuff":[[1,null]],"Bot Flags":[[40,"bot-flags"]],"Botnet":[[5,"botnet"],[51,"botnet"]],"Botnet Sharing and Linking":[[40,null]],"Botnet/Dcc/Telnet Settings":[[41,"botnet-dcc-telnet-settings"]],"C Binding":[[8,"c-binding"]],"C Handler":[[8,"c-handler"]],"CTCP CHAT/CHAT4/CHAT6":[[43,"ctcp-chat-chat4-chat6"]],"CTCP Module":[[14,null]],"Calling the Bind":[[32,"calling-the-bind"]],"Can I compile Eggdrop without dynamic modules? (Static compile)":[[7,"can-i-compile-eggdrop-without-dynamic-modules-static-compile"]],"Channel Commands":[[49,"channel-commands"]],"Channel Settings":[[11,"channel-settings"]],"Channels Module":[[11,null]],"Checking Account-tracking Status":[[37,"checking-account-tracking-status"]],"Command Line":[[4,"command-line"]],"Commands":[[53,"commands"]],"Common First Steps":[[31,null]],"Common first steps":[[31,"id1"]],"Compress Module":[[12,null],[49,"compress-module"]],"Config file changes":[[5,"config-file-changes"]],"Config file setup":[[16,"config-file-setup"]],"Configuration":[[34,"configuration"]],"Configuration File Preparation - Generating Keys":[[35,"configuration-file-preparation-generating-keys"]],"Configuration File Preparation - Listening with TLS":[[35,"configuration-file-preparation-listening-with-tls"]],"Configure Channel Settings":[[31,"configure-channel-settings"]],"Connecting to a TLS-enabled IRC server":[[35,"connecting-to-a-tls-enabled-irc-server"]],"Connecting to an Eggdrop listening with TLS":[[35,"connecting-to-an-eggdrop-listening-with-tls"]],"Console Module":[[13,null]],"Console Settings":[[41,"console-settings"]],"Context":[[29,"context"]],"Control Procedures":[[49,"control-procedures"]],"Crontab Method":[[31,"crontab-method"]],"Cygwin Requirements (Windows)":[[3,"cygwin-requirements-windows"]],"DCC Chat":[[31,"dcc-chat"]],"DCC Commands":[[49,"dcc-commands"]],"DNS Module":[[15,null]],"Default Channel Values":[[11,"default-channel-values"]],"Defining bind arguments":[[32,"defining-bind-arguments"]],"Determining if a Server Supports Account Capabilities":[[37,"determining-if-a-server-supports-account-capabilities"]],"Development hints":[[38,"development-hints"]],"Direct TLS":[[31,"direct-tls"]],"Disclaimer":[[54,"disclaimer"]],"Do I still need to \u2018loadmodule\u2019 modules?":[[7,"do-i-still-need-to-loadmodule-modules"]],"Docker":[[4,"docker"]],"Documentation":[[4,"documentation"],[5,"documentation"]],"Download locations":[[34,"download-locations"]],"Editing the config file":[[34,"editing-the-config-file"],[54,"editing-the-config-file"]],"Eggdrop Autoscripts":[[38,null]],"Eggdrop Bind Internals":[[8,null]],"Eggdrop Core Settings":[[41,null]],"Eggdrop Features":[[42,null]],"Eggdrop Module Information":[[7,null]],"Eggdrop Modules":[[2,null]],"Eggdrop Python Commands":[[48,"eggdrop-python-commands"]],"Eggdrop Tcl Commands":[[49,null]],"Eggdrop Twitch Tcl Commands":[[53,null]],"Eggdrop, an open source IRC bot":[[2,null]],"Enable/Disable Channel Settings":[[11,"enable-disable-channel-settings"]],"Enabling Eggdrop Account Tracking":[[37,"enabling-eggdrop-account-tracking"]],"Enabling TLS Security on Eggdrop":[[35,null]],"Enabling hybrid configuration":[[47,"enabling-hybrid-configuration"]],"Enabling solo configuration":[[47,"enabling-solo-configuration"]],"Encryption/Hashing":[[47,null]],"Errata":[[44,"errata"]],"Example bottrees":[[40,"example-bottrees"]],"Executable Path":[[41,"executable-path"]],"Explaining the Linking/Sharing Process":[[36,"explaining-the-linking-sharing-process"]],"Extended Bans (EXTBANS)":[[39,"extended-bans-extbans"]],"FTP":[[4,"ftp"]],"File and Directory Settings":[[41,"file-and-directory-settings"]],"File placement":[[38,"file-placement"]],"Filesys Module":[[16,null],[49,"filesys-module"]],"Flag Masks":[[49,"flag-masks"]],"Flags":[[53,"flags"]],"General Workflow To Create a New Bind":[[8,"general-workflow-to-create-a-new-bind"]],"Getting the source":[[34,"getting-the-source"]],"Git Development Snapshot":[[4,"git-development-snapshot"]],"Global Variables":[[49,"global-variables"]],"Header section":[[48,"header-section"]],"History":[[34,"history"]],"How to Get Eggdrop":[[4,"how-to-get-eggdrop"]],"How to Upgrade":[[5,"how-to-upgrade"]],"How to Write an Eggdrop Module":[[29,null]],"How to get Eggdrop":[[2,"how-to-get-eggdrop"]],"How to install Eggdrop":[[2,"how-to-install-eggdrop"]],"How to install a module":[[7,"how-to-install-a-module"]],"How to share userfiles- the super-short version":[[36,"how-to-share-userfiles-the-super-short-version"]],"Hybrid Configuration":[[47,"hybrid-configuration"]],"IPv6 support":[[43,null]],"IRC":[[51,"irc"]],"IRC Module":[[18,null]],"IRCv3 support":[[44,null]],"Ident Module":[[17,null]],"Installation":[[2,"installation"],[34,"installation"],[43,"installation"],[51,"installation"]],"Installation Pre-requisites":[[2,"installation-pre-requisites"]],"Installing Eggdrop":[[2,null],[3,null]],"Join a Channel":[[31,"join-a-channel"]],"Keeping Logs":[[52,"keeping-logs"]],"Keys, certificates and authentication":[[51,"keys-certificates-and-authentication"]],"Limitations":[[26,"limitations"]],"Loading Python":[[21,"loading-python"],[48,"loading-python"]],"Log Files":[[41,"log-files"]],"Log on to the partyline":[[31,"log-on-to-the-partyline"]],"MODULE_close ()":[[29,"module-close"]],"MODULE_expmem":[[29,"module-expmem"]],"MODULE_report":[[29,"module-report"]],"MODULE_start":[[29,"module-start"]],"MODULE_table":[[29,"module-table"]],"Making bots share user records":[[40,"making-bots-share-user-records"]],"Manifest.json":[[38,"manifest-json"]],"Match Characters":[[49,"match-characters"]],"Minimum Requirements":[[4,"minimum-requirements"]],"Miscellaneous Commands":[[49,"miscellaneous-commands"]],"Modifying Default Strings":[[52,"modifying-default-strings"]],"Modularizing Your Config File":[[52,"modularizing-your-config-file"]],"Module Header":[[32,"module-header"]],"Module requirements":[[29,"module-requirements"]],"Modules":[[3,"modules"],[5,"modules"],[41,"modules"]],"Modules included with Eggdrop":[[6,null]],"Multithreading and async":[[48,"multithreading-and-async"]],"Must-read changes for Eggdrop v1.10":[[5,"must-read-changes-for-eggdrop-v1-10"]],"No show?":[[34,"no-show"]],"Notes Module":[[19,null],[49,"notes-module"]],"Notice":[[4,"notice"]],"Obtaining Help":[[4,"obtaining-help"]],"On the Hub Bot":[[36,"on-the-hub-bot"]],"On the Leaf Bot":[[36,"on-the-leaf-bot"]],"Output Commands":[[49,"output-commands"]],"PBKDF2 Module":[[20,null],[49,"pbkdf2-module"]],"Partyline Commands":[[21,"partyline-commands"]],"Partyline commands":[[26,"partyline-commands"]],"Partyline usage":[[16,"partyline-usage"]],"Patching Eggdrop":[[46,null]],"Pre-requisites":[[35,"pre-requisites"]],"Prerequisites":[[34,"prerequisites"]],"Protecting Botnet Communications":[[35,"protecting-botnet-communications"]],"Python Module":[[21,null]],"Quick Startup":[[3,"quick-startup"],[4,"quick-startup"]],"README":[[4,null]],"Registering with Twitch":[[54,"registering-with-twitch"]],"Reloading Python Scripts":[[48,"reloading-python-scripts"]],"Removing a bind":[[49,"removing-a-bind"]],"Renaming commands":[[52,"renaming-commands"]],"Required Code":[[32,"required-code"]],"Required Server Capabilities":[[37,"required-server-capabilities"]],"Return Values":[[49,"return-values"]],"SCHAT":[[31,"schat"]],"SSL Settings":[[41,"ssl-settings"]],"SSL/TLS Settings":[[51,"ssl-tls-settings"]],"Scripts":[[5,"scripts"],[41,"scripts"],[51,"scripts"]],"Secure (TLS) Links":[[40,"secure-tls-links"]],"Secure DCC":[[51,"secure-dcc"]],"Seen Module":[[22,null]],"Self-logging":[[52,"self-logging"]],"Server Module":[[23,null]],"Setting Up Eggdrop":[[34,null]],"Setting up SASL authentication":[[31,"setting-up-sasl-authentication"]],"Settings":[[43,"settings"]],"Share Module":[[24,null]],"Sharing Userfiles":[[36,null]],"Solo configuration":[[47,"solo-configuration"]],"Some things you can do with Eggdrop":[[2,"some-things-you-can-do-with-eggdrop"]],"Stackable Binds: HT_STACKABLE":[[8,"stackable-binds-ht-stackable"]],"Stackable binds":[[49,"stackable-binds"]],"Starting the Eggdrop":[[34,"starting-the-eggdrop"]],"Submitting a patch via GitHub":[[46,"submitting-a-patch-via-github"]],"Summary":[[8,"summary"]],"Supported CAP capabilities":[[44,"supported-cap-capabilities"]],"System Pre-Requisites":[[4,"system-pre-requisites"]],"System Requirements":[[21,"system-requirements"],[48,"system-requirements"]],"Systemd Method (Newer Linux Systems)":[[31,"systemd-method-newer-linux-systems"]],"TCP Connections":[[49,"tcp-connections"]],"TLS support":[[51,null]],"Tcl API":[[26,"tcl-api"]],"Tcl Commands":[[5,"tcl-commands"],[21,"tcl-commands"],[38,"tcl-commands"]],"Tcl File":[[38,"tcl-file"]],"Tcl Interface":[[47,"tcl-interface"]],"Telnet":[[31,"telnet"]],"Terms":[[40,"terms"]],"Textfile Substitutions":[[50,null]],"The Party Line":[[45,null]],"The super-short version":[[34,"the-super-short-version"]],"Transfer Module":[[25,null]],"Triggering any Bind":[[8,"triggering-any-bind"]],"Tutorials":[[2,null]],"Twitch":[[54,null]],"Twitch IRC limitations":[[54,"twitch-irc-limitations"]],"Twitch Module":[[26,null]],"Twitch web UI functions":[[54,"twitch-web-ui-functions"]],"Upgrading":[[4,"upgrading"]],"Upgrading Eggdrop":[[5,null]],"Uptime Module":[[27,null]],"Usage":[[43,"usage"],[44,"usage"],[47,"usage"],[51,"usage"]],"User Record Manipulation Commands":[[49,"user-record-manipulation-commands"]],"Users and Flags":[[55,null]],"Using Accounts with Tcl Scripts":[[37,"using-accounts-with-tcl-scripts"]],"Using Eggdrop":[[2,null]],"Using botflags":[[40,"using-botflags"]],"Using certificates to authenticate Eggdrops":[[40,"using-certificates-to-authenticate-eggdrops"]],"Using the Bind in Tcl":[[8,"using-the-bind-in-tcl"]],"Using the Python Module":[[48,null]],"Value-based Channel Settings":[[11,"value-based-channel-settings"]],"Variables in Your Config":[[52,"variables-in-your-config"]],"WHOX":[[37,"whox"]],"What are modules?":[[7,"what-are-modules"]],"What is Eggdrop?":[[4,"what-is-eggdrop"]],"What is a botnet?":[[40,"what-is-a-botnet"]],"What to do with a module?":[[29,"what-to-do-with-a-module"]],"Where to find more help":[[2,"where-to-find-more-help"]],"Woobie Module":[[28,null]],"Writing a Basic Eggdrop Module":[[32,null]],"Writing an Eggdrop Python Script":[[33,null]],"Writing an Eggdrop Python script":[[48,"writing-an-eggdrop-python-script"]],"Writing an Eggdrop Tcl Script":[[30,null]],"account-extban":[[49,"account-extban"]],"account-notify":[[37,"account-notify"]],"account-tag":[[37,"account-tag"]],"account2nicks [channel]":[[49,"account2nicks-account-channel"]],"accounttracking":[[49,"accounttracking"]],"add/rem_builtins":[[29,"add-rem-builtins"]],"add/rem_tcl_commands":[[29,"add-rem-tcl-commands"]],"add/rem_tcl_ints":[[29,"add-rem-tcl-ints"]],"add/rem_tcl_strings":[[29,"add-rem-tcl-strings"]],"add_hook/del_hook":[[29,"add-hook-del-hook"]],"addbot
[botport [userport]]":[[49,"addbot-handle-address-botport-userport"]],"addchanrec ":[[49,"addchanrec-handle-channel"]],"adduser [hostmask]":[[49,"adduser-handle-hostmask"]],"and":[[49,"and"]],"assoc [name]":[[49,"assoc-chan-name"]],"backup":[[49,"backup"]],"banlist [channel]":[[49,"banlist-channel"]],"bind ":[[48,"bind-arguments"]],"bind [proc-name]":[[49,"bind-type-flags-keyword-mask-proc-name"]],"binds [type/mask]":[[49,"binds-type-mask"]],"boot [reason]":[[49,"boot-user-bot-reason"]],"botattr [changes [channel]]":[[49,"botattr-handle-changes-channel"]],"botishalfop [channel]":[[49,"botishalfop-channel"]],"botisop [channel]":[[49,"botisop-channel"]],"botisvoice [channel]":[[49,"botisvoice-channel"]],"botlist":[[49,"botlist"]],"botname":[[49,"botname"]],"botnick":[[49,"botnick"]],"botonchan [channel]":[[49,"botonchan-channel"]],"bots":[[49,"bots"]],"callevent ":[[49,"callevent-event"]],"cap [arg]":[[49,"cap-ls-values-req-enabled-raw-arg"]],"chanbans ":[[49,"chanbans-channel"]],"chandname2name ":[[49,"chandname2name-channel-dname"]],"chanexempts ":[[49,"chanexempts-channel"]],"chaninvites ":[[49,"chaninvites-channel"]],"chanlist [flags][<&|>chanflags]":[[49,"chanlist-channel-flags-chanflags"]],"channame2dname ":[[49,"channame2dname-channel-name"]],"channel add [option-list]":[[49,"channel-add-name-option-list"]],"channel get [setting]":[[49,"channel-get-name-setting"]],"channel info ":[[49,"channel-info-name"]],"channel remove ":[[49,"channel-remove-name"]],"channel set ":[[49,"channel-set-name-options"]],"channels":[[49,"channels"]],"chansettype ":[[49,"chansettype-setting"]],"chattr [changes [channel]]":[[49,"chattr-handle-changes-channel"]],"chhandle ":[[49,"chhandle-old-handle-new-handle"]],"clean

Writing an Eggdrop Tcl Script

So you want to write an Eggdrop Tcl script, but you don’t really know where to begin. This file will give you a very basic idea about what Eggdrop scripting is like, using a very simple script that may help you get started with your own scripts.

This guide assumes you know a bit about Eggdrops and IRC. You should have already installed Eggdrop. The bot should not be on any important or busy channels (development bots can be annoying if your script has bugs). If you plan on doing a lot of development, enable the .tcl and .set commands, and make sure nobody else has access to your bot. The .tcl and .set commands are helpful in debugging and testing your code.

First, read through the script. Very few commands are listed here intentionally, but as you want to develop more advanced scripts, you will definitely want to get familiar with the core Tcl language commands, especially the string- and list-related commands, as well as Eggdrop’s own library of custom Tcl commands, located in tcl-commands.doc

If you have the .tcl command enabled, you can load a script by typing ‘.tcl source script/file.tcl’ to load it. Otherwise, add it to your config file like normal (examples to do so are at the bottom of the config file) and ‘.rehash’ or ‘.restart’ your bot.

Let’s look at a very basic example script that greets users as they join a channel:

# GreetScript.tcl
# Version: 1.0
# Author: Geo <geo@eggheads.org>
#
# Description:
# A simple script that greets users as they join a channel
#
### Config Section ###
# How would you like the bot to gree users?
# 0 - via public message to channel
# 1 - via private message
set pmsg 0
# What message would you like to send to users when they join?
set greetmsg "Hi! Welcome to the channel!"
### DO NOT EDIT BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING! ###

bind join - * greet

proc greet {nick uhost hand chan} {
  global pmsg
  global greetmsg
  if {$pmsg} {
    putserv "PRIVMSG $nick :$greetmsg"
  } else {
    putserv "PRIVMSG $chan :$greetmsg"
  }
}

putlog "greetscript.tcl v1.0 by Geo"

Whew! There’s a lot going on here. You’ll generally see scripts broken into a few key parts- the header, the config section, and the code section. Ok, let’s go over this piece by piece. First, the header of the script:

# GreetScript.tcl
# Version: 1.0
# Author: Geo <geo@eggheads.org> or #eggdrop on Libera
#
# Description:
# A simple script that greets users as they join a channel

Any line prefixed by a # means it is comment, and thus ignored. You can type whatever you want, and it won’t matter. When writing scripts (especially if you want to give them to other people, it is good to use comments in the code to show what you’re doing. Here though, we use it to describe what the script is and, most importantly, who wrote it! Let’s give credit where credit is due, right? You may want to give users a way to contact you as well.

Next, let’s look at the configuration section:

### Config Section ###
# How would you like the bot to gree users?
# 0 - via public message to channel
# 1 - via private message
set pmsg 0
# What message would you like to send to users when they join?
set greetmsg "Hi! Welcome to the channel!"
### DO NOT EDIT BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING! ###

To make scripts easy to use, you’ll want to have a section that allows users to easily change the way the script operates, without having to edit the script itself. Here, we have two settings: one that controls which method the Eggdrop uses to greet a user, and a second with the message to greet the user with. Sure, we could hard-code that into the code section below, but in larger scripts that makes things harder to find, and also forces you to potentially have to make the same change multiple times in code. Why not make it simple and do it once, up top? Notice the settings do not have #s in front of them- they are variables that will be used by the script later on. And of course, the standard ominous warning not to change anything below!

Now, let’s look start to dissect the actual code!:

bind join - * greet

This is a bind. This sets up an action that Eggdrop will react to. You can read all the binds that Eggdrop uses here. Generally, we like to place all binds towards the top of the script so that they are together and easy to find. Now, let’s look at documentation of the bind join together.

bind JOIN

bind join <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel>

Description: triggered by someone joining the channel. The mask in the bind is matched against “#channel nick!user@host” and can contain wildcards.

So here, we can start to match the bind listed in the code to how it is described in the documentation. The first term after the bind command is ‘join’, showing that it is a join bind, which means the action we define here will take place each time a user joins a channel. The next term is ‘mask’, and it says it is in the form “#channel nick!user@host”. This is where we can start to refine exactly when this bind is triggered. If we want it to work for every person joining every channel Eggdrop is on, then a simple ‘*’ will suffice here- that will match everything. If we wanted this bind to only work in #foo, then the mask would be “#foo *”. If we wanted to greet users on every channel, but only those who are on AOL, the mask would be “* *!*@*.aol.com”. Finally the ‘proc’ argument is the name of the Tcl proc we want to call, where the code that actually does stuff is located.

So to sum up this line from the example script: When a user joins on any channel and with any hostmask, run the code located in proc ‘greet’.

Now that we told the Eggdrop what action to look for, we need to tell it what to do when that action occurs!:

proc greet {nick uhost hand chan} {

This is how we declare a Tcl proc. As we said above, this is where the magic happens. To set up the proc (this will look differently for different binds), lets refer back to the bind JOIN documentation. The second line shows procname <nick> <user@host> <handle> <channel>. Eggdrop does a lot of stuff in the background when a bind is triggered, and this is telling you how Eggdrop will present that information to you. Here, Eggdrop is telling you it is going to pass the proc you created four variables: One that contains the nickname of the person who triggered the bind (in this case, the user who joined), the user@host of that user, the handle of that user (if the user has one on the bot), and the channel that the bind was triggered on.

So let’s say someone with the nickname Geo with a hostmask of awesome@aol.com joined #sexystuff and that person is not added to the bot as a user. Eggdrop will pass 4 values to the variables you set up in that proc: The first variable will get the value “Geo”, the second “awesome@aol.com”, the third “*”, and the fourth “#sexystuff”. (That third value was a trick, we didn’t talk about that- if the user is not added to the bot, handle will get a “*” as a value). Now, let’s use those variables!:

global pmsg
global greetmsg

This is a simple one- because we’re using variables declared in the main body of the script (remember way up top?), we have to tell this proc to use that variable, not not create a new local variable for this proc.

And finally, let’s actually send a message to the user:

if {pmsg}
  putserv "PRIVMSG $nick :$greetmsg"
} else {
  putserv "PRIVMSG $chan :$greetmsg"
}

Here, we’re going to check if pmsg is true (any value that is not 0) and, if yes, send a private message to the user. If pmsg is not true (it is 0), then we will send the message to the channel. You can see that the first putserv message sends a PRIVMSG message to $nick - this is the nickname of the user that joined, and that Eggdrop stored for us in the first variable of the proc, which we called ‘nick’. The second putserv message will send a PRIVMSG message to the $chan - this is the channel the user joined, and that Eggdrop stored for us in the fourth variable of the proc, which we called ‘chan’.

And finally: get the credit you deserve when the script loads!:

putlog "greetscript.tcl v1.0 by Geo"

Like your variables at the top of the script, this line is not inside a Tcl proc and will execute when the script is loaded. You can put this or any other initialization code you want to run.

And there you have it- your first script! Take this, modify it and experiment. A few challenges for you:

  • How can you configure which channel it should run on, without hard-coding it into the bind? (Maybe with a variable…)

  • How can you configure it to only message a user with the nickname “FancyPants”? (Sounds like something a bind could handle)

  • How can you delay the message from sending by 5 seconds? (Hint: utimer)

  • How can you send different messages to different channels? (A new setting may be in order…)

  • How can you get the bot to not greet itself when it joins the channel? (Eggdrop stores its own nickname in a variable called $botnick)

  • How can you add the person joining the channel’s nickname to the greet message? (You can put variables inside variables…)

If you want to try these out, join #eggdrop on Libera and check your answers (and remember, there are dozens of ways to do this, so a) don’t be defensive of your choices, and b) take others’ answers with a grain of salt!)

Copyright (C) 2003 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/tutorials/firststeps.html000066400000000000000000000763601524730042700241720ustar00rootroot00000000000000 Common First Steps — Eggdrop 1.10.2 documentation

Common First Steps

Log on to the partyline

Now that your bot is online, you’ll want to join the partyline to further use the bot. First, read what it tells you when you started it up:

STARTING BOT IN USERFILE CREATION MODE.
Telnet to the bot and enter 'NEW' as your nickname.
OR go to IRC and type:  /msg BotNick hello
This will make the bot recognize you as the master.

You can either connect to the bot via telnet (unencrypted), connect to the bot using DCC Chat (unencrypted), connect to the bot using SCHAT (encrypted), or use TLS-compatible socket client (encrypted).

Telnet

To telnet, you’ll either need a program like Putty (Windows), or you can do it from the command line of your shell using the telnet command:

telnet <IP of bot> <listen port>

You can find the IP and port the bot is listening on by a) remembering what you set in the config file ;) or b) reading the display the bot presented when it started up. Look for a line that looks similar to this:

Listening for telnet connections on 2.4.6.9:3183 (all).

This tells you that the bot is listening on IP 2.4.6.9, port 3183. If you see 0.0.0.0 listed, that means Eggdrop is listening on all available IPs on that particular host.

DCC Chat

To use a DCC chat connection in your IRC client, you can either /dcc chat BotNick or /ctcp BotNick chat.

SCHAT

SCHAT (Secure Chat) is a sub-protocol of Secure Direct Client-to-Client (SDCC) that enables encrypted TLS sessions between two users. KVIrc and AdiIRC are examples of IRC clients that support the SCHAT protocol. If TLS is configured for your Eggdrop, you can connect via /ctcp bot schat.

Direct TLS

To use a client such as openssl to wrap your connection with TLS, please read the TLS docs regarding partyline connection. There are also other clients that can connect to the partyline with TLS, like pefero, maintained by Eggdrop contributor mortmann.

If one of those methods does not work for you, try another. Once you’re on the bot for the first time, type .help for a short list of available commands, or .help all for a more thorough list.

Common first steps

To learn more about any of these commands, type .help <command> on the partyline. It will provide you the syntax you need, as well as a short description of how to use the command.

Join a Channel

To tell the Eggdrop to join a channel, use:

.+chan #channel

Add a User

To add a user to the bot, use:

.+user <handle>

Add a Host to a User

The handle is the name that the bot uses to track a user. No matter what nickname on IRC a user uses, a single handle is used to track the user by their hostmask. To add a hostmask of a user to a handle, use:

.+host <handle> <hostmask>

where the hostmask is in the format of <nick>!<ident>@hostname.com . Wildcards can be used; common formats are *!*@hostname.com for static hosts, or *!ident@*.foo.com for dynamic hostnames.

Assign Permission Flags

To assign an access level to a user, first read .help whois for a listing of possible access levels and their corresponding flags. Then, assign the desired flag to the user with:

.chattr <handle> <+flag>

So to grant a user the voice flag, you would do:

.chattr handle +v

It is important to note that, when on the partyline, you want to use the handle of the user, not their current nickname.

Configure Channel Settings

Finally, Eggdrop is often used to moderate and control channels. This is done via the .chanset command. To learn more about the (numerous!) settings that can be used to control a channel, read:

.help chaninfo

Common uses involve setting channels modes. This can be done with the chanmode channel setting:

.chanset #channel chanmode +snt

which will enforce the s, n, and t flags on a channel.

Automatically restarting an Eggdrop

A common question asked by users is, how can I configure Eggdrop to automatically restart should it die, such as after a reboot? Historically, Eggdrop relied on the host’s crontab system to run a script (called botchk) every ten minutes to see if the eggdrop is running. If the eggdrop is not running, the script will restart the bot, with an optional email sent to the user informing them of the action. Newer Linux systems come with systemd, which can provide better real-time monitoring of processes such as Eggdrop. You probably want to use systemd if your system has it.

Crontab Method

  1. Enter the directory you installed your Eggdrop to. Most commonly, this is ~/eggdrop (also known as /home/<username>/eggdrop).

  2. Just humor us- run ./scripts/autobotchk without any arguments and read the options available to you. They’re listed there for a reason!

  3. If you don’t want to customize anything via the options listed in #2, you can install a crontab job to start Eggdrop simply by running:

    ./scripts/autobotchk yourEggdropConfigNameHere.conf
    
  4. Review the output of the script, and verify your new crontab entry by typing:

    crontab -l
    

By default, it should create an entry that looks similar to:

0,10,20,30,40,50 * * * * /home/user/bot/scripts/YourEggdrop.botchk 2>&1

This will run the generated botchk script every ten minutes and restart your Eggdrop if it is not running during the check. Also note that if you run autobotchk from the scripts directory, you’ll have to manually specify your config file location with the -dir option. To remove a crontab entry, use crontab -e to open the crontab file in your system’s default editor and remove the crontab line.

Systemd Method (Newer Linux Systems)

  1. Enter the directory you installed your Eggdrop to. Most commonly, this is ~/eggdrop (also known as /home/<username>/eggdrop).

  2. Install the systemd job for Eggdrop simply by running:

    ./scripts/autobotchk yourEggdropConfigNameHere.conf -systemd
    
  3. Note the output at the end of the script informing you of the command you can use to start/stop the Eggdrop in thee future. For example, to manually start the Eggdrop, use:

    systemctl --user start <botname>.service
    

To stop Eggdrop, use:

systemctl --user stop <botname>.service

To rehash (not reload) Eggdrop, use:

systemctl --user reload <botname>.service

(Yes, we acknowledge the confusion that the systemd reload command will execute the Eggdrop ‘.rehash’ command, not the ‘.reload’ command. Unfortunately, systemd did not consult us when choosing its commands!)

To prevent Eggdrop from automatically running after a system start, use:

systemctl --user disable <botname>.service

To re-enable Eggdrop automatically starting after a system start, use:

systemctl --user enable <botname>.service

Authenticating with NickServ

Many IRC features require you to authenticate with NickServ to use them. You can do this from your config file by searching for the line:

#  putserv "PRIVMSG NickServ :identify <password>"

in your config file. Uncomment it by removing the ‘#’ sign and then replace <password> with your password. Your bot will now authenticate with NickServ each time it joins a server.

Setting up SASL authentication

Simple Authentication and Security Layer (SASL) is becoming a prevalant method of authenticating with IRC services such as NickServ prior to your client finalizing a connection to the IRC server, eliminating the need to /msg NickServ to identify yourself. In other words, you can authenticate with NickServ and do things like receive a cloaked hostmask before your client ever appears on the IRC server. Eggdrop supports three methods of SASL authentication, set via the sasl-mechanism setting:

  • PLAIN: To use this method, set sasl-mechanism to 0. This method passes the username and password (set in the sasl-username and sasl-password config file settings) to the IRC server in plaintext. If you only connect to the IRC server using a connection protected by SSL/TLS this is a generally safe method of authentication; however you probably want to avoid this method if you connect to a server on a non-protected port as the exchange itself is not encrypted.

  • ECDSA-NIST256P-CHALLENGE: To use this method, set sasl-mechanism to 1. This method uses a public/private keypair to authenticate, so no username/password is required. Not all servers support this method. If your server does support this, you you must generate a certificate pair using:

    openssl ecparam -genkey -name prime256v1 -out eggdrop-ecdsa.pem
    

    You will need to determine your public key fingerprint by using:

    openssl ec -noout -text -conv_form compressed -in eggdrop-ecdsa.pem | grep '^pub:' -A 3 | tail -n 3 | tr -d ' \n:' | xxd -r -p | base64
    

    Then, authenticate with your NickServ service and register your public certificate with NickServ. You can view your public key On Libera for example, it is done by:

    /msg NickServ set pubkey <fingerprint string from above goes here>
    
  • EXTERNAL: To use this method, set sasl-mechanism to 2. This method allows you to use other TLS certificates to connect to the IRC server, if the IRC server supports it. An EXTERNAL authentication method usually requires you to connect to the IRC server using SSL/TLS. There are many ways to generate certificates; one such way is generating your own certificate using:

    openssl req -new -x509 -nodes -keyout eggdrop.key -out eggdrop.crt
    

You will need to determine your public key fingerprint by using:

openssl x509 -in eggdrop.crt -outform der | sha1sum -b | cut -d' ' -f1

Then, ensure you have those keys loaded in the ssl-privatekey and ssl-certificate settings in the config file. Finally, to add this certificate to your NickServ account, type:

/msg NickServ cert add <fingerprint string from above goes here>
  • SCRAM-SHA-256: To use this method, set sasl-mechanism to 3.

  • SCRAM-SHA-512: To use this method, set sasl-mechanism to 4.

eggheads-eggdrop-9c00fe1/doc/html/tutorials/module.html000066400000000000000000000776021524730042700232510ustar00rootroot00000000000000 Writing a Basic Eggdrop Module — Eggdrop 1.10.2 documentation

Writing a Basic Eggdrop Module

An Eggdrop module is a piece of C code that can be loaded (or unloaded) onto the core Eggdrop code. A module differs from a Tcl script in that modules must be compiled and then loaded, whereas scripts can be edited and loaded directly. Importantly, a module can be written to create new Eggdrop-specific Tcl commands and binds that a user can then use in a Tcl script. For example, the server module loaded by Eggdrop is what creates the “jump” Tcl command, which causes tells the Eggdrop to jump to the next server in its server list.

There are a few required functions a module must perform in order to properly work with Eggdrop

Module Header

A module should include license information. This tells other open source users how they are allowed to use the code. Eggdrop uses GPL 2.0 licensing, and our license information looks like this:

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

Required Code

For this section, you don’t necessarily need to understand what it is doing, but this code is required for a module to function. If you want to learn more about this, check out How to Write an Eggdrop Module

You’ll next want to name your module:

#define MODULE_NAME "woobie"

Declare your own function tables (again, you don’t need to understand this part; you just need to copy/paste it):

#undef global
static Function *global = NULL, *server_funcs = NULL;
EXPORT_SCOPE char *woobie_start();

Next are two memory-related functions used by the core Eggdrop .status and .module commands:

static int woobie_expmem()
{
  int size = 0;

  return size;
}

static void woobie_report(int idx, int details)
{
  if (details) {
    int size = woobie_expmem();

    dprintf(idx, "    Using %d byte%s of memory\n", size,
            (size != 1) ? "s" : "");
  }
}

This function is called when Eggdrop loads the module:

char *woobie_start(Function *global_funcs)
{
  global = global_funcs;

  /* Register the module. */
  module_register(MODULE_NAME, woobie_table, 2, 1);
  /*                                            ^--- minor module version
   *                                         ^------ major module version
   *                           ^-------------------- module function table
   *              ^--------------------------------- module name
   */

  if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) {
    module_undepend(MODULE_NAME);
    return "This module requires Eggdrop 1.8.0 or later.";
  }

This next function is used to unload the module:

static char *woobie_close()
{
  module_undepend(MODULE_NAME);
  return NULL;
}

This creates a function table that is exported to Eggdrop. In other words, these are commands that are made available to the Eggdrop core and other modules. At minimum, the following functions must be exported:

static Function woobie_table[] = {
  (Function) woobie_start,
  (Function) woobie_close,
  (Function) woobie_expmem,
  (Function) woobie_report,
};

At this point, you should have a module that compiles and can be loaded by Eggdrop- but doesn’t really do anything yet. We’ll change that in the next section!

Adding a Partyline Command

A partyline command function accepts three arguments- a pointer to the user record of the user that called the command; the idx the user was on when calling the command; and a pointer to the arguments appended to the command. A command should immediately log that it was called to the LOG_CMDS log level, and then run its desired code. This simple example prints “WOOBIE” to the partyline idx of the user that called it:

static int cmd_woobie(struct userrec *u, int idx, char *par)
{
  putlog(LOG_CMDS, "*", "#%s# woobie", dcc[idx].nick);
  dprintf(idx, "WOOBIE!\n");
  return 0;
}

If you add partyline commands, you need to create a table which links the new command name to the function it should call. This can be done like so:

static cmd_t mywoobie[] = {
  /* command  flags  function     tcl-name */
  {"woobie",  "",    cmd_woobie,  NULL},
  {NULL,      NULL,  NULL,        NULL}  /* Mark end. */
};

The tcl-name field can be a name for a Tcl command that will also call the partyline command, or it can be left as NULL.

Adding a Tcl Command

Eggdrop uses the Tcl C API library to interact with the Tcl interpreter. Learning this API is outside the scope of this tutorial, but this example Tcl command will echo the provided argument:

static int tcl_echome STDVAR {
  BADARGS(2, 2, " arg");

  if (strcmp(argv[1], "llama") {
    Tcl_AppendResult(irp, "You said: ", argv[1], NULL);
    return TCL_OK;
  } else {
    Tcl_AppendResult(irp, "illegal word!");
    return TCL_ERROR;
  }
}

A few notes on this example. BADARGS is a macro that checks the input provided to the Tcl command. The first argument BADARGS accepts is the minimum number of parameters the Tcl command must accept (including the command itself). The second argument is the maximum number of parameters that BADARGS will accept. The third argument is the help text that will be displayed if these boundaries are exceeded. For example, BADARGS(2, 4, “ name ?date? ?place?”) requires at least one argument to be passed, and a maximum of three arguments. Eggdrop code style is to enclose optional arguments between qusetion marks in the help text.

Similar to adding a partyline command, you also have to create a function table for a new Tcl command:

static tcl_cmds mytcl[] = {
  {"echome",           tcl_echome},
  {NULL,                   NULL}   /* Required to mark end of table */
};

And now the newly-created Tcl command ‘echome’ is available for use in a script!

Adding a Tcl Bind

A Tcl bind is a command that is activated when a certain condition is met. With Eggdrop, these are usually linked to receiving messages or other IRC events. To create a bind, you must first register the bind type with Eggdrop when the module is loaded (you added the woobie_start() and woobie_close functions earlier, you still need all that earlier code in here as well):

static p_tcl_bind_list H_woob;

...

char *woobie_start(Function *global_funcs)
{
  ...
  H_woob = add_bind_table("woobie", HT_STACKABLE, woobie_2char);
}

And then remove the binds when the module is unloaded:

static char *woobie_close()
{
  ...
  del_bind_table(H_woob);
}

Here, “woobie” is the name of the bind (similar to the PUB, MSG, JOIN types of binds you already see in tcl-commands.doc). HT_STACKABLE means you can have multiple binds of this type. “woobie_2char” defines how many arguments the bind will take, and we’ll talk about that next.

Defining bind arguments

The following code example defines a bind that will take two arguments:

static int woobie_2char STDVAR
{
  Function F = (Function) cd;

  BADARGS(3, 3, " nick chan");

  CHECKVALIDITY(woobie_2char);
  F(argv[1], argv[2]);
  return TCL_OK;
}

And this example defines a bind that will take three arguments:

static int woobie_3char STDVAR
{
  Function F = (Function) cd;

  BADARGS(4, 4, " foo bar moo");

  CHECKVALIDITY(woobie_3char);
  F(argv[1], argv[2], argv[3]);
  return TCL_OK;
}

Like before, BADARGS still checks that the number of arguments passed is correct, and outputs help text if it is not. The rest is boilerplate code to pass the arguments when the bind is called.

Calling the Bind

To call the bind, Eggdrop coding style is to name that function “check_tcl_bindname”. So here, whenever we reach a point in code that should trigger the bind, we’ll call check_tcl_woobie() and pass the arguments we defined- in this case, two arguments that woobie_2char was created to handle. Here is some sample code:

check_tcl_woobie(chan, nick);


static int check_tcl_woobie(char *chan, char *nick, char *userhost) {
  int x;
  char mask[1024];
  struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };

  snprintf(mask, sizeof mask, "%s %s!%s",
                                chan, nick, userhost);
  Tcl_SetVar(interp, "_woob1", nick ? (char *) nick : "", 0);
  Tcl_SetVar(interp, "_woob2", chan, 0);
  x = check_tcl_bind(H_woob, mask, &fr, " $_woob1 $_woob2",
        MATCH_MASK | BIND_STACKABLE);
  return (x == BIND_EXEC_LOG);
}

Now that we have encountered a condition that triggers the bind type (in code by calling check_tcl_woobie() ), we need to check it against the binds the user has loaded in scripts and see if it matches those conditions. This is done with check_tcl_bind(), called with the bind type, the userhost of the user, the flag record of the user if it exists, the bind arguments, and bind options. We can configure how we want to check the triggering action against the bind, and we can further use the return value from check_tcl_bind() to take additional action by Eggdrop. You can read more about the specific values used in check_tcl_bind in Triggering any Bind

eggheads-eggdrop-9c00fe1/doc/html/tutorials/pythonscript.html000066400000000000000000000464721524730042700245330ustar00rootroot00000000000000 Writing an Eggdrop Python Script — Eggdrop 1.10.2 documentation

Writing an Eggdrop Python Script

So you want to write an Eggdrop python script, but you don’t really know where to begin. This file will give you a very basic idea about what Eggdrop python scripting is like, using a very simple script that may help you get started with your own scripts.

This guide assumes you know a bit about Eggdrops and, because it reimplements those commands, Tcl. You should have already installed Eggdrop. The bot should not be on any important or busy channels (development bots can be annoying if your script has bugs). If you plan on doing a lot of development, enable the .python commands, and make sure nobody else has access to your bot.

First, read through the script below. Very few commands are listed here intentionally, but as you want to develop more advanced scripts, you will definitely want to get familiar with the Eggdrop custom Tcl commands, as Python reimplements them.

If you have the .python command enabled, you can load a script by typing ‘.pysource script/file.tcl’ to load it. Otherwise, add it to your config file like normal (examples to do so are at the bottom of the config file) and ‘.rehash’ or ‘.restart’ your bot.

Let’s look at a very basic example script that greets users as they join a channel:

from eggdrop import bind
from eggdrop.tcl import putmsg

def joinGreetUser(nick, host, handle, channel, **kwargs):
  putmsg(channel, f"Hello {nick}, welcome to {channel}")

def joinGreetOp(nick, host, handle, channel, **kwargs):
  putmsg(channel, f"{nick} is an operator on this channel!")

if 'GREET_BINDS' in globals():
  for greetbind in GREET_BINDS:
    greetbind.unbind()
  del GREET_BINDS

GREET_BINDS = list()
GREET_BINDS.append(bind("join", "*", "*", joinGreetUser))
GREET_BINDS.append(bind("join", "o", "*", joinGreetOp))

print('Loaded greet.py')

Whew! There’s a lot going on here. You’ll generally see scripts broken into a few key parts- the header, the config section, and the code section. Ok, let’s go over this piece by piece. Any line prefixed by a # means it is comment, and thus ignored. You can type whatever you want, and it won’t matter. When writing scripts (especially if you want to give them to other people, it is good to use comments in the code to show what you’re doing.

Because developers are lazy and don’t want to manually reimplement every single Tcl command in Python code, Python includes a Tcl module that calls Tcl command via Python:

from eggdrop import bind
from eggdrop.tcl import putmsg

You can load the entire Tcl library by running import eggdrop.tcl, or (the better solution to save memory) is to only load the commands you need via something like from eggdrop.tcl import putmsg. The one exception to be aware of here is the bind command- you always want to load the custom python implementation of that so that it called a python bind, not a Tcl proc. Do this via from eggdrop import bind.

Next, let’s create some functions that the main script will call:

def joinGreetUser(nick, host, handle, channel, **kwargs):
  putmsg(channel, f"Hello {nick}, welcome to {channel}")

def joinGreetOp(nick, host, handle, channel, **kwargs):
  putmsg(channel, f"{nick} is an operator on this channel!")

Above, we create a function called joinGreetUser and a function called joinGreetOp. This function calls the putmsg Tcl. Note that the arguments here are slightly different than Tcl, requiring a comma instead of a space between the arguments.

We include the next code segment due to a limitation of Eggdrop’s Python module. Currently, if Eggdrop is rehashed, previously existing binds will be duplicated instead of being overwritten. This is example code checks for previously-existing binds after the script reloaded and deletes them:

if 'GREET_BINDS' in globals():
  for greetbind in GREET_BINDS:
    greetbind.unbind()
  del GREET_BINDS

As part of the above code, we need to track the binds. To do that, the GREET_BINDS list is created, and then we add the binds to it:

GREET_BINDS = list()
GREET_BINDS.append(bind("join", "*", "*", joinGreetUser))
# Again, arguments are separated with a comma. This bind requires the 'o' flag to be triggered.
GREET_BINDS.append(bind("join", "o", "*", joinGreetOp))

Note that you must call binds at the end of a script (not the top, like we do in Tcl), because the functions must be defined before being called. And again, we create the binds using comma-separated arguments, not spaces like Tcl. GREET_BINDS is created as a JOIN bind that is triggered when a user with an operator flag joins. When it is triggered, it called the function joinGreetOp that we defined at the beginning of the script.

And lastly, our lovely print('Loaded greet.py') line. This is a normal python command, nothing special about it- it will only print to standard output (ie, if you’re running in a terminal). If you want to print to something like the partyline, you’d want to import and then use putlog("Loaded greet.py.

Copyright (C) 2003 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/tutorials/setup.html000066400000000000000000001023661524730042700231200ustar00rootroot00000000000000 Setting Up Eggdrop — Eggdrop 1.10.2 documentation

Setting Up Eggdrop

This guide was based off perhaps the most helpful Eggdrop website ever written, egghelp.org by slennox. As happens with life, slennox has moved on and no longer updates it, but the information here is still incredibly useful. We have updated his setup page here and hope it will continue to prove useful to our users

Prerequisites

Make sure Tcl AND it’s dev packages are installed on your system. On Debian-based systems, this is done with:

sudo apt-get install tcl tcl-dev

It is also STRONGLY recommended to have openssl installed, to enable SSL/TLS protection:

sudo apt-get install openssl libssl-dev

The super-short version

You can read the Installation section for a more detailed explanation of these steps.

  1. Download the current version of Eggdrop to your shell via FTP, or simply type: wget geteggdrop.com -O eggdrop-1.10.2.tar.gz

  2. Next, from the commandline of the shell, ensure you are in the same directory that you downloaded the tar file into

  3. Type: tar zxvf eggdrop-1.10.2.tar.gz

  4. Type: cd eggdrop-1.10.2

  5. Type ./configure

  6. Type make config

  7. Type make

  8. Type make install

  9. Type cd ~/eggdrop

  10. For a quick start, edit the eggdrop-basic.conf file. To take advantage of all Eggdrop’s features, we recommend using eggdrop.conf instead. It is also a good idea to rename the file to something easy to remember and specific to your bot, like botnick.conf.

  11. Type ./eggdrop -m <config file>

Getting the source

History

The current supported version of Eggdrop is the 1.10.x tree. Only the current major version (1.10.x) is supported; earlier major versions are not.

The most current version of Eggdrop, and the one appropriate for most users, is the current 1.10 series. It added many features such as SASL support, multi-ip listening, and a new password hashing module. It is the most complete, feature-rich, and functional version of Eggdrop. If you’re just starting out with Eggdrop, you should use 1.10.2.

Prior to that, the 1.8 series added several major features, to include IPv6 support and SSL/TLS connections. 1.6.21, which is now over 10 years old, was the last release of the 1.6 series. Because Eggdrop remained at the 1.6.21 patchlevel for several years it is still often run by users who have become comfortable with that version and don’t wish to move to a newer version- however, it is getting harder and harder to continue running 1.6 bots on modern Linux systems. Tcl scripts written for bots as far back as the 1.6 series generally work on all later versions of Eggdrop as well, so if you haven’t already- upgrade!

The 1.10 Eggdrop tree is currently under active development and the most recent changes are available in daily snapshots for users to download for testing. While the development snapshot will contain the most current, up-to-date features of Eggdrop, it is not yet considered stable and users stand a higher chance of encountering bugs during use. If you do use it and find a bug, it is highly encouraged to report it via the Eggheads GitHub issues page.

Download locations

The developers distribute Eggdrop via two main methods: FTP, and GitHub. For FTP, it is packaged in tarball format (with the .tar.gz filename extension), with the version number in the filename. The Eggdrop 1.10.2 source, for example, would be named eggdrop-1.10.2.tar.gz.

The Eggheads FTP is a repository for the current version of Eggdrop, as well as the most current development snapshot and previous stable releases.

Eggdrop also maintains a GitHub page where you can download the development snapshot or a stable version, via either git commandline or by downloading a tarball. To download via git, type git clone https://github.com/eggheads/eggdrop.git, then cd eggdrop. This gives you the development version. To switch to the most recent stable version, type git checkout stable/1.10. You can then skip to step 4 in the Installation section below.

Installation

Installing Eggdrop is a relatively simple process provided your shell has the required tools for successful compilation. On most commercial shell accounts which allow Eggdrop bots you won’t have any problems with installation, but on some private boxes or a shell on your ISP you may experience errors during compilation.

Below is a step by step guide to the installation process. These instructions apply to 1.10 bots. It assumes you will be installing eggdrop-1.10.2.tar.gz, so just change the numbers if you are installing another version.

  1. Put the Eggdrop source on your shell using one of the specified download locations, either by downloading the current version of Eggdrop to your local system and then uploading it to the shell via FTP, or downloading it directly to the shell via the shell’s FTP client, git, wget, or curl. You don’t need to put the .tar.gz file in its own directory (it’ll be done automatically in the next step).

  2. SSH to the shell (if you haven’t already), and type tar zxvf eggdrop-1.10.2.tar.gz (if this doesn’t work, try gunzip eggdrop-1.10.2.tar.gz then tar xvf eggdrop-1.10.2.tar). This will extract the Eggdrop source into its installation directory, named eggdrop-1.10.2.

  3. Type cd eggdrop-1.10.2 switch to the directory the Eggdrop source was extracted to.

  4. Type ./configure (that’s a period followed by a slash followed by the word ‘configure’). This makes sure the shell has all the right tools for compiling Eggdrop, and helps Eggdrop figure out how to compile on the shell.

  5. When configure is done, type make config. This sets up which modules are to be compiled. For a more efficient installation, you can use make iconfig to select the modules to compile, but if you’re not sure just use make config.

  6. Type make. This compiles the Eggdrop. The process takes a brief moment on fast systems, longer on slow systems.

  7. Type make install DEST=~/botdir. This will install Eggdrop into a directory named ‘botdir’ in your home directory. You can change ‘botdir’ to anything you like. Note that in some cases you may need to specify the full path, e.g. make install DEST=/home/cooldude/botdir, using the ~ character in make install won’t always work. You can get the full path by typing pwd.

  8. You can safely delete the installation directory named eggdrop-1.10.2 (to do this, type cd ~ then rm -rf eggdrop-1.10.2) that was created previously, although some people may find it handy to keep that directory for performing additional or future installations of the same version without recompiling.

That’s it! Eggdrop is now installed into its own directory on the shell. It’s time to edit the configuration files to make Eggdrop work the way you want it to.

Configuration

You will need to edit the configuration file before you can start up your Eggdrop. You can find the example configuration file in the directory you extracted the Eggdrop source to, under the name ‘eggdrop.conf’. If you downloaded Eggdrop to your system, you can unzip the tarball (.tar.gz) file to its own directory using 7-Zip or a similar program, and view the example config file, botchk file, and all the documentation files locally. You can use Notepad to edit these files, although it’s sometimes desirable to use an editor that supports the Unix file format such as EditPlus. To edit the file once it is on your shell, a program such as ‘nano’ or ‘vim’ is recommended.

Editing the config file

Eggdrop comes with two versions of the configuration file- eggdrop.conf and eggdrop-basic.conf. While it is recommended that users edit a copy of eggdrop.conf to take advantage of all the features Eggdrop has to offer, using eggdrop-basic.conf to start will be a quicker path for some. Still, it is recommended that you come back to the full config file at some point to see what you are missing.

It is first recommended to rename the sample config to something other than “eggdrop.conf”. Giving it the name of the bot’s nick (e.g. NiceBot.conf) is quite common. In the config file, you set up the IRC servers you want the bot to use and set Eggdrop’s options to suit your needs. Eggdrop has many options to configure, and editing the configuration file can take some time. I recommend you go over the entire config file to ensure the bot will be configured properly for your needs. All of the options in the config file have written explanations - be sure to read them carefully. Some of them can be a little bit vague, though.

To comment out a line (prevent the bot from reading that line), you can add a ‘#’ in front of a line. When you come to a line that you need to edit, one popular option is to comment out the original and add your new line right below it. This preserves the original line as an example. For example:

# Set the nick the bot uses on IRC, and on the botnet unless you specify a
# separate botnet-nick, here.
#set nick "Lamestbot"
set nick LlamaBot

Below are some of the common settings used for Eggdrop:

set username:

if your shell runs identd (most do), then you should set this to your account login name.

set vhost4:

you’ll need to set this if you want your bot to use a vhost. This setting lets you choose which IP to use if your shell has multiple. Use vhost4 for an IPv4 address (ie, 1.2.3.4) See also: vhost6

set vhost6:

the same as vhost4, only for IPv6 addresses (ie, 5254:dead:b33f::1337:f270).

logfile:

keeping logs is a good idea. Generally, you should have one log for bot stuff, and one log for each of your channels. To capture bot stuff, add the line logfile mcobxs * "botnick.log" to the config. To capture channel stuff, add logfile jkp #donkeys "#donkeys.log", logfile jkp #horses "#horses.log", etc. Make sure you remove the sample logfile lines for the channel #lamest. If you’d like to put your logfiles in their own directory, specify the directory in the log name (e.g. logfile jkp #donkeys "logs/#donkeys.log" to write the logfiles in the /logs directory).

listen 3333 all:

you will almost certainly want to change this, as 3333 will probably be in use if there are other Eggdrops running on the machine. Generally, you can choose any port from 1024 to 65535, but the 49152-65535 range is best as these are the private/dynamic ports least likely to be reserved by other processes. You can choose not to have a port by commenting this line out, but that will prevent any telnet connections to the bot (you won’t be able to use the bot as a hub, won’t be able to telnet to the bot yourself, and the bot won’t respond to /ctcp botnick CHAT requests).

set protect-telnet:

setting this to 1 is strongly recommended for security reasons.

set require-p:

this is a useful feature allowing you to give party line access on a user-specific basis. I recommend setting it to 1.

set stealth-telnets:

when you telnet to your bot, it will usually display the bot’s nickname and version information. You probably don’t want people seeing this info if they do a port scan on the bot’s shell. Setting this to 1 will prevent the bot from displaying its nickname and version when someone telnets to it.

set notify-newusers:

set this to the nick you will have on the bot. This setting isn’t really used if you have learn-users switched off.

set owner:

you should only put one person in this list - yourself. Set it to the nick you will have on the bot. Do NOT leave it set to the default “MrLame, MrsLame”.

set default-flags:

these are the flags automatically given to a user when they introduce themselves to the bot (if learn-users is on) or when they’re added using .adduser. If you don’t want the user to be given any flags initially, set this to “” or “-“.

set must-be-owner:

if you have the .tcl and .set commands enabled, you should definitely set this to 1. In 1.3.26 and later, you can set it to 2 for even better security.

set chanfile:

the chanfile allows you to store ‘dynamic’ channels so that the bot rejoins the channel if restarted. Dynamic channels are those you make the bot join using the .+chan command - they aren’t defined in the config file. The chanfile is good if you frequently add/remove channels from the bot, but can be a pain if you only like to add/remove channels using the config file since settings stored in the chanfile with overwrite those set in the config. You can choose not to use a chanfile by setting it to “”.

set nick:

this is what you use to specify your bot’s nickname. I recommend against using [ ] { } character’s in the bot’s nick, since these can cause problems with some Tcl scripts, but if you’d like to use them, you’ll need to precede each of those characters with a backslash in the setting, e.g. if you wanted your bot to have the nick [NiceBot], use set nick "\[NiceBot\]".

set altnick:

if you want to use [ ] { } characters in the bot’s alternate nick, follow the backslash rule described previously.

server add:

you should specify multiple servers in this list, in case the bot is unable to connect to the first server. The format for this list is shown below:

server add you.need.to.change.this:6667
server add another.example.com:7000:password
server add [2001:db8:618:5c0:263::]:6669:password
server add ssl.example.net:+6697
set learn-users:

this is an important setting that determines how users will be added to your Eggdrop. If set to 1, people can add themselves to the bot by sending ‘hello’ to it (the user will be added with the flags set in default-flags). If set to 0, users cannot add themselves - a master or owner must add them using the .adduser command.

set dcc-block:

although the example config file recommends you set this to 0 (turbo-dcc), this may cause DCC transfers to abort prematurely. If you’ll be using DCC transfers a lot, set this to 1024.

Finally, be sure to remove the ‘die’ commands from the config (there are two of them ‘hidden’ in various places), or the bot won’t start. Once you’ve finished editing the config file, make sure you rename it to something other than “eggdrop.conf” if you haven’t already. Then, if you edited the config file locally, upload the config file to the directory you installed the bot.

Starting the Eggdrop

Phew! Now that you’ve compiled, installed, and configured Eggdrop, it’s time to start it up. Switch to the directory to which you installed the bot, cross your fingers, and type ./eggdrop -m <config> (where <config> is the name you gave to the config file). Eggdrop should start up, and the bot should appear on IRC within a few minutes. The -m option creates a new userfile for your bot, and is only needed the first time you start your Eggdrop. In future, you will only need to type ./eggdrop <config> to start the bot. Make sure you take the time to read what it tells you when you start it up!

Once your bot is on IRC, it’s important that you promptly introduce yourself to the bot. Msg it the ‘hello’ command you specified in the config file, e.g. /msg <botnick> hello. This will make you the bot’s owner. Once that’s done, you need to set a password using /msg <botnick> pass <password>. You can then DCC chat to the bot.

Now that your Eggdrop is on IRC and you’ve introduced yourself as owner, it’s time to learn how to use your Eggdrop!

No show?

If your bot didn’t appear on IRC, you should log in to the shell and view the bot’s logfile (the default in the config file is “logs/eggdrop.log”).

Additionally, you can kill the bot via the command line (kill pid, the pid is shown to you when you started the bot or can be viewed by running ps x) and then restart it with the -mnt flag, which will launch you directly into the partyline, to assist with troubleshooting. Note that if you use the -t flag, the bot will not persist and you will kill it once you quit the partyline.

If you’re still unsure what the problem is, try asking in #eggdrop on Libera, and be sure to include any relevant information from the logfile. Good luck!

eggheads-eggdrop-9c00fe1/doc/html/tutorials/tlssetup.html000066400000000000000000000361561524730042700236460ustar00rootroot00000000000000 Enabling TLS Security on Eggdrop — Eggdrop 1.10.2 documentation

Enabling TLS Security on Eggdrop

There are several ways TLS encryption can protect communication between your Eggdrop and the world. This guide will walk you through a few common scenarios and how to properly set up TLS protection.

Sidenote: Despite SSL (Secure Socket Layer) encryption being deprecated and no longer secure, the term “SSL” is a bit of an anachronism and still commonly used interchangeably with TLS (Transport Layer Security). If you see the term “SSL” used to describe a secure connection method, including Eggdrop’s own documentation and configuration files, it is probably safe to assume it is actually referring to the secure TLS protocol. If you talk to someone and they use the term “SSL” be sure to correct them, we’re sure they will definitely appreciate it :)

Pre-requisites

Your server must have OpenSSL (or an equivalent fork) installed. Most commonly this is done through your OS’s package manager. Both the main package as well as the development headers must be installed. On a Debian/Ubuntu distro, this can be done by running:

apt-get install openssl libssl-dev

where openssl is the main package binaries, and libssl-dev are the development headers. Without these packages, TLS protection is not possible.

You can check if your Eggdrop properly detected the installation of OpenSSL by either reviewing the ./configure output for the following line:

checking for openssl/ssl.h... yes

Or, for an Eggdrop that is already running, you can join the partyline and type:

.status

and look for:

TLS support is enabled.

Connecting to a TLS-enabled IRC server

Many, if not most, IRC servers offer connection ports that add TLS protection around the data. Eggdrop uses a ‘+’ symbol in front of the port to denote a port as a TLS-enabled port. To add a server in the config file that supports TLS, add it as normal but simply prefix the port with a ‘+’. For example, if irc.pretendNet.org says it offers TLS on port 7000, you would add it to your configuration file as:

server add irc.pretendNet.org +7000

No other action is necessary.

Protecting Botnet Communications

Eggdrop has the ability to protect botnet (direct bot to bot) communications with TLS.

Configuration File Preparation - Generating Keys

If an Eggdrop is going to listen/accept connections on a TLS port (more on that in a moment), it must have a public/private certificate pair generated and configured. For most users, a self-signed certificate is sufficient for encryption (a certificate signed by a certificate authority would be more secure, but obtaining one is outside the scope of this tutorial. However, the implementation of a signed keypair is no different than a self-signed pair). To generate a self-signed key pair, enter the Eggdrop source directory (the directory you first compiled Eggdrop from, usually named eggdrop-X.Y.Z) and type:

make sslcert

The wizard will walk you through generating a keypair and will, by default, install to ~/eggdrop (the install location can be changed by “make sslcert DEST=/path/to/eggdrop/install”

In your config file, uncomment the “ssl-privatekey” and “ssl-certificate” settings. Eggdrop will look in the directory it is running from (~/eggdrop by default) for the files listed; add an absolute path if you installed them outside of Eggdrop’s directory.

Configuration File Preparation - Listening with TLS

Now you need to tell Eggdrop to accept TLS connections. As an example, to listen with TLS on port 5555 on all available IPs, add to the config file:

listen +5555 all

(There are numerous ways to format the listen command; read the config file and documentation for other alternatives)

Connecting to an Eggdrop listening with TLS

To connect to a bot listening with TLS, prefix the port number with a ‘+’. For example:

.+bot HubBot 1.2.3.4 +5555

will use TLS to connect to 1.2.3.4 on port 5555 the next time a connection is attempted to HubBot.

Additional Information

For additional information and a more thorough explanation of Eggdrop’s TLS implementation, please read the TLS docs.

eggheads-eggdrop-9c00fe1/doc/html/tutorials/userfilesharing.html000066400000000000000000000375471524730042700251620ustar00rootroot00000000000000 Sharing Userfiles — Eggdrop 1.10.2 documentation

Sharing Userfiles

One of the great features of Eggdrop is bot linking, allowing you to create your own ‘botnet’. A botnet can consist of two linked bots to as many as hundreds of linked bots. If you’re using an Eggdrop for channel management, the most important feature of linked bots is userfile sharing. With userfile sharing, the bots can keep their user lists, ban lists, and ignore lists in sync with one another. A botnet also has many other possible capabilities, such as allowing bots to op one another securely, and making all bots join a channel with one command.

Linking bots can be a confusing process for first timers, but it’s quite simple if you know how. The first thing you need to do is chose a hub bot. The hub is the main bot which all the other bots on the botnet (which will become leaf bots) connect to. Even if you are only using two bots, it’s best to use one as a hub bot, and the other as a leaf. The hub bot should be the most reliable bot on your botnet, running on a fast, stable, and secure shell - so keep that in mind when deciding which bot will be the hub.

Once you’ve decided on the hub, it’s time to set up the linking.

How to share userfiles- the super-short version

(This example steps assume Eggdrop has been compiled with TLS support, and you are sharing all channels)

On the Hub Bot

  1. Ensure you know what ports your bot is listening on, this is set in the config and those ports will be used in the example below. For this example, our config looks like:

    listen +3333/+4444 all
    
  2. On the hub (for this example, the Hub is called Hubalicious), add the leaf bot (for this example, the Leaf is called LeifErikson) with .+bot LeifErikson <IP_address_of_LiefErikson> +4444/+3333 <LiefErikson_hostmask>.

  3. On the hub, give the leaf bot the appropriate leaf sharing flags with .botattr LiefErikson +gs.

On the Leaf Bot

  1. Ensure you know what ports your bot is listening on, this is set in the config and those ports will be used in the example below. For this example, our config looks like:

    listen +5555/+6666 all
    
  2. Now on the leaf, add the hub bot with .+bot Hubalicious <IP_address_of_Hubalicious> +6666/+5555 <Hubalicious hostmask>.

  3. On the leaf, give the hub bot the appropriate hub and sharing flags with .botattr Hubalicious +ghp.

At this point, the leaf bot should attempt to connect to the hub bot within the next minute, or you can force the link connection with .link Hubalicious. You can also use .bottree to see your botnet structure.

Explaining the Linking/Sharing Process

Eggdrop bots can talk to each other for a variety of reasons. In order for an Eggdrop to talk to another Eggdrop for anything, they must link. This is done by adding a bot record the remote bot on the hub bot (In our example above, using .+bot LeifErikson on Hubalicious, and using .+bot Hubalicious on LiefErikson). Once the bot records are added, bots can be manually connected using the .link command.

In the example above, we add the +s bot flag to LiefErikson’s bot record on Hubalicious to tell Hubalicious that LiefErikson is not only allowed to connect, but is authorized to download the userfiles from Hubalicious. Similarly, we add the +p bot flag to Hubalicious’s bot record on LiefErikson to tell LiefErikson that Hubalicous is authorized to send userfiles to LiefErikson. The +h bot flag is added to Hubalicious’s bot record on LiefErikson so tell LiefErikson that Hubalicious is the hub, and it should always try to automatically connect to Hubalicious.

Lastly, the +g flag is used on both bot records to indicate that Hubalicious is authorized to send userfiles for all channels. This is a shortcut method to sharing all channels instead of setting the +shared channel setting on the hub for each channel userfile you wish the hub to share (set via .chanset), and using the |+s #channel bot flag for each channel userfile that the leaf is authorized to receive userfiles from the hub. As an example of channel-specific userfile sharing, you would use .botattr LiefErikson |+s #theforest on Hubalicious to set #theforest as a channel authorized to be shared to LiefErikson, and .chanset #theforest +shared to tell LiefErikson to accept the channel userfile for #theforest.

One more commonly used flag is the +a flag. This flag specifies an alternate hub to connect to in case the primary hub goes down. The alternate hub should be linked to hub and maintain all channel userfiles the hub maintains to ensure there is no desynchronization while the hub bot is down. Should the hub bot go down, and assuming the +h flag is set for the hub on a leaf, the leafbost will automatically try to reconnect to the hub every minute, even if it makes a connection with the alternate hub in the meantime. An alternate hub is added the same as a hub, except swapping out the h for the a: .botattr AltRock +agp.

Now that you have a hub and leaf bot successfully connected and sharing userfiles, you can repeat the On the Leaf Bot section to add additional leafs to your hub.

eggheads-eggdrop-9c00fe1/doc/html/using/000077500000000000000000000000001524730042700201615ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/html/using/accounts.html000066400000000000000000000461311524730042700226730ustar00rootroot00000000000000 Account tracking in Eggdrop — Eggdrop 1.10.2 documentation

Account tracking in Eggdrop

In Eggdrop 1.9.3, Eggdrop added the ability to associate nicknames with the service accounts they are logged into. It is IMPORTANT to note that Eggdrop’s ability to do this is dependent on an IRC server’s implementation of three features- the IRCv3 extended-join capability, the IRCv3 account-notify capability, and WHOX support. All three of these features must be supported by the server and, in the case of extended-join and account-notify, requested by Eggdrop in order for Eggdrop to maintain “perfect” association between nicknames and account statuses.

Required Server Capabilities

You’re going to see this repeated a lot- the IRC server must support three features in order for Eggdrop to accurately associate accounts with nicknames. These three features allow Eggdrop to always know the current association between an account and a nickname by getting account statuses of users already on a channel when it joins, new users joining a channel, and users who authenticate while on a channel.

extended-join

extended-join is an IRCv3-defined capability that adds the account name of a user to the JOIN message sent by the IRC server, alerting clients that a new member has joined a channel. Enabling this capability allows Eggdrop to immediately determine the account name associated with a user joining a channel

account-notify

account-notify is an IRCv3-defined capability that sends a message to a channel when a member of the channel either authenticates or deauthenticates from their account. Enabling this capability allows Eggdrop to immediately associate an account to a channel member when they authenticate or deauthenticate.

WHOX

‘WHOX <https://ircv3.net/specs/extensions/whox>`_ is a server feature that allows a client to request custom fields to be returned in a WHO response. If a server supports this capability, Eggdrop sends a WHOX query to the server when it joins a channel, allowing it to immediately determine accounts associated with channel members when Eggdrop joins a channel.

Enabling Eggdrop Account Tracking

By default, the Eggdrop config file will attempt to enable all the capabilities required for account tracking. There are two settings to pay attention to

# To request the account-notify feature via CAP, set this to 1
set account-notify 1

# To request the extended-join feature via CAP, set this to 1
set extended-join 1

The ability of a server to support WHOX queries is determined via a server’s ISUPPORT (005) reply. If a server supports WHOX queries, Eggdrop will automatically enable this feature.

Checking Account-tracking Status

While Eggdrop is running, join the partyline and type .status. If account-tracking is enabled (both the server supports and Eggdrop has requested), you’ll see this line

Loaded module information:
  #eggdroptest        : (not on channel)
  Channels: #eggdroptest (trying)
  Account tracking: Enabled           <--- This line
  Online as: BeerBot (BeerBot)

Otherwise, the prompt will tell you which required capability is missing/not enabled

Loaded module information:
  #eggdroptest        :   2 members, enforcing "+tn" (greet)
  Channels: #eggdroptest (need ops)
  Account tracking: Best-effort (Missing capabilities: extended-join, see .status all for details)      <---- This line
  Online as: Eggdrop (Eggdrop)

Determining if a Server Supports Account Capabilities

A server announces the capabilities it supports via a CAP request. If you have Tcl enabled on the partyline (or via a raw message from a client), you can send .tcl cap ls and see if the extended-join and account-notify capabilities are supported by the server. If they are not listed, the server does not support it.

A server announces if it supports WHOX via its ISUPPORT (005) announcement. If you have Tcl enabled on the partyline, you can send .tcl issupport isset WHOX and if it returns ‘1’, WHOX is supported by the server.

Best-Effort Account Tracking

If a server only supports some, but not all, of the required capabilities, Eggdrop will switch to ‘best effort’ account tracking. This means Eggdrop will update account statuses whenever it sees account information, but in this mode Eggdrop cannot guarantee that all account associations are up to date.

If a server does not support extended-join, Eggdrop will not be able to determine the account associated with a user when they join. Eggdrop can update this information by sending a WHOX to the server.

If a server does not support account-notify, Eggdrop will not be able to determine the account associated with a user if they authenticate/deauthenticate from their account after joining a channel. Eggdrop can update this information by sending a WHOX to the server.

If a server does not support WHOX, Eggdrop will not be able to determine the accounts associated with users already on a channel before Eggdrop joined. There is no reliable way to update this information.

One workaround to significantly increase the accuracy of account tracking for scripts in a ‘best effort’ scenario would be to issue a WHOX query (assuming the server supports it), wait for the reply from the server, and then query for the account information.

account-tag

One supplementary capability that can assist a best-effort account tracking scenario is the IRCv3-defined account-tag capability. The account-tag capability attaches a tag with the account name associated with the user sending a command. Enabling this capability allows Eggdrop to update its account tracking every time a user talks in channel, sets a mode, sends a kick, etc. While still not able to offer the same level of accuracy as enabling the “main three” account tracking features, it can increase the overall accuracy of the account list. Additionally, binds that react to user activity (pub, kick, mode, etc) containing account-tag will update the internal account list prior to executing the associated callback, so looking up the account name in the callback can be considered accurate.

Using Accounts with Tcl Scripts

The Eggdrop Tcl ACCOUNT bind is triggered whenever an existing account record stored by Eggdrop is modified, such as a user de/authenticating to their account while in a channel, or information such as an account-tag being seen that updates an existing user. However, the ACCOUNT bind will NOT be triggered for the creation of a new user record, such as a user joining a channel. The bind is triggered for every channel the user is seen on- this means if a user is present with Eggdrop on four channels, the bind will be executed four times, each time with a different channel variable being passed to the associated Tcl procedure. Additionally, in a best-effort account tracking situation, Eggdrop will update the account associated with a user on all channels, not just the channel the event is seen on (and thus resulting in a bind trigger for each channel the user is on).

In order to trigger Tcl script events to cover all instances where a user logs in, you need to pair an ACCOUNT bind with a JOIN bind. This will allow you to execute account-based events when a user joins as well as if they authenticate after joining.

eggheads-eggdrop-9c00fe1/doc/html/using/autoscripts.html000066400000000000000000000620561524730042700234400ustar00rootroot00000000000000 Eggdrop Autoscripts — Eggdrop 1.10.2 documentation

Eggdrop Autoscripts

Since it’s inception, users have needed to load a Tcl script into Eggdrop by downloading a Tcl file, editing the file to customize settings, and then sourceing that file in the config file. In v1.10, the Autoscripts system was added to make this process a little more user-friendly. The autoscripts system helps by:

  • Centralizing commonly-used scripts in a single location

  • Allowing scripts to be downloaded via the partyline

  • Allowing script settings to be configured via the partyline

  • Allowing user-written scripts to be managed by the autoscripts system

  • Providing a documented API to write autoscripts-compatible scripts

Autoscripts usage

To view available autoscript commands, type .autoscript on the partyline. This will open up a special Eggdrop console that doesn’t require you to prefix commands with a ‘.’ . The following sub-commands are available for use with script:

remote

This command will list scripts hosted on the Eggdrop website that are available to be downloaded and installed on your Eggdrop.

fetch <script>

This command will download the specified script from the Eggdrop website and place it into the autoscript/ directory.

list

This command will list scripts locallt present in the autoscripts/ directory, available to be configured and loaded.

config <script>

This command will list settings available for configuration for the provided script.

set <script> <setting>

This command will set setting for script to the provided value. To activate this change, use the load command.

load <script>

This command will activate the script for use. You can also use this command to reload a script after modifying a script variable.

unload <script>

This command will prevent the script from being loaded the next time Eggdrop starts. To fully unload a script, Eggdrop must be restarted!

clean <script>

This command will delete the script from the filesystem. After running this command, you will have to re-download and re-configure the script if you wish to use it again.

update [script]

If no script is specified, this command checks if there any downloaded script has a newer version available. If a script is specified, autoscript will fetch and install the updated script.

Autoscripts File Structure

An autoscripts package requires (minimum) two files: the Tcl script, and a json manifest file.

Tcl File

Nothing new or novel here; this is where your Tcl code goes. The one change to this file is that any setting intended should now be located in the manifest.json file, not the Tcl script file. All variables will be added to the global namespace. For this reason, we suggest wrapping a custom autoscript inside a namespace eval <scriptname> {} statement as an autoscript best practice, which will lessen the chance of a variable name colliding with a variable from a separate script.

Manifest.json

Every autoscripts package must have a manifest.json file. This file contains metadata for the script such as version and description information, as well as the user-configurable settings for use with th script. A simple example of a manifest.json file is as follows:

{
  "schema": 1,
  "name": "woobie",
  "version_major": 1,
  "version_minor": 0,
  "description": "An example script to help developers write autoscript packages",
  "long_description": "This is an example script to help understand the autoscript system. Yeah, it doesn't really do anything, but that's besides the point. It could, and that should be enough for anyone"
  "config": {
    "loaded": 0,
    "udef": {
       "myflag": {
          "type": "flag",
          "description": "Activate the script on <channel> by doing"
       },
       "mystr1": {
          "type": "str",
          "description": "Flood limit, modify the channel value for this doing",
          "value": "{10:6}"
       },
       "mystr2": {
          "type": "str",
          "description": "Change that with",
          "value": "Just my string"
       }
       "myint1": {
          "type": "int",
          "description": "Number of allowed kicks, could be change with",
          "value": 4
       }
    },
    "requires": "tls",
    "vars": {
      "woobie_dict": {
        "description": "A setting that accepts a dict as a value",
        "value": "{quiet q}"
      },
      "woobie_setting": {
        "description": "A normal setting to enable or disable something",
        "value": "1"
      },
      "woobie_string": {
        "description": "A setting taking a string, like a filename or something",
        "value": "woobie"
      },
      "woobie(array)": {
        "description": "A setting that is set as an array",
        "value":"another string"
      }
    }
  }
}

schema

The schema version of autoscript (currently 1)

name

The name of the script. Must match the script name (if the script is foo.tcl, then this must be foo)

version_major

The major version integer (ie, 1 for 1.6)

version_minor

The minor version integer (ie, 6 for 1.6)

description

A one-line summary of what the script does. This will be shown when available scripts are listed on the partyline via .script list.

long_description

A longer description of what the script does, similar to a README. This will be shown when a script is viewed via .script config.

config-loaded

Whether this script is currently loaded or not. It should be default set to 0.

config-udef-<varname>-type

Type of the user-defined channel setting, could be flag, str or int.

config-udef-<varname>-description

Description of user-defined channel setting used by the script. The description is appended with “ .chanset <channel> <varname> value” in case of int or str, and with “ .channel <channel> +<varname>” when flag

config-udef-<varname>-value

Default value of user-defined channel setting used by the script. This is displayed when configuration settings are displayed to the user on the partyline.

config-requires

Any Tcl package required for use by the script, such as tls, http, json, etc.

config-vars-<varname>

A setting intended to be modified by the user. The ‘description’ field should describe what the setting does, and the ‘value’ field stores the current value. These settings are displayed when the configuration settings are displayed to the user on the partyline.

config-vars-<varname>-description

A description of the setting, displayed in the configuration listing for the script.

config-vars-<varname>-value

The value the setting is set to

File placement

Autoscript files are stored in the autoscript directory. The path structure is eggdrop/autoscript/<scriptname>/[script files]. If the autoscript fetch command is used, a .tgz file will be downloaded and extracted to the proper location automatically. If you wish to manually add a script, create a directory with the same name as the script, and then place the script and manifest files inside the directory. The directory name must exactly match the script name (without the .tcl extension)! If the Tcl script to be loaded is called myscript_goodversion_specialfeature.tcl, then the directory must also called myscript_goodversion_specialfeature.

Development hints

  • An autoscript should not require a user to manually open the script in an editor for any reason. Design your script as such!

  • Use user defined channel flags to enable/disable a script for a particular channel, they’re easy!

  • Don’t use global statements. Based on the manifest, variables are created by autoscript in the global namespace before the script is loaded. Instead of the global command, use the variable command to access a global variable inside a proc. And because Tcl is awesome, each variable must be declared on its own line, not all on a single line like you can do with global. Sorry!

  • While we’re talking about variables… make them unique to prevent collisions! We recommend prefixing the script name in front of a variable, such as myscript_setting or ms_setting. Alternatively, you can wrap your autoscript inside a namespace eval <scriptname> {} statement, which create a private namespace for your script to operate within.

Tcl Commands

The autoscripts Tcl script adds three new commands for use with Tcl scripts:

egg_loaded

Description: lists all scripts currently loaded via the autoscripts system

Returns: A Tcl list of script names currently loaded via autoscripts

egg_unloaded

Description: lists all scripts downloaded to the local machine via the autoscripts system but not currently loaded by Eggdrop

Returns: A Tcl list of script names downloaded but not currently loaded via autoscripts

egg_all

Description: lists all script downloaded to the localm machine via the autoscripts system, regardless if they are running or not

Returns: A Tcl list of all script namees download via autoscripts

eggheads-eggdrop-9c00fe1/doc/html/using/bans.html000066400000000000000000000320121524730042700217700ustar00rootroot00000000000000 Bans, Invites, and Exempts — Eggdrop 1.10.2 documentation

Bans, Invites and Exempts

Bans, Invites, and Exempts

Eggdrop handles bans, exempts and invites in various ways, and this file is intended to help clarify how these modes are used within the bot. From here on, ‘mode’ applies to all three modes (bans, exempts, and invites) unless otherwise specified. There are three types of modes:

Global modes

These modes will be active on every channel the bot monitors. Some will “expire” after a while (be removed automatically). Others are considered “permanent” and can only be removed by a master.

Channel modes

These modes are active only on one channel, and are almost always temporary modes that expire after an hour or so (depending on how long you’ve specified in the config file). Usually they’re created by a Tcl script of some sort.

Non-bot modes

These are modes that were not placed by the bot. They can be removed by anyone on the channel. The other two types of modes are protected by the bot (unless the channel settings specify otherwise), and have to be removed via the bot.

Bans can also be either sticky or unsticky:

Sticky

These modes are usually set by a user using the “.stick” command. Modes with this attribute are attempted to be kept active on the channel at all times by the bot, even if the channel is set to use dynamic modes. Obviously, if the channel isn’t set to use dynamic modes, this won’t have any effect.

Un-sticky

These modes are the style that Eggdrop sets by default when a user uses one of the commands that result in a mode. This attribute means that the ban will be removed if using dynamic modes after a certain time. If a mode is “sticky” and you wish to set it to be “un-sticky”, use the “.unstick” command.

Mode behavior:

Bans

If the channel is supporting dynamic bans, then the ban is set when a user with a matching hostmask joins the channel; otherwise, the modes are permanently set. On a channel with dynamic bans, the ban expires after ‘ban-time’ minutes (which is specified in the config file).

Exempts

If the channel is not supporting dynamic exempts, then they are set at all times. Otherwise, the exemption is set when a ban is placed whose host includes that covered by the exempt. The exempt will remain in place for at least ‘exempt-time’ minutes (defined in config file) or until after the corresponding ban has been removed, whichever happens last.

Invites

If the channel does not support dynamic invites, then they are set at all times. Otherwise, the invite is set when the channel is +i and a user requests an invite into the channel. The invite then remains set for ‘invite-time’ minutes (defined in config file) or until the channel goes -i again, whichever happens last.

Extended Bans (EXTBANS)

As of version 1.10.2, Eggdrop incorportes extended ban support. Exteneded bans give new functionality to the traditional IRC ban through the use of new prefix to the banmask. Eggdrop currently only supports single-character extended ban flags, not longform (ie “a”, but not “account”).

When a ban is added through Eggdrop with the .+extban command, Eggdrop automatically takes the EXTBAN prefix for the server and prepends it to the EXTBAN flag the user provides. This is the preferred method of adding extbans to ensure they are functionally added to Eggdrop

If you add an extended ban via .+ban instead of .+extban, Eggdrop assumes you know what you are doing and gives the user lattitude to set what they want. When an extban ban is added through Eggdrop with the .+ban command, Eggdrop identifies and parses the supplied mask as extban syntax before storing it. Eggdrop will then check whether the provided extban flag is supported by the current server, either through ACCOUNTEXTBAN or the advertised EXTBAN flags. If the flag is not supported, Eggdrop will save the ban internally but will not set it on the channel until connected to a server that supports that flag. If the flag is supported and locally matchable (ACCOUNTEXTBAN or one of UABCmNpqQT), Eggdrop will store it like a normal ban.

However, some extended bans cannot be matched by Eggdrop (for example, an extended ban that matches if a user is on a channel. If the extban is a valid flag but cannot be matched by Eggdrop, it is automatically stored as sticky. Because it cannot be matched, it will be kept set on the channel despite any dynamic-ban channel status set.

If a user or server sets an extended ban directly on the channel and it contains a banmask that Eggdrop can match against, Eggdrop tracks it as a normal channel banlist entry, subject to normal channel banlist handling, including removal after ban-time when +dynamicbans is enabled (unless it corresponds to a sticky internal ban).

The only extended bans that can be enforced with a kick via +enforcebans are the value of ACCOUNTEXTBAN, and U. These extended ban flags are eligible for Eggdrop’s local kick/enforcement behavior; other matchable extbans are set and tracked like bans but Eggdrop does lacks the ability to know if the user violates them and cannot kick users itself.

eggheads-eggdrop-9c00fe1/doc/html/using/botnet.html000066400000000000000000000636241524730042700223550ustar00rootroot00000000000000 Botnet Sharing and Linking — Eggdrop 1.10.2 documentation

Botnet Sharing and Linking

Botnet Sharing and Linking

The purpose of this document is to show you what a botnet is and how it could be useful to you. It also covers botflags userfile sharing.

What is a botnet?

A botnet consists of two or more bots linked together. This can allow bots to op each other securely, control floods efficiently, and share user lists, ban lists, exempt/invite lists, and ignore lists (if sharing is enabled).

Terms

The following are some common terms used in this document:

Botnet

A botnet consists of two or more bots connected together.

Link

Link is the term used to describe a bot connecting to another bot.

Hub

A bot is described as a hub-bot if one or more bots are linked to it.

Leaf

A leaf is a non-hub bot connecting to a hub-bot. A leaf has only one other bot connected to it, its hub. Leaf bots can be assigned the “l” botflag to prevent other bots from linking to them.

Link Bot

A link-bot is a bot that is linked to another bot. It may or may not be a hub-bot.

Share

Share is the term used to describe the sharing of user records.

Share Bot

A share-bot is a bot which shares user records with one or more linked bots.

Aggressive Share

Aggressive share is a term used to describe the direction of sharing user-files. Aggressive share bots will SEND userfiles to another passive bot.

Passive Share

Passive share is a term used to describe the direction of sharing user-files. Passive share bots will accept userfiles from an aggressive share bot.

Address

The physical address, containing the address and port of the bot. For example: lame.org:3333. You can change a bot’s address with:

.chaddr <botname> <address> <port#>[/<relay-port#>]
Relay Port

The relay port number of the bot is defined in the config file. Note that you can define one port for bots and another for user connections.

Relay

A relay connection is used to relay (jump) to another bot via telnet or DCC chat. You can relay to another bot even if the remote bot is not linked. You can use:

.relay <botname>

to relay to another bot.

Port

The telnet port is used by the bot to communicate with other bots and/or users. Note that you can define separate ports for user and bot connections.

Example bottrees

BotA
  |-+BotB
  |==BotC
  |=+BotD
  `--BotC

Legend: * – means the bots are linked, but not sharing userfiles * -+ means the bots are sharing userfiles * == means the bots have an encrypted link between them, and are not sharing userfiles * =+ means the bots have an encrypted link between them, and are sharing userfiles

Bot Flags

Flags are attributes that determine what a bot can or is allowed to do. Flags can be either global (such as +s) or channel specific (such as |+s #lamest). See ‘.help botattr’ for help with setting these flags.

The following is a list of valid bot flags:

a

alternate (automatically link to this bot if the hub bot can’t be linked)

b

ban sharing (passive bot is allowed to share bans with aggressive bot)

c

channel sharing (passive bot is allowed to share channel changes with aggressive bot)

d

share aggressively (SEND userfile to a passive bot), limited sharing (passive bot is NOT allowed to share anything)

e

exempt sharing (passive bot is allowed to share exempts with aggressive bot)

g

global share (share all channels)

h

hub (automatically link to this bot)

i

isolate (isolate the party line across a bot link)

j

invite sharing (passive bot is allowed to share invites with aggressive bot)

l

leaf (bot is not allowed to link in other bots)

n

ignore sharing (passive bot is allowed to share ignores with aggressive bot)

p

share passively (ACCEPT userfile from an aggressive bot)

r

reject (bot will not be allowed to link)

s

share aggressively (SEND userfile to a passive bot), unlimited sharing (passive bot is allowed to share anything)

u

user sharing (passive bot is allowed to share user changes with aggressive bot)

0-9

user defined flags

Adding and linking bots

With the common terms out of the way, we can start with the process of linking two bots. Before you start, you need to know the address and port of each bot you wish to link.

Here is an example scenario:

BotA is on lame.org listening on port 3333, and BotB is on irc.org listening on port 4444. First, you have to add each Bot to the other’s userfile. On BotA, you would type ‘.+bot BotB irc.org 4444’. If BotB is on a common channel with BotA, BotB’s hostmask is automatically added. Otherwise, you have to add the hostmask manually with the ‘.+host’ command. On BotB, you would type ‘.+bot BotA lame.org 3333’.

At this point, you can link the two bots by typing ‘.link BotA’ on BotB (or ‘.link BotB’ on BotA). The bots will now give themselves random passwords which are not stored encrypted in the userfile. Note that you can link as many bots as you wish to your botnet.

Using botflags

Botflags are needed to assign special functions and tasks to your bots. Bot flags are set with the ‘.botattr’ command. See ‘.help botattr’ for help with this command. The following is a list of botflags and their functions:

“h” (hub)

If you want your bot(s) to automatically link/relink, you can assign the +h botflag each bot’s hub. Note that if you set multiple bots +h, the bot only attempts to link to one.

“a” (alternate)

If your bots are, for some reason, unable to link to their hub, they will attempt to connect to an alternate hub. You can assign a bot as an alternate hub by giving it the +a botflag.

“l” (leaf)

This flag, assigned to a link bot, will prevent the link bot from linking other bots to your botnet.

“r” (reject)

If you assign this flag to a link bot, the link bot will not be allowed to link to the botnet.

“i” (isolate)

This flag isolates a link bot’s partyline from the rest of the botnet. Anything said on the link bot’s partyline won’t appear on the rest of the botnet.

“s” (SEND userfile to)
+s Giving a link bot this flag will make the bot share aggressively

with the link bot. See ‘Aggressive Share’ in section 2 of this document for more information on aggressive sharing.

“|s” (channel-specific sharing)

+s bots need this flag for each channel you want to share.

“p” (ACCEPT userfile from)

Giving a link bot this flag will make the bot share passively with the link bot. See ‘Passive Share’ in section 2 of this document for more information on passive sharing.

“g” (global share)

This flag allows the sharing of all channels with a link bot.

“0-9” (user-defined)

These 10 flags are user-defined can be used by scripters.

Making bots share user records

Before you start preparing your bots for sharing, make sure that you’ve loaded the transfer and share modules. You also have to ensure that each channel you wish to share is set +shared (see ‘.help chanset’ and ‘.help chaninfo’).

By using specific botflags, you can cause your bot to share aggressively with some link bots, and passively with others. For sharing to work, flags must be set properly on both the passive and the aggressive bots. An aggressive bot will not share userfiles with another aggressive bot; a passive bot will not share userfiles with another passive bot.

First off, let’s say we have two bots we want to link. We will call one Lamestbot, and the other Lameshare. The first thing that needs to be done is each bot needs the other bot added to its user record. Your botnet should have a hub bot. This will have them connect automatically whenever they are started. Without a hub, you would have to manually link them with the .link command. For this situation we will make Lamestbot the hub for our small botnet. Let’s also use a channel called #eggdrop as the one we want to share user channel flags with. Do the following:

On Lamestbot:

.+bot Lameshare eggdrop.com 3333

This command adds a user record to Lamestbot for Lameshare. Lameshare is running from eggdrop.com and is on port 3333. If Lameshare were to have a separate port for users/relays we would just add a ‘/’ and the port number, i.e. 3333/5555:

.botattr Lameshare +s

This tells us that Lamestbot will only send user files to Lameshare; Lameshare will not send them to it:

.botattr Lameshare |s #eggdrop

This sets up sharing of the channel flags for #eggdrop between the bots. Without this, they will not share channel flags only global flags for the users

On Lameshare:

.+bot Lamestbot best.com 3333/5555

Again this will add a user on Lameshare called Lamestbot with the domain of best.com. The bot has two ports, 3333 for bot communications and 5555 for users/relays:

.botattr Lamestbot +hp

This command sets Lamestbot as the hub and also as a passive share, which means Lameshare will accept user files from Lamestbot::

.botattr Lamestbot |s #eggdrop

This sets #eggdrop as a share channel

Our botnet:

Lamestbot
  `-+Lameshare

Let’s add a third bot called beldin to this scenario:

On Lamestbot:

.+bot beldin llama.com 3333

.botattr beldin s|s #eggdrop

Notice how i piped (the | character) the channel flag, also saving time.)

Also note that you don’t have to add beldin on Lameshare. Since they are already sharing, it was added automatically. The one thing that you should note is that no bot flags will be shared. If you set beldin as +s (Aggressive share) on the hub (Lamestbot) he will not be set on the other bots as that. The same with the channel +s flag. All other flags will be shared such as the o, f, etc. Now that we have three bots, we can also set up one as an alternate hub. The alternate hub is the bot that the bots will auto-connect to in the event the hub goes down or is unreachable.

Let’s make beldin an alternate hub for our little botnet.

On Lameshare:

.botattr beldin +a

That’s all there is to it. Again, since bot flags are not shared, you would have to add the bot flag +a for beldin on them all. The only ones you would not have to do this on are beldin and the hub (Lamestbot). Of course, if you had more bots, you would have to add beldin as a +a on them, but you would not do it on the hub or beldin.

Our botnet:

Lamestbot
  |-+beldin
  `-+Lameshare

Using certificates to authenticate Eggdrops

Eggdrops can use certificates to authenticate when linking to each other instead of a password. First, you must ensure you have set the appropriate certificates in the ssl-privatekey and ssl-certificate settings in the config file, and then enable the ssl-cert-auth setting. Next, add the certificate on the partyline by using .fprint + to add the fingerprint for the certificate currently in use, or .fprint <SHA1 fingerprint> to manually add a fingerprint. Once the config file settings are set 0and fingerprints are added on the partyline, Eggdrops will attempt to use their certificates instead of passwords for authentication.

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/core.html000066400000000000000000001212061524730042700220010ustar00rootroot00000000000000 Eggdrop Core Settings — Eggdrop 1.10.2 documentation

Last revised: October 2, 2025

Eggdrop Core Settings

This file describes the syntax and all the settings of your Eggdrop configuration file. Please note that you don’t need to set all of these variables to make your bot work properly.

Executable Path

The first line in an Eggdrop configuration file should contain a fully qualified path to your Eggdrop executable. It has to be implemented in the way the example shows to make the config file work as a shell script.

Example:

#! /path/to/eggdrop

Basic Settings

You can change the basic Eggdrop appearance and behavior in this section.

set username “lamest”

This setting defines the username the bot uses on IRC. This setting has no effect if an ident daemon is running on your bot’s machine.

set admin “Lamer <email: lamer@lamest.lame.org>”

This setting defines which contact person should be shown in .status, /msg help, and other places. You really should include this information.

set network “SomeIrcNetwork”

This setting is used only for info to share with others on your botnet. Set this to the IRC network your bot is connected to.

set timezone “America/New_York”

This setting defines which timezone your bot is in. It’s used for internal routines as well as for logfile timestamping and scripting purposes. It must be three or more alphabetic characters. For example, timezone “Europe/Berlin” automatically adjusts for standard time and daylight saving time. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List

set env(TZ) “$timezone” (disabled by default)

If you don’t want to use the timezone setting for scripting purposes only, but instead everywhere possible, then use this setting.

set vhost4 “99.99.0.0”
set vhost4 “virtual.host.com”
If you’re using virtual hosting (your machine has more than 1 IP), you may want to specify the particular IP to bind to. You can specify either by hostname or by IP. Note that this is not used for listening. Use the ‘listen’ command to specify the listening address.
set vhost6 “2001:db8:618:5c0:263::”
set vhost6 “my.ipv6.host.com”
IPv6 vhost to bind to for outgoing IPv6 connections. You can set it to any valid IPv6 address or hostname, resolving to an IPv6 address. Note that this is not used for listening. Use the ‘listen’ command to specify the listening address.
set prefer-ipv6 “1”

Prefer IPv6 over IPv4 for connections and dns resolution. If the preferred protocol family is not supported, other possible families will be tried.

addlang “english”

If you want to have your Eggdrop messages displayed in another language, change this command to match your preferences. An alternative would be to set the environment variable EGG_LANG to that value.

Languages included with Eggdrop: Danish, English, French, Finnish, German.

Log Files

Eggdrop is capable of logging various things, from channel chatter to partyline commands and file transfers.

Logfiles are normally kept for 24 hours. Afterwards, they will be renamed to “(logfilename).yesterday”. After 48 hours, they will be overwritten by the logfile of the next day.

set max-logs 20

This is the maximum number of concurrent logfiles that can be opened for writing at one time. At most, this value should be the maximum number of channels you expect to create log files for. There is no value for ‘infinity’; very few cases should ever require more than 20. A decrease to this value while running will require a restart (not rehash) of the bot. However, don’t decrease it below 5.

set max-logsize 0

This is the maximum size of your logfiles. Set it to 0 to disable. This value is in kilobytes, so ‘550’ would mean cycle logs when it reaches the size of 550 kilobytes. Note that this only works if you have keep-all-logs set to 0 (OFF).

set raw-log 0

This setting allows you the logging of raw incoming server traffic via console/log flag ‘r’, raw outgoing server traffic via console/log mode ‘v’, raw incoming botnet traffic via console/log mode ‘t’, raw outgoing botnet traffic via console/log mode ‘u’, raw outgoing share traffic via console/log mode ‘g’, and raw incoming share traffic via console/log mode ‘h’. These flags can create a large security hole, allowing people to see user passwords. This is now restricted to +n users only. Please choose your owners with care.

logfile <logflags> <channel> “logs/logfile”

This setting tells the bot what should be logged, from where, and to which file.

Logfile flags:

b

information about bot linking and userfile sharing

c

commands

d

misc debug information

g

raw outgoing share traffic

h

raw incoming share traffic

j

joins, parts, quits, and netsplits on the channel

k

kicks, bans, and mode changes on the channel

l

linked bot messages

m

private msgs, notices and ctcps to the bot

o

misc info, errors, etc (IMPORTANT STUFF)

p

public text on the channel

r

raw incoming server traffic

s

server connects, disconnects, and notices

t

raw incoming botnet traffic

u

raw outgoing botnet traffic

v

raw outgoing server traffic

w

wallops (make sure the bot sets +w in init-server)

x

file transfers and file-area commands

Note that modes d, h, r, t, and v can fill disk quotas quickly. There are also eight user-defined levels (1-8) which can be used by Tcl scripts.

Each logfile belongs to a certain channel. Events of type ‘k’, ‘j’, and ‘p’ are logged to whatever channel they happened on. Most other events are currently logged to every channel. You can make a logfile belong to all channels by assigning it to channel “*”.

Examples:

logfile mco * "logs/eggdrop.log"
logfile jpk #lamest "logs/lamest.log"

In ‘eggdrop.log’, put private msgs/ctcps, commands, misc info, and errors from any channel.

In ‘lamest.log’, log all joins, parts, kicks, bans, public chatter, and mode changes from #lamest.

set log-time 1

Use this feature to timestamp entries in the log file.

set timestamp-format “[%H:%M:%S]”

Set the following to the timestamp for the logfile entries. Popular times might be “[%H:%M]” (hour, min), or “[%H:%M:%S]” (hour, min, sec). Read ‘man strftime’ for more formatting options. Keep it below 32 chars.

set keep-all-logs 0

If you want to keep your logfiles forever, turn this setting on. All logfiles will get the suffix “.[day, 2 digits][month, 3 letters][year, 4 digits]”. Note that your quota/hard-disk might be filled by this, so check your logfiles often and download them.

set switch-logfiles-at 300

You can specify when Eggdrop should switch logfiles and start fresh. use military time for this setting. 300 is the default, and describes 03:00 (AM).

set quiet-save 0

“Writing user file…” and “Writing channel file…” messages won’t be logged anymore if this option is enabled. If you set it to 2, the “Backing up user file…” and “Backing up channel file…” messages will also not be logged. In addition to this, you can disable the “Switching logfiles…” and the new date message at midnight, by setting this to 3.

set logfile-suffix “.%d%b%Y”

If keep-all-logs is 1, this setting will define the suffix of the logfiles. The default will result in a suffix like “04May2000”. “%Y%m%d” will produce the often used yyyymmdd format. Read the strftime manpages for more options.

Console Settings

set console “mkcoblxs”

This is the default console mode. It uses the same event flags as the log files do. The console channel is automatically set to your “primary” channel, which is set in the modules section of the config file. Masters can change their console channel and modes with the ‘.console’ command.

File and Directory Settings

set userfile “LamestBot.user”

Specify here the filename your userfile should be saved as.

set pidfile “pid.LamestBot”

Specify here the filename Eggdrop will save its pid to. If no pidfile is specified, pid.(botnet-nick) will be used.

set help-path “help/”

Specify here where Eggdrop should look for help files. Don’t modify this setting unless you know what you’re doing!

set text-path “text/”

Specify here where Eggdrop should look for text files. This is used for certain Tcl and DCC commands.

set motd “text/motd”

The MOTD (Message Of The day) is displayed when people dcc chat or telnet to the bot. Look at doc/TEXT-SUBSTITUTIONS for options.

set telnet-banner “text/banner”

This banner will be displayed on telnet connections. Look at doc/text-substitutions.doc for options.

set userfile-perm 0600

This specifies what permissions the user, channel, and notes files should be set to. The octal values are the same as for the chmod system command.

To remind you:

|      u  g  o           u  g  o           u  g  o
|0600  rw-------   0400  r--------   0200  -w-------    u - user
|0660  rw-rw----   0440  r--r-----   0220  -w--w----    g - group
|0666  rw-rw-rw-   0444  r--r--r--   0222  -w--w--w-    o - others

Note that the default 0600 is the most secure one and should only be changed if you need your files for shell scripting or other external applications.

set mod-path “modules/”

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 :).

Botnet/Dcc/Telnet Settings

Settings in this section should be unimportant for you until you deal with botnets (multiple Eggdrops connected together to maximize efficiency). You should read doc/BOTNET before modifying these settings.

set botnet-nick “LlamaBot” (disabled by default)

If you want to use a different nickname on the botnet than you use on IRC (i.e. if you’re on an un-trusted botnet), un-comment this line and set it to the nick you would like to use.

listen <port> <mode>

This opens a telnet port by which you and other bots can interact with the Eggdrop by telneting in.

There are more options for the listen command in doc/tcl-commands.doc. Note that if you are running more than one bot on the same machine, you will want to space the telnet ports at LEAST 5 apart, although 10 is even better.

Valid ports are typically anything between 1025 and 65535 assuming the port is not already in use.

If you would like the bot to listen for users and bots in separate ports, use the following format:

listen 3333 bots
listen 4444 users

If you wish to use only one port, use this format:

listen 3333 all

You can setup a SSL port by prepending a plus sign to it:

listen +5555 all

You need to un-comment this line and change the port number in order to open the listen port. You should not keep this set to 3333.

set remote-boots 2

This setting defines whether or not people can boot users on the Eggdrop from other bots in your botnet. Valid settings are:

0

allow no outside boots

1

allow boots from sharebots

2

allow any boots

set share-unlinks 1

This setting prohibits Eggdrop to unlink from share bots if an remote bots tells so.

set protect-telnet 0

This setting will drop telnet connections not matching a known host.

set dcc-sanitycheck 0

This setting will make the bot ignore DCC chat requests which appear to have bogus information on the grounds that the user may have been trying to make the bot connect to somewhere that will get it into trouble, or that the user has a broken client, in which case the connect wouldn’t work anyway.

set ident-timeout 5

This setting defines the time in seconds the bot should wait for ident reply before the lookup fails. The default ident on timeout is ‘telnet’.

set require-p 0

Define here whether or not a +o user still needs the +p flag to dcc the bot.

set open-telnets 0

If you want people allow to telnet in and type ‘NEW’ to become a new user, set this to 1. This is similar to the ‘hello’ msg command. The protect-telnet setting must be set to 0 to use this.

set stealth-telnets 0

If you don’t want Eggdrop to identify itself as an Eggdrop on a telnet connection, set this setting to 1. Eggdrop will display ‘Nickname’ instead.

set use-telnet-banner 0

If you want Eggdrop to display a banner when telneting in, set this setting to 1. The telnet banner is set by ‘set telnet-banner’.

set connect-timeout 15

This setting defines a time in seconds that the bot should wait before a dcc chat, telnet, or relay connection times out.

set dcc-flood-thr 3

Specify here the number of lines to accept from a user on the partyline within 1 second before they are considered to be flooding and therefore get booted.

set telnet-flood 5:60

Define here how many telnet connection attempts in how many seconds from the same host constitute a flood. The correct format is Attempts:Seconds.

set paranoid-telnet-flood 1

If you want telnet-flood to apply even to +f users, set this setting to 1.

set resolve-timeout 15

Set here the amount of seconds before giving up on hostname/address lookup (you might want to increase this if you are on a slow network).

Advanced Settings

set firewall “!sun-barr.ebay:3666”

Set this to your socks host if your Eggdrop sits behind a firewall. If you use a Sun “telnet passthru” firewall, prefix the host with a “!”.

set nat-ip “127.0.0.1”

If you have a NAT firewall (your box has an IP in one of the following ranges: 192.168.0.0-192.168.255.255, 172.16.0.0-172.31.255.255, 10.0.0.0-10.255.255.255 and your firewall transparently changes your address to a unique address for your box) or you have IP masquerading between you and the rest of the world, and /dcc chat, /ctcp chat or userfile sharing aren’t working, enter your outside IP here. This IP is used for transfers only, and has nothing to do with the vhost4/6 or listen settings. You may still need to set them.

set reserved-portrange 2010:2020

If you want all dcc file transfers to use a particular portrange either because you’re behind a firewall, or for other security reasons, set it here.

set ignore-time 15

Set the time in minutes that temporary ignores should last.

set hourly-updates 00

Define here what Eggdrop considers ‘hourly’. All calls to it, including such things as note notifying or userfile saving, are affected by this.

Example:

set hourly-updates 15

The bot will save its userfile 15 minutes past every hour.

set owner “MrLame, MrsLame”

Un-comment this line and set the list of owners of the bot. You NEED to change this setting.

set notify-newusers “$owner”

Who should a note be sent to when new users are learned?

set default-flags “hp”

Enter the flags that all new users should get by default. See ‘.help whois’ on the partyline for a list of flags and their descriptions.

set whois-fields “url birthday”

Enter all user-defined fields that should be displayed in a ‘.whois’. This will only be shown if the user has one of these extra fields. You might prefer to comment this out and use the userinfo1.0.tcl script which provides commands for changing all of these.

#unbind dcc n tcl *dcc:tcl

#unbind dcc n set *dcc:set

Uncomment these two lines if you wish to disable the .tcl and .set commands. If you select your owners wisely, you should be okay leaving these enabled.

set must-be-owner 1

If you enable this setting, only permanent owners (owner setting) will be able to use .tcl and .set. Moreover, if you want to only let permanent owners use .dump, then set this to 2.

unbind dcc n simul *dcc:simul

Comment out this line to add the ‘simul’ partyline command (owners can manipulate other people on the party line). Please select owners wisely and use this command ethically!

set max-dcc 50

Set here the maximum number of dcc connections you will allow. You can increase this later, but never decrease it.

set allow-dk-cmds 1

Enable this setting if you want +d & +k users to use commands bound as -|-.

set dupwait-timeout 5

If your Eggdrop rejects bots that actually have already disconnected from another hub, but the disconnect information has not yet spread over the botnet due to lag, use this setting. The bot will wait dupwait-timeout seconds before it checks again and then finally reject the bot.

set cidr-support 0

Enables cidr support for b/e/I modes if set to 1. This means the bot will understand and match modes in cidr notation, and will be able to put and enforce such bans or unban itself, if banned with a cidr mask. Do NOT set this, if your network/server does not support cidr!

SSL Settings

Settings in this section take effect when eggdrop is compiled with TLS support.

set ssl-privatekey “eggdrop.key”

File containing your private key, needed for the SSL certificate (see below). You can create one issuing the following command:

openssl genrsa -out eggdrop.key 4096

It will create a 4096 bit RSA key, strong enough for eggdrop. This is required for SSL hubs/listen ports, secure file transfer and /ctcp botnick schat For your convenience, you can type ‘make sslcert’ after ‘make install’ and you’ll get a key and a certificate in your DEST directory.

set ssl-certificate “eggdrop.crt”

Specify the filename where your SSL certificate is located. If you don’t set this, eggdrop will not be able to act as a server in SSL connections, as with most ciphers a certificate and a private key are required on the server side. Must be in PEM format. If you don’t have one, you can create it using the following command:

openssl req -new -key eggdrop.key -x509 -out eggdrop.crt -days 365

This is required for SSL hubs/listen ports, secure file transfer and /ctcp botnick schat For your convenience, you can type ‘make sslcert’ after ‘make install’ and you’ll get a key and a certificate in your DEST directory.

set ssl-verify-depth 9

Sets the maximum depth for the certificate chain verification that shall be allowed for ssl. When certificate verification is enabled, any chain exceeding this depth will fail verification.

set ssl-capath “/etc/ssl/”
set ssl-cafile “”
Specify the location at which CA certificates for verification purposes are located. These certificates are trusted. If you don’t set this, certificate verification will not work.
set ssl-ciphers “”

Specify the list of ciphers (in order of preference) allowed for use with ssl. The cipher list is one or more cipher strings separated by colons, commas or spaces. Unavailable ciphers are silently ignored unless no usable cipher could be found. For the list of possible cipher strings and their meanings, please refer to the ciphers(1) manual. Note: if you set this, the value replaces any ciphers OpenSSL might use by default. To include the default ciphers, you can put DEFAULT as a cipher string in the list. For example:

set ssl-ciphers "DEFAULT ADH"

… will make eggdrop allow the default OpenSSL selection plus anonymous DH ciphers.

set ssl-ciphers "ALL"

… will make eggdrop allow all ciphers supported by OpenSSL, in a reasonable order.

set ssl-cert-auth 0

Enable certificate authorization. Set to 1 to allow users and bots to identify automatically by their certificate fingerprints. Setting it to 2 to will force fingerprint logins. With a value of 2, users without a fingerprint set or with a certificate UID not matching their handle won’t be allowed to login on SSL enabled telnet ports. Fingerprints must be set in advance with the .fprint and .chfinger commands. NOTE: this setting has no effect on plain-text ports.

You can control SSL certificate verification using the following variables. All of them are flag-based. You can set them by adding together the numbers for all exceptions you want to enable. By default certificate verification is disabled and all certificates are assumed to be valid.

The options are the following:

0

disable verification

1

enable certificate verification

2

allow self-signed certificates

4

don’t check peer common or alt names

8

allow expired certificates

16

allow certificates which are not valid yet

32

allow revoked certificates

set ssl-verify-dcc 0

Control certificate verification for DCC chats (only /dcc chat botnick)

set ssl-verify-bots 0

Control certificate verification for linking to hubs

set ssl-verify-clients 0

Control certificate verification for SSL listening ports. This includes leaf bots connecting, users telneting in and /ctcp bot chat.

Modules

After the core settings, you should start loading modules. Modules are loaded by the command “loadmodule <module>”. Eggdrop looks for modules in the directory you specified by the mod-path setting in the files and directories section.

Please note that for different configurations, different modules are needed. Four examples:

Channel Security Bot:

This bot needs the channels, blowfish, console, dns, irc, and (if you like) ctcp modules loaded. More is not needed and makes the bot slower.

Public IRC Bot:

A public bot should have all modules available loaded since they provide all functions for everyday use.

Secure Filesys Bot:

This bot needs all normal IRC operating modules, but not the notes, seen, ctcp or share modules.

Limbo Bot:

A limbo bot (serves as a botnet hub outside IRC) just needs the channels, console, dns, and maybe notes or share modules loaded. Of course, an encryption module needs to be loaded here, too.

Scripts

The scripts section should be placed at the end of the config file. All modules should be loaded and their variables should be set at this point.

source scripts/script.tcl

This line loads script.tcl from the scripts directory inside your Eggdrop’s directory. All scripts should be put there, although you can place them where you like as long as you can supply a fully qualified path to them.

Some commonly loaded scripts are alltools.tcl and action.fix.tcl.

The appropriate source lines are:

source scripts/alltools.tcl
source scripts/action.fix.tcl

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/features.html000066400000000000000000000234471524730042700226770ustar00rootroot00000000000000 Eggdrop Features — Eggdrop 1.10.2 documentation

Eggdrop Features

Eggdrop is the most advanced IRC robot available. It has been under development since December 1993, and unlike most other bots, it is still regularly updated. Some of its features include:

  • Capability (CAP) support used to enable IRCv3 features. Eggdrop currently supports the following IRCv3 capability sets: account-notify, account-tag, away-notify, cap-notify, chghost, echo-message, extended-join, invite-notify, message-tags, monitor, SASL, server-time, setname, WHOX, and +typing.

  • Support for SSL-enabled IRC servers

  • Support for IPv6 users

  • Support for Twitch servers

  • Completely separate channel user lists like having a separate bot for each channel.

  • A “party line” available through dcc chat or telnet, with multiple channels, giving you the ability to talk to people without being affected by netsplits.

  • A “botnet”. A botnet consists of one or more bots linked together. This can allow bots to op each other securely, control floods efficiently, and share user lists, ban lists, exempt/invite lists, and ignore lists (if sharing is enabled).

  • User records are saved on disk and alterable via dcc chat. Each user can have a password (encrypted), a list of valid hostmasks, a set of access flags, etc.

  • The ability to “learn” new users (if you choose to let the bot do so) by letting users /MSG the bot “hello”. The bot will grant them automatic access of whatever type you specify (or even no access at all).

  • A file system where users can upload and download files in an environment that looks and acts (for the most part) like a typical UNIX system. It also has the ability to mark files and directories as hidden – inaccessible to people without certain user flags.

  • Console mode: you can view each channel through dcc chat or telnet, selectively looking at mode changes, joins and parts, channel talk, or any combination of the above.

  • A scripting language: commands and features can be easily added to the bot by means of the Tcl scripting language, giving you the power of TOTAL customization of your bot.

  • Module support: you can remove/add features to your bot by adding or removing modules.

Copyright (C) 1997 Robey Pointer

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/ipv6.html000066400000000000000000000265041524730042700217420ustar00rootroot00000000000000 IPv6 support — Eggdrop 1.10.2 documentation

IPv6 support Last revised: Apr 26, 2026

IPv6 support

This document provides information about IPv6 support which is a new eggdrop feature since version 1.8.0.

About

Eggdrop can be compiled with IPv6 support. To make use of this, you need an IPv6-enabled OS and IPv6 connectivity. Every possible type of TCP connection can be established over IPv6 now, which includes IRC connections, DCC connections, file transfer, botnet connections, Tcl script connections initiated with the listen/connect commands, telnet and ident lookups.

Installation

./configure and install as usual, the configure script will detect if your system supports IPv6 and will enable it automatically. You can override this behavior and manually enable or disable IPv6 with ./configure –enable-ipv6 or ./configure –disable-ipv6. All major operating systems and Cygwin have had full support for IPv6 for a heck of a long time.

Usage

You can use IPv6 addresses wherever you could specify IPv4 ones. IPs and hostnames are interchangeable everywhere. For certain settings and commands, you can enclose IPv6 addresses in square brackets to prevent the colon character (:) from being interpreted as a port separator. These are documented in the help files and the html documentation, so you can consult them when in doubt.

CTCP CHAT/CHAT4/CHAT6

When a user sends a CTCP chat request, the request is passed to the bot via the IRC server, hiding the user’s IP. Since Eggdrop is unable to ‘see’ the type IP of the user is using (IPv4 or IPv6), it is thus unable to determine whether it should send back an IPv4 or an IPv6 address for the user to connect to. To work around this problem, the CHAT4 and CHAT6 commands were added to Eggdrop to force it to present an IPv4 or IPv6 address for use with a DCC connection, respectively. Otherwise, the traditional CHAT command will likely result in the Eggdrop presenting an IPv4 address to the user. So in short, if you’re on an IPv6 address and want to use CTCP CHAT to initiate a DCC session for the partyline, use CHAT6, not CHAT as the CTCP argument.

Settings

There are four new IPv6 related config variables:

vhost4

set this to use a specific vhost with IPv4 connections. Can contain either an IP address or a hostname.

vhost6

set this to use a specific vhost with IPv6 connections. Can contain either an IPv6 address or a hostname.

prefer-ipv6

when a connection can be established through both IPv4 and IPv6. You can set this to 1 to prefer IPv6 or to 0 to prefer IPv4.

Other affected variables:

my-ip and my-hostname are removed now. Their function is split between vhost4 and the listen command.

nat-ip works with IPv4 as it used to. It has no meaning for IPv6 and is not queried for IPv6 connections.

Copyright (C) 2010 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/ircv3.html000066400000000000000000000265501524730042700221050ustar00rootroot00000000000000 IRCv3 support — Eggdrop 1.10.2 documentation

IRCv3 support Last revised: November 27, 2021

IRCv3 support

This document provides information about IRCv3 capabilities, as defined via specifications documented by the IRCv3 working group (https://ircv3.net/). Support for some of these specifications was added starting with version 1.9.0, and more capabilities are added as possible with new versions.

About

As more and more IRC servers began to develop and implement their own versions of the IRC protocol (generally defined in RFC1459 and RFC2812), a working group comprised of server, client, and bot developers decided to work together to document these features to make their implementation defined and standardized across servers. What emerged was the IRCv3 set of standards. The specifications developed by the IRCv3 working group was designed to be backwards compatible and are generally implemented via a CAP (capability) request sent at the initialization of an IRC session. A client can optinoally request these “extra” capabilities be enabled through the CAP request, with the assumption that the client can then support the capability requested and enabled. Not all servers or clients support the same capabilities, a general support list can be via the appropriate support table link at https://ircv3.net/.

Usage

Within eggdrop.conf, several common IRCv3-defined capabilities are enabled simply changing their setting to ‘1’. Other capabilities without explicit settings in eggdrop.conf may be requested by adding them in a space-separated list to the cap-request setting. For more information on what a specific IRCv3-defined capability does, please consult https://ircv3.net/irc/.

Supported CAP capabilities

The following capabilities are supported by Eggdrop:

  • CAP/CAP 302 requests

  • SASL 3.2

  • account-notify

  • account-tag

  • away-notify

  • BOT 005 mode

  • cap-notify

  • chghost

  • echo-message

  • extended-join

  • invite-notify

  • message-tags

  • Monitor

  • server-time

  • setname

  • userhost-in-names

  • +typing

Errata

  • Enabling echo-message will cause Eggdrop to trigger PUB/PUBM binds on its own messages (because now it can actually see them). This may cause unintentional functionality with some scripts

  • Enabling userhost-in-names will cause Eggdrop’s internal mechanisms to mark a channel’s userlist as synch’d upon receiving the NAMES list after a join, instead of waiting for a full WHO listing. This is done because the assumption is that userhost-in-names was enabled as a response to WHO queries being disabled on a server, which prevents Eggdrop from populating its userlist. To avoid unintended functionality, it is suggested that this capability only be enabled on servers that disable WHO queries.

Copyright (C) 2010 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/partyline.html000066400000000000000000000222471524730042700230650ustar00rootroot00000000000000 The Party Line — Eggdrop 1.10.2 documentation

Last revised: December 31, 2001

The Party Line

The most important way you will communicate with your bot is through the party line. The party line is accessible via DCC chat or telnet. It’s pretty much just a miniature, lag-less IRC (see doc/BOTNET), but it also consists of a console through which you can watch channel activity and give commands.

To enter the party line, DCC chat or open a telnet connection to your bot. It should ask for your password if you’ve set one. If you don’t have one set, use the /msg <bot> PASS <new password> command to set one.

The party line is actually split up into 200,000 “channels”. The console is available from each channel, but you can only talk to people who are on your current channel (just like IRC). Channel 0 is the main party line, while others are typically reserved for private conversations. Channels 1-99,999 are botnet wide chat channels and any user joining that channel anywhere on the botnet will be able to chat with you. Channels *0-*99,999 are local channels (only people on the bot you are on can chat with you on these channels).

Console commands start with a dot (.), similar to the slash (/) used for IRC commands. At any time, you can type “.help all” to get a list of all possible commands. To find out what a command does, use “.help <command>”. For example: “.help channel”.

When you’re on the party line, anything you type that doesn’t start with a dot (.), a comma (,), or an apostrophe (’) is considered to be broadcast to everyone else, just like talking on a channel. A message prefixed with a comma goes only to other bot owners (+n). A message prefixed with an apostrophe is sent to all users on the local bot only. You can change channels with the “.chat” command or even leave all channels with “.chat off”.

Copyright (C) 2002 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/patch.html000066400000000000000000000234031524730042700221500ustar00rootroot00000000000000 Patching Eggdrop — Eggdrop 1.10.2 documentation

Patching Eggdrop

Eggdrop is an open source project that depends on contributions of its users- like you! If you come across a feature you’d like to see implemented, find a bug, or think the documentation could be made more clear, you can open an issue on GitHub to discuss your ideas, and then submit a Pull Request to implement it!

Submitting a patch via GitHub

To create a patch via github:

  1. Fork the eggdrop git repo by logging in to your GitHub account and clicking the fork button at the top of the screen. Follow the directions it provides to configure your local repo.

  2. Enter the local directory and create a new branch for your patch:

    git checkout -b <DescriptiveBranchName>
    
  3. Make your changes

  4. Once you confirm your patch works, push the changes back to your GitHub repo - this is usually done by something similar to:

    git push origin <DescriptiveBranchName>
    
  5. Create a pull request by clicking on the “Pull Request” button on your GitHub page. Select the appropriate branches (usually eggheads/eggdrop/develop and yourUserName/eggdrop/YourBranchName)

  6. Fill in the Pull Request template and submit

  7. Pour yourself a cold one and bask in the warm feeling of contributing to the open source community! Karma++!

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/pbkdf2info.html000066400000000000000000000367601524730042700231070ustar00rootroot00000000000000 Encryption/Hashing — Eggdrop 1.10.2 documentation

Encryption/Hashing

With the release of Eggdrop 1.9.0, an updated crytopgraphy module (PBKDF2) was released for use with Eggdrop. PBKDF2 is a one-way hashing algorithm used to protect the contents of the password file, as well as for use via the Tcl interface. Prior to this, blowfish was used for cryptographic requirements, such as hashing passwords for storage in the userfile.

Background

Prior to Eggdrop 1.9.0, the blowfish module was included with Eggdrop to protect sensitive data such as passwords stored in the userfile. While there are no known practical attacks against blowfish at the time of this writing, it was decided that a more modern crypto solution was desirable to be included with Eggdrop. The PBKDF2 (Password-based Key Derivation Function 2) uses a password and salt value to create a password hash (the salt value ensures that the hashes of two identical passwords are different). This process is one-way, which means the hashes cannot be cryptographically reversed and thus are safe for storing in a file.

The default configuration of Eggdrop 1.9.0 has both the blowfish and pbkdf2 modules enabled (see Hybrid Configuration below). This will allow users upgrading a seamless transition to the PBKDF2 module. For users starting an Eggdrop for the first time, it is recommended to comment out the ‘loadmodule blowfish’ line, in order to implement the Solo Configuration.

Also of note, the maximum password length is increased to 30 with PBKDF2, up from 15 with Blowfish. The automatically-generated botnet passwords are now 30 characters instead of the maximum-allowed 16 used with the blowfish module, and pull from a larger character set than what was used with the blowfish module. Finally, if you are linking bots, you’ll need to ensure the same module is loaded on both bots (ie, if the hub bot is using the pbkdf2 module, the leaf bots must have pbkdf2 loaded as well in order to enable authentication checks).

Usage

There are two ways to implement PBKDF2- Hybrid configuration, which is recommended for transitioning an already-existing userfile to PBKDF2 by working with the blowfish module, and Solo configuration, which is recommended for use when starting a new Eggdrop for the first time.

Hybrid Configuration

With a hybrid configuration, Eggdrop will run both the blowfish and the pbkdf2 modules concurrently. This will allow Eggdrop to authenticate users against their existing blowfish passwords stored in the userfile. However, the first time a user logs in, the pbkdf2 module will hash the (correct) password they enter and save it to the userfile. The pbkdf2-hashed password will then be used for all future logins.

Enabling hybrid configuration

  1. BACK UP YOUR USERFILE! This is the file that usually ends with ‘.user’.

  2. Ensure

loadmodule blowfish

is added to the config file and not commented out (it should already be there).

  1. Ensure

loadmodule pbkdf2

is uncommented in the config file (or added, if this is a config file from 1.8)

  1. Start Eggdrop

  2. If this is your first time starting this Eggdrop, follow the instructions it gives you at startup to identify yourself. Otherwise, for an existing Eggdrop where you are already added to the userfile, log in as usual to the partyline via telnet or DCC, or authenticate via a message command like /msg bot op <password>

  3. Sit back and enjoy a cold beverage, you did it! Now encourage the rest of your users to log in so that their passwords are updated to the new format as well.

Solo configuration

With a solo configuration, Eggdrop will only run the pbkdf2 module. Eggdrop will not be able to authenticate against passwords in an already-existing userfile and thus will require every user to set a password again, as if they were just added to Eggdrop. This can be done via the PASS msg command (/msg bot PASS <password>) or by having a user with appropriate permissions (and an already-set password) log into the partyline and use the ‘.chpass’ command.

SECURITY CONSIDERATION: This configuration is not ideal for transitioning an existing userfile to PBKDF2. Without the blowfish module loaded, every user in the userfile essentially has no password set. This means any other user that matches a hostmask applied to a handle (!*@.aol.com, I’m looking at you) could set the password and gain access to that user’s Eggdrop account.

Enabling solo configuration

  1. BACK UP YOUR USERFILE! This is the file that usually ends with ‘.user’.

  2. Remove or comment:

    # loadmodule blowfish
    

from your config file.

  1. Ensure:

    loadmodule pbkdf2
    

is uncommented (or added, if this is a config file from 1.8) from your config file.

  1. Start Eggdrop

  2. If this is your first time starting this Eggdrop, follow the instructions it gives you at startup to identify yourself. Otherwise, for an existing Eggdrop where you are already added to the userfile, set a new password via /msg bot PASS <password>

  3. Sit back and enjoy a fancy lobster dinner, you did it! If there are other users already added to the bot, DEFINITELY encourage them to set a new password IMMEDIATELY!

Tcl Interface

The PBKDF2 module adds the ‘encpass2’ command to the Tcl library. This command takes a string and hashes it using the PBKDF2 algorithm, and returns a string in the following format:

$<PBK method>$rounds=<rounds>$<salt>$<password hash>

where ‘PBK method’ is the method specified in the configuration file, ‘rounds’ is the number of rounds specified in the configuration file, ‘salt’ is the value used for the salt, and ‘password hash’ is the output of the hashing algorithm.

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/python.html000066400000000000000000000571341524730042700224020ustar00rootroot00000000000000 Using the Python Module — Eggdrop 1.10.2 documentation

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.

The minimum supported Python version is 3.8 and we do require the Global Interpreter Lock for thread safety, even if you use latest Python.

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

If you need to install dependencies we recommend using virtual environments, see https://docs.python.org/3/library/venv.html for more details.

To create the virtual environment in a hidden directory called .venv (only necessary once):

cd eggdrop && python3 -m venv .venv

To install a python package in the above .venv directory (in this example, the requests package):

cd eggdrop && source .venv/bin/activate && pip install requests

Starting eggdrop with activated venv (must be run every time for proper functionality):

cd eggdrop && source .venv/bin/activate && ./eggdrop

You always need to start Eggdrop with the activated venv to set the necessary environment variables.

Reloading Python Scripts

Unfortunately, reloading python scripts with rehash like Tcl scripts is currently unsupported. Scripts can unbind their existing binds and re-bind them on being loaded again (see the bind section). For now, you should restart your bot when the Python scripts change.

You can (should?) also write scripts that manually unload their binds upon a reshash, example code looks like this:

# Create a list to track the join binds
if 'JOIN_BINDS' in globals():
  for joinbind in JOIN_BINDS:
    joinbind.unbind()
  del JOIN_BINDS

JOIN_BINDS = list()

<...>

# Create the binds in the script like this
JOIN_BINDS.append(bind("join", "*", "*", joinGreetUser))
JOIN_BINDS.append(bind("join", "o", "*", joinGreetOp))

Multithreading and async

pysource loads a Python script in the main Eggdrop thread but is free to use both async Python and threads.

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 <arguments>

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 Bind Types. The eggdrop.tcl.bind command should not be used as it will attempt to call a Tcl proc.

The bind command returns a PythonBind object that has an unbind method:

x = bind("pub", "*", "!foo", myproc)
x.unbind()

parse_tcl_list <string>

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 <string>

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:

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).

Copyright (C) 2000 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/tcl-commands.html000066400000000000000000006364171524730042700234510ustar00rootroot00000000000000 Eggdrop Tcl Commands — Eggdrop 1.10.2 documentation

Eggdrop Tcl Commands Last revised: January 6, 2024

Eggdrop Tcl Commands

This is an exhaustive list of all the Tcl commands added to Eggdrop. All of the normal Tcl built-in commands are still there, of course, but you can also use these to manipulate features of the bot. They are listed according to category.

This list is accurate for Eggdrop v1.10.2. Most scripts written for the v1.3, v1.4, 1.6, 1.8, and 1.9 series of Eggdrop should probably work in their current form, with only a very few needing minor modifications. Scripts which were written for v0.9, v1.0, v1.1 or v1.2 will probably not work without modification.

Output Commands

putserv <text> [options]

Description: sends text to the server, like ‘.dump’ (intended for direct server commands); output is queued so that the bot won’t flood itself off the server.

Options: -next push messages to the front of the queue -normal no effect

Returns: nothing

Module: server

puthelp <text> [options]

Description: sends text to the server, like ‘putserv’, but it uses a different queue intended for sending messages to channels or people.

Options: -next push messages to the front of the queue -normal no effect

Returns: nothing

Module: server

putquick <text> [options]

Description: sends text to the server, like ‘putserv’, but it uses a different (and faster) queue.

Options: -next push messages to the front of the queue -normal no effect

Returns: nothing

Module: server

putnow <text> [-oneline]

Description: sends text to the server immediately, bypassing all queues. Use with caution, as the bot may easily flood itself off the server.

Options: -oneline send text up to the first r or n, discarding the rest

Returns: nothing

Module: server

putkick <channel> <nick,nick,…> [reason]

Description: sends kicks to the server and tries to put as many nicks into one kick command as possible.

Returns: nothing

Module: irc

putlog <text>

Description: logs <text> to the logfile and partyline if the ‘misc’ flag (o) is active via the ‘logfile’ config file setting and the ‘.console’ partyline setting, respectively.

Returns: nothing

Module: core

putcmdlog <text>

Description: logs <text> to the logfile and partyline if the ‘cmds’ flag (c) is active via the ‘logfile’ config file setting and the ‘.console’ partyline setting, respectively.

Returns: nothing

Module: core

putxferlog <text>

Description: logs <text> to the logfile and partyline if the ‘files’ flag (x) is active via the ‘logfile’ config file setting and the ‘.console’ partyline setting, respectively.

Returns: nothing

Module: core

putloglev <flag(s)> <channel> <text>

Description: logs <text> to the logfile and partyline at the log level of the specified flag. Use “*” in lieu of a flag to indicate all log levels.

Returns: nothing

Module: core

dumpfile <nick> <filename>

Description: dumps file from the help/text directory to a user on IRC via msg (one line per msg). The user has no flags, so the flag bindings won’t work within the file.

Returns: nothing

Module: core

queuesize [queue]

Returns: the number of messages in all queues. If a queue is specified, only the size of this queue is returned. Valid queues are: mode, server, help.

Module: server

clearqueue <queue>

Description: removes all messages from a queue. Valid arguments are: mode, server, help, or all.

Returns: the number of deleted lines from the specified queue.

Module: server

cap <ls/values/req/enabled/raw> [arg]

Description: displays CAP status or sends a raw CAP command to the server. “ls” will list the capabilities Eggdrop is internally tracking as supported by the server. “values” will list all capabilities and their associated CAP 302 values (if any) as a key/value pair, and “values” with a capability name as arg will list the values associated for the capability. “enabled” will list the capabilities Eggdrop is internally tracking as negotiated with the server. “req” will request the capabilities listed in “arg” from the server. “raw” will send a raw CAP command to the server. The arg field is a single argument, and should be submitted as a single string. For example, to request capabilities foo and bar, you would use [cap req “foo bar”], and for example purposes, sending the same request as a raw command would be [cap raw “REQ :foo bar”].

Returns: a list of CAP capabilities for the “enabled” and “ls” sub-commands; a dict of capability/value pairs for the “values” command or a list if “values” if followed by an argument; otherwise nothing.

Module: server

tagmsg <tags> <target>

Description: sends an IRCv3 TAGMSG command to the target. Only works if message-tags has been negotiated with the server via the cap command. tags is a Tcl dict (or space-separated string) of the tags you wish to send separated by commas (do not include the @prefix), and target is the nickname or channel you wish to send the tags to. To send a tag only (not a key/value pair), use a “” as the value for a key in a dict, or a “{}” if you are sending as a space-separated string.

Examples:

set mytags [dict create +foo bar moo baa +last “”]; tagmsg $mytags #channel tagmsg “+foo bar moo baa +last {}” #channel

Returns: nothing

Module: server

server add <ip/host> [[+]port [password]]

Description: adds a server to the list of servers Eggdrop will connect to. Prefix the port with ‘+’ to indicate an SSL-protected port. A port value is required if password is to be specified. The SSL status (+) of the provided port is matched against as well (ie, 7000 is not the same as +7000).

Returns: nothing

Module: server

server remove <ip/host> [[+]port]

Description: removes a server from the list of servers Eggdrop will connect to. If no port is provided, all servers matching the ip or hostname provided will be removed, otherwise only the ip/host with the corresponding port will be removed. The SSL status (+) of the provided port is matched against as well (ie, 7000 is not the same as +7000).

Returns: nothing

Module: server

server list

Description: lists all servers currently added to the bots internal server list

Returns: A list of lists in the format {{hostname} {port} {password}}

Module: server

User Record Manipulation Commands

countusers

Returns: number of users in the bot’s database

Module: core

validuser <handle>

Returns: 1 if a user by that name exists; 0 otherwise

Module: core

finduser [-account] <value>

Description: finds the internal user record which most closely matches the given value. When used with the -account flag, value is a services account name, otherwise by default value is a string in the hostmask format of nick!user@host.

Returns: the handle found, or “*” if none

Module: core

userlist [flags]

Returns: a list of users on the bot. You can use the flag matching system here ([global]{&/|}[chan]{&/|}[bot]). ‘&’ specifies “and”; ‘|’ specifies “or”.

Module: core

passwdok <handle> <pass>

Description: checks the password given against the user’s password. Check against the password “-” to find out if a user has no password set.

Returns: 1 if the password matches for that user; 0 otherwise. Or if we are checking against the password “-”: 1 if the user has no password set; 0 otherwise.

Module: core

getuser <handle> [entry-type] [extra info]

Description: an interface to the new generic userfile support. Without an entry-type, it returns a flat key/value list (dict) of all set entries. Valid entry types are:

ACCOUNT

returns the list of service accounts associated with the user

BOTFL

returns the current bot-specific flags for the user (bot-only)

BOTADDR

returns a list containing the bot’s address, bot listen port, and user listen port

HOSTS

returns a list of hosts for the user

LASTON

returns a list containing the unixtime last seen and the last seen place. LASTON #channel returns the time last seen time for the channel or 0 if no info exists.

INFO

returns the user’s global info line

XTRA

returns the user’s XTRA info

COMMENT

returns the master-visible only comment for the user

HANDLE

returns the user’s handle as it is saved in the userfile

PASS

returns the user’s encrypted password

For additional custom user fields, to include the deprecated “EMAIL” and “URL” fields, reference scripts/userinfo.tcl.

Returns: info specific to each entry-type

Module: core

setuser <handle> <entry-type> [extra info]

Description: this is the counterpart of getuser. It lets you set the various values. Other then the ones listed below, the entry-types are the same as getuser’s.

Type

Extra Info

ACCOUNT

[account] If no value is specified, all accounts for the user will be cleared. Otherwise, only a single account will be added to the account list

PASS

<password>

Password string (Empty value will clear the password)

BOTADDR

<address> [bot listen port] [user listen port]

Sets address, bot listen port and user listen port. If no listen ports are specified, only the bot address is updated. If only the bot listen port is specified, both the bot and user listen ports are set to the bot listen port.

HOSTS

[hostmask]

If no value is specified, all hosts for the user will be cleared. Otherwise, only 1 hostmask is added :P

LASTON

This setting has 3 forms.

<unixtime> <place>

sets global LASTON time. Standard values used by Eggdrop for <place> are partyline, linked, unlinked, filearea, <#channel>, and <@remotebotname>, but can be set to anything.

<unixtime>

sets global LASTON time (leaving the place field empty)

<unixtime> <channel>

sets a user’s LASTON time for a channel (if it is a valid channel)

Returns: nothing

Module: core

chhandle <old-handle> <new-handle>

Description: changes a user’s handle

Returns: 1 on success; 0 if the new handle is invalid or already used, or if the user can’t be found

Module: core

chattr <handle> [changes [channel]]

Description: changes the attributes for a user record, if you include any. Changes are of the form ‘+f’, ‘-o’, ‘+dk’, ‘-o+d’, etc. If changes are specified in the format of |<changes> <channel>, the channel-specific flags for that channel are altered. You can now use the +o|-o #channel format here too.

Returns: new flags for the user (if you made no changes, the current flags are returned). If a channel was specified, the global AND the channel-specific flags for that channel are returned in the format of globalflags|channelflags. “*” is returned if the specified user does not exist.

Module: core

botattr <handle> [changes [channel]]

Description: similar to chattr except this modifies bot flags rather than normal user attributes.

Returns: new flags for the bot (if you made no changes, the current flags are returned). If a channel was specified, the global AND the channel-specific flags for that channel are returned in the format of globalflags|channelflags. “*” is returned if the specified bot does not exist.

Module: core

matchattr <handle> <flags> [channel]

Description: checks if the flags of the specified user match the flags provided. “flags” is of the form:

[+/-]<global flags>[&/|<channel flags>[&/|<bot flags>]]

Either | or & can be used as a separator between global, channel, and bot flags, but only one separator can be used per flag section. A ‘+’ is used to check if a user has the subsequent flags, and a ‘-’ is used to check if a user does NOT have the subsequent flags. Please see Flag Masks for additional information on flag usage.

Returns: 1 if the specified user has the flags matching the provided mask; 0 otherwise

Module: core

adduser <handle> [hostmask]

Description: creates a new user entry with the handle and hostmask given (with no password and the default flags)

Returns: 1 if successful; 0 if the handle already exists

Module: core

addbot <handle> <address> [botport [userport]]

Description: adds a new bot to the userlist with the handle and botaddress given (with no password and no flags). <address> format is one of:

  • ipaddress

  • ipv4address:botport/userport [DEPRECATED]

  • [ipv6address]:botport/userport [DEPRECATED]

NOTE 1: The []s around the ipv6address argument are literal []s, not optional arguments. NOTE 2: In the deprecated formats, an additional botport and/or userport given as follow-on arguments are ignored.

Returns: 1 if successful; 0 if the bot already exists or a port is invalid

Module: core

deluser <handle>

Description: attempts to erase the user record for a handle

Returns: 1 if successful, 0 if no such user exists

Module: core

delhost <handle> <hostmask>

Description: deletes a hostmask from a user’s host list

Returns: 1 on success; 0 if the hostmask (or user) doesn’t exist

Module: core

addchanrec <handle> <channel>

Description: adds a channel record for a user

Returns: 1 on success; 0 if the user or channel does not exist

Module: channels

delchanrec <handle> <channel>

Description: removes a channel record for a user. This includes all associated channel flags.

Returns: 1 on success; 0 if the user or channel does not exist

Module: channels

haschanrec <handle> <channel>

Returns: 1 if the given handle has a chanrec for the specified channel; 0 otherwise

Module: channels

getchaninfo <handle> <channel>

Returns: info line for a specific channel (behaves just like ‘getinfo’)

Module: channels

setchaninfo <handle> <channel> <info>

Description: sets the info line on a specific channel for a user. If info is “none”, it will be removed.

Returns: nothing

Module: channels

newchanban <channel> <ban> <creator> <comment> [lifetime] [options]

Description: adds a ban to the ban list of a channel; creator is given credit for the ban in the ban list. lifetime is specified in minutes. If lifetime is not specified, ban-time (usually 60) is used. Setting the lifetime to 0 makes it a permanent ban.

Options:

sticky

forces the ban to be always active on a channel, even with dynamicbans on

Returns: nothing

Module: channels

newban <ban> <creator> <comment> [lifetime] [options]

Description: adds a ban to the global ban list (which takes effect on all channels); creator is given credit for the ban in the ban list. lifetime is specified in minutes. If lifetime is not specified, default-ban-time (usually 120) is used. Setting the lifetime to 0 makes it a permanent ban.

Options:

sticky

forces the ban to be always active on a channel, even with dynamicbans on

Returns: nothing

Module: channels

newchanexempt <channel> <exempt> <creator> <comment> [lifetime] [options]

Description: adds a exempt to the exempt list of a channel; creator is given credit for the exempt in the exempt list. lifetime is specified in minutes. If lifetime is not specified, exempt-time (usually 60) is used. Setting the lifetime to 0 makes it a permanent exempt. The exempt will not be removed until the corresponding ban has been removed. For timed bans, once the time period has expired, the exempt will not be removed until the corresponding ban has either expired or been removed.

Options:

sticky

forces the exempt to be always active on a channel, even with dynamicexempts on

Returns: nothing

Module: channels

newexempt <exempt> <creator> <comment> [lifetime] [options]

Description: adds a exempt to the global exempt list (which takes effect on all channels); creator is given credit for the exempt in the exempt list. lifetime is specified in minutes. If lifetime is not specified, exempt-time (usually 60) is used. Setting the lifetime to 0 makes it a permanent exempt. The exempt will not be removed until the corresponding ban has been removed.

Options:

sticky

forces the exempt to be always active on a channel, even with dynamicexempts on

Returns: nothing

Module: channels

newchaninvite <channel> <invite> <creator> <comment> [lifetime] [options]

Description: adds a invite to the invite list of a channel; creator is given credit for the invite in the invite list. lifetime is specified in minutes. If lifetime is not specified, invite-time (usually 60) is used. Setting the lifetime to 0 makes it a permanent invite. The invite will not be removed until the channel has gone -i.

Options:

sticky

forces the invite to be always active on a channel, even with dynamicinvites on

Returns: nothing

Module: channels

newinvite <invite> <creator> <comment> [lifetime] [options]

Description: adds a invite to the global invite list (which takes effect on all channels); creator is given credit for the invite in the invite list. lifetime is specified in minutes. If lifetime is not specified, invite-time (usually 60) is used. Setting the lifetime to 0 makes it a permanent invite. The invite will not be removed until the channel has gone -i.

Options:

sticky

forces the invite to be always active on a channel, even with dynamicinvites on

Returns: nothing

Module: channels

stickban <banmask> [channel]

Description: makes a ban sticky, or, if a channel is specified, then it is set sticky on that channel only.

Returns: 1 on success; 0 otherwise

Module: channels

unstickban <banmask> [channel]

Description: makes a ban no longer sticky, or, if a channel is specified, then it is unstuck on that channel only.

Returns: 1 on success; 0 otherwise

Module: channels

stickexempt <exemptmask> [channel]

Description: makes an exempt sticky, or, if a channel is specified, then it is set sticky on that channel only.

Returns: 1 on success; 0 otherwise

Module: channels

unstickexempt <exemptmask> [channel]

Description: makes an exempt no longer sticky, or, if a channel is specified, then it is unstuck on that channel only.

Returns: 1 on success; 0 otherwise

Module: channels

stickinvite <invitemask> [channel]

Description: makes an invite sticky, or, if a channel is specified, then it is set sticky on that channel only.

Returns: 1 on success; 0 otherwise

Module: channels

unstickinvite <invitemask> [channel]

Description: makes an invite no longer sticky, or, if a channel is specified, then it is unstuck on that channel only.

Returns: 1 on success; 0 otherwise

Module: channels

killchanban <channel> <ban>

Description: removes a ban from the ban list for a channel

Returns: 1 on success; 0 otherwise

Module: channels

killban <ban>

Description: removes a ban from the global ban list

Returns: 1 on success; 0 otherwise

Module: channels

killchanexempt <channel> <exempt>

Description: removes an exempt from the exempt list for a channel

Returns: 1 on success; 0 otherwise

Module: channels

killexempt <exempt>

Description: removes an exempt from the global exempt list

Returns: 1 on success; 0 otherwise

Module: channels

killchaninvite <channel> <invite>

Description: removes an invite from the invite list for a channel

Returns: 1 on success; 0 otherwise

Module: channels

killinvite <invite>

Description: removes an invite from the global invite list

Returns: 1 on success; 0 otherwise

Module: channels

ischanjuped <channel>

Returns: 1 if the channel is juped, and the bot is unable to join; 0 otherwise

Module: channels

isban <ban> [channel [-channel]]

Returns: 1 if the specified ban is in the global ban list; 0 otherwise. If a channel is specified, that channel’s ban list is checked as well. If the -channel flag is used at the end of the command, *only* the channel bans are checked.

Module: channels

ispermban <ban> [channel [-channel]]

Returns: 1 if the specified ban is in the global ban list AND is marked as permanent; 0 otherwise. If a channel is specified, that channel’s ban list is checked as well. If the -channel flag is used at the end of the command, *only* the channel bans are checked.

Module: channels

isexempt <exempt> [channel [-channel]]

Returns: 1 if the specified exempt is in the global exempt list; 0 otherwise. If a channel is specified, that channel’s exempt list is checked as well. If the -channel flag is used at the end of the command, *only* the channel exempts are checked.

Module: channels

ispermexempt <exempt> [channel [-channel]]

Returns: 1 if the specified exempt is in the global exempt list AND is marked as permanent; 0 otherwise. If a channel is specified, that channel’s exempt list is checked as well. If the -channel flag is used at the end of the command, *only* the channel exempts are checked.

Module: channels

isinvite <invite> [channel [-channel]]

Returns: 1 if the specified invite is in the global invite list; 0 otherwise. If a channel is specified, that channel’s invite list is checked as well. If the -channel flag is used at the end of the command, *only* the channel invites are checked.

Module: channels

isperminvite <invite> [channel [-channel]]

Returns: 1 if the specified invite is in the global invite list AND is marked as permanent; 0 otherwise. If a channel is specified, that channel’s invite list is checked as well. If the -channel flag is used at the end of the command, *only* the channel invites are checked.

Module: channels

isbansticky <ban> [channel [-channel]]

Returns: 1 if the specified ban is marked as sticky in the global ban list; 0 otherwise. If a channel is specified, that channel’s ban list is checked as well. If the -channel flag is used at the end of the command, *only* the channel bans are checked.

Module: channels

isexemptsticky <exempt> [channel [-channel]]

Returns: 1 if the specified exempt is marked as sticky in the global exempt list; 0 otherwise. If a channel is specified, that channel’s exempt list is checked as well. If the -channel flag is used at the end of the command, *only* the channel exempts are checked.

Module: channels

isinvitesticky <invite> [channel [-channel]]

Returns: 1 if the specified invite is marked as sticky in the global invite list; 0 otherwise. If a channel is specified, that channel’s invite list is checked as well. If the -channel flag is used at the end of the command, *only* the channel invites are checked.

Module: channels

matchban <nick!user@host> [channel]

Returns: 1 if the specified nick!user@host matches a ban in the global ban list; 0 otherwise. If a channel is specified, that channel’s ban list is checked as well.

Module: channels

matchexempt <nick!user@host> [channel]

Returns: 1 if the specified nick!user@host matches an exempt in the global exempt list; 0 otherwise. If a channel is specified, that channel’s exempt list is checked as well.

Module: channels

matchinvite <nick!user@host> [channel]

Returns: 1 if the specified nick!user@host matches an invite in the global invite list; 0 otherwise. If a channel is specified, that channel’s invite list is checked as well.

Module: channels

banlist [channel]

Returns: a list of global bans, or, if a channel is specified, a list of channel-specific bans. Each entry is a sublist containing: hostmask, comment, expiration timestamp, time added, last time active, and creator. The three timestamps are in unixtime format.

Module: channels

exemptlist [channel]

Returns: a list of global exempts, or, if a channel is specified, a list of channel-specific exempts. Each entry is a sublist containing: hostmask, comment, expiration timestamp, time added, last time active, and creator. The three timestamps are in unixtime format.

Module: channels

invitelist [channel]

Returns: a list of global invites, or, if a channel is specified, a list of channel-specific invites. Each entry is a sublist containing: hostmask, comment, expiration timestamp, time added, last time active, and creator. The three timestamps are in unixtime format.

Module: channels

newignore <hostmask> <creator> <comment> [lifetime]

Description: adds an entry to the ignore list; creator is given credit for the ignore. lifetime is how many minutes until the ignore expires and is removed. If lifetime is not specified, ignore-time (usually 60) is used. Setting the lifetime to 0 makes it a permanent ignore.

Returns: nothing

Module: core

killignore <hostmask>

Description: removes an entry from the ignore list

Returns: 1 if successful; 0 otherwise

Module: core

ignorelist

Returns: a list of ignores. Each entry is a sublist containing: hostmask, comment, expiration timestamp, time added, and creator. The timestamps are in unixtime format.

Module: core

isignore <hostmask>

Returns: 1 if the ignore is in the list; 0 otherwise

Module: core

save

Description: writes the user and channel files to disk

Returns: nothing

Module: core

reload

Description: loads the userfile from disk, replacing whatever is in memory

Returns: nothing

Module: core

backup

Description: makes a simple backup of the userfile that’s on disk. If the channels module is loaded, this also makes a simple backup of the channel file.

Returns: nothing

Module: core

getting-users

Returns: 1 if the bot is currently downloading a userfile from a sharebot (and hence, user records are about to drastically change); 0 if not

Module: core

Channel Commands

channel add <name> [option-list]

Description: adds a channel record for the bot to monitor. The full list of possible options are given in doc/settings/mod.channels. Note that the channel options must be in a list (enclosed in {}).

Returns: nothing

Module: channels

channel set <name> <options…>

Description: sets options for the channel specified. options is a flat list of either +/-settings or key/value pairs. The full list of possible options are given in doc/settings/mod.channels. Note: Tcl code settings such as the need-* settings must be valid Tcl code as a single word, for example channel set #lamest need-op { putmsg ChanServ "op #lamest" }

Returns: nothing

Module: channels

channel info <name>

Returns: a list of info about the specified channel’s settings.

Module: channels

channel get <name> [setting]

Returns: The value of the setting you specify. For flags, a value of 0 means it is disabled (-), and non-zero means enabled (+). If no setting is specified, a flat list of all available settings and their values will be returned.

Module: channels

channel remove <name>

Description: removes a channel record from the bot and makes the bot no longer monitor the channel

Returns: nothing

Module: channels

savechannels

Description: saves the channel settings to the channel-file if one is defined.

Returns: nothing

Module: channels

loadchannels

Description: reloads the channel settings from the channel-file if one is defined.

Returns: nothing

Module: channels

channels

Returns: a list of the channels the bot has a channel record for

Module: channels

channame2dname <channel-name>

chandname2name <channel-dname>

Description: these two functions are important to correctly support !channels. The bot differentiates between channel description names (chan dnames) and real channel names (chan names). The chan dnames are what you would normally call the channel, such as “!channel”. The chan names are what the IRC server uses to identify the channel. They consist of the chan dname prefixed with an ID; such as “!ABCDEchannel”.

For bot functions like isop, isvoice, etc. you need to know the chan dnames. If you communicate with the server, you usually get the chan name, though. That’s what you need the channame2dname function for.

If you only have the chan dname and want to directly send raw server commands, use the chandname2name command.

NOTE: For non-!channels, chan dname and chan name are the same.

Module: irc

isbotnick <nick>

Returns: 1 if the nick matches the botnick; 0 otherwise

Module: server

botisop [channel]

Returns: 1 if the bot has ops on the specified channel (or any channel if no channel is specified); 0 otherwise

Module: irc

botishalfop [channel]

Returns: 1 if the bot has halfops on the specified channel (or any channel if no channel is specified); 0 otherwise

Module: irc

botisvoice [channel]

Returns: 1 if the bot has a voice on the specified channel (or any channel if no channel is specified); 0 otherwise

Module: irc

botonchan [channel]

Returns: 1 if the bot is on the specified channel (or any channel if no channel is specified); 0 otherwise

Module: irc

isop <nickname> [channel]

Returns: 1 if someone by the specified nickname is on the channel (or any channel if no channel name is specified) and has ops; 0 otherwise

Module: irc

ishalfop <nickname> [channel]

Returns: 1 if someone by the specified nickname is on the channel (or any channel if no channel name is specified) and has halfops; 0 otherwise

Module: irc

wasop <nickname> <channel>

Returns: 1 if someone that just got opped/deopped in the chan had op before the modechange; 0 otherwise

Module: irc

washalfop <nickname> <channel>

Returns: 1 if someone that just got halfopped/dehalfopped in the chan had halfop before the modechange; 0 otherwise

Module: irc

isvoice <nickname> [channel]

Returns: 1 if someone by that nickname is on the channel (or any channel if no channel is specified) and has voice (+v); 0 otherwise

Module: irc

isidentified <nickname> [channel]

Description: determine if a user is identified to irc services. WARNING: this may not be accurate depending on the server and configuration. For accurate results, the server must support (and Eggdrop must have enabled via CAP) the account-notify and extended-join capabilities, and the server must understand WHOX requests (also known as raw 354 responses)

Returns: 1 if someone by the specified nickname is on the channel (or any channel if no channel name is specified) and is logged in); 0 otherwise.

Module: irc

isaway <nickname> [channel]

Description: determine if a user is marked as ‘away’ on a server. IMPORTANT: this command is only “mostly” reliable on its own when the IRCv3 away-notify capability is available and negotiated with the IRC server (if you didn’t add this to your config file, it likely isn’t enabled- you can confirm using the cap Tcl command). Additionally, there is no way for Eggdrop (or any client) to capture a user’s away status when the user first joins a channel (they are assumed present by Eggdrop on join). To use this command without the away-notify capability negotiated, or to get a user’s away status on join (via a JOIN bind), use refreshchan <channel> w on a channel the user is on, which will refresh the current away status stored by Eggdrop for all users on the channel.

Returns: 1 if Eggdrop is currently tracking someone by that nickname marked as ‘away’ (again, see disclaimer above) by an IRC server; 0 otherwise.

Module: irc

isircbot <nickname> [channel]

Description: determine if a user has denoted themselves as a bot via an ircd-defined user flag (declared via BOT in a server’s 005/ISUPPORT line). Due to server implementations, accurately monitoring this is incredibly fragile, as the flag can be added and removed by a user without any notification to other users. To ensure this status is current for use, it is recommended to use refreshchan <channel> w on a channel the user is on, which will refresh if the user is a bot or not for all users on the channel. If a server does not advertise BOT in its ISUPPORT line but still supports it (currently the case for unrealircd), you can manually set it by adding “BOT=B” (or whatever flag is used) to the isupport-default setting in your eggdrop.conf file.

Returns: 1 if Eggdrop is currently tracking someone by that nickname marked as a bot by an IRC server; 0 otherwise.

onchan <nickname> [channel]

Returns: 1 if someone by that nickname is on the specified channel (or any channel if none is specified); 0 otherwise

Module: irc

monitor <add/delete/list/online/offline/status/clear> [nickname]

Description: interacts with the list of nicknames Eggdrop has asked the IRC server to track. valid sub-commands are add, delete, list, online, offline, status, and clear. The ‘add’ command sends ‘nickname’ to the server to track. The ‘delete’ command removes ‘nickname’ from being tracked by the server (or returns an error if the nickname is not present). The ‘list’ command returns a list of all nicknames the IRC server is tracking on behalf of Eggdrop. The ‘online’ command returns a string of tracked nicknames that are currently online. The ‘offline’ command returns a list of tracked nicknames that are currently offline.

Returns: The ‘add’ sub-command returns a ‘1’ if the nick was successfully added, a ‘0’ if the nick is already in the monitor list, and a ‘2’ if the nick could not be added. The ‘delete’ sub-command returns a ‘1’ if the nick is removed, or an error if the nick is not found. The ‘status’ sub-command returns a ‘1’ if ‘nickname’ is online or a 0 if ‘nickname’ is offline. The ‘clear’ command removes all nicknames from the list the server is monitoring.

Module: irc

accounttracking

Description: checks to see if the three required functionalities to enable proper account tracking are available (and enabled) to Eggdrop. This checks if the extended-join and account-notify IRCv3 capabilities are currently enabled, and checks if the server supports WHOX (based on the type of server selected in the config file, or the use-354 variable being set to 1 when selecting an “Other” server).

Returns: a ‘1’ if all three functionalities are present, a ‘0’ if one or more are missing.

Module: irc

getaccount <nickname> [channel]

Returns: the services account name associated with nickname, “*” if the user is not logged into services, or “” if eggdrop does not know the account status of the user.

NOTE: the three required IRC components for account tracking are: the WHOX feature, the extended-join IRCv3 capability and the account-notify IRCv3 capability. if only some of the three feature are available, eggdrop provides best-effort account tracking. please see doc/ACCOUNTS for additional information.

nick2hand <nickname> [channel]

Returns: the handle of a nickname on a channel. If a channel is not specified, the bot will check all of its channels. If the nick is not found, “” is returned. If the nick is found but does not have a handle, “*” is returned. If no channel is specified, all channels are checked.

Module: irc

account2nicks <account> [channel]

Returns: a de-duplicated Tcl list of the nickname(s) on the specified channel (if one is specified) whose nickname matches the given account; “” is returned if no match is found. This command will only work if a server supports (and Eggdrop has enabled) the account-notify and extended-join capabilities, and the server understands WHOX requests (also known as raw 354 responses). If no channel is specified, all channels are checked.

Module: irc

hand2nick <handle> [channel]

Returns: nickname of the first person on the specified channel (if one is specified) whose nick!user@host matches the given handle; “” is returned if no match is found. If no channel is specified, all channels are checked.

Module: irc

hand2nicks <handle> [channel]

Returns: a de-duplicated Tcl list of the nickname(s) on the specified channel (if one is specified) whose nick!user@host matches the given handle; “” is returned if no match is found. If no channel is specified, all channels are checked.

Module: irc

handonchan <handle> [channel]

Returns: 1 if the the nick!user@host for someone on the channel (or any channel if no channel name is specified) matches for the handle given; 0 otherwise

Module: irc

ischanban <ban> <channel>

Returns: 1 if the specified ban is on the given channel’s ban list (not the bot’s banlist for the channel)

Module: irc

ischanexempt <exempt> <channel>

Returns: 1 if the specified exempt is on the given channel’s exempt list (not the bot’s exemptlist for the channel)

Module: irc

ischaninvite <invite> <channel>

Returns: 1 if the specified invite is on the given channel’s invite list (not the bot’s invitelist for the channel)

Module: irc

chanbans <channel>

Returns: a list of the current bans on the channel. Each element is a sublist of the form {<ban> <bywho> <age>}. age is seconds from the bot’s point of view

Module: irc

chanexempts <channel>

Returns: a list of the current exempts on the channel. Each element is a sublist of the form {<exempts> <bywho> <age>}. age is seconds from the bot’s point of view

Module: irc

chaninvites <channel>

Returns: a list of the current invites on the channel. Each element is a sublist of the form {<invites> <bywho> <age>}. age is seconds from the bot’s point of view

Module: irc

resetbans <channel>

Description: removes all bans on the channel that aren’t in the bot’s ban list and refreshes any bans that should be on the channel but aren’t

Returns: nothing

Module: irc

resetexempts <channel>

Description: removes all exempt on the channel that aren’t in the bot’s exempt list and refreshes any exempts that should be on the channel but aren’t

Returns: nothing

Module: irc

resetinvites <channel>

Description: removes all invites on the channel that aren’t in the bot’s invite list and refreshes any invites that should be on the channel but aren’t

Returns: nothing

Module: irc

resetchanidle [nick] <channel>

Description: resets the channel idle time for the given nick or for all nicks on the channel if no nick is specified.

Returns: nothing

Module: irc

resetchanjoin [nick] <channel>

Description: resets the channel join time for the given nick or for all nicks on the channel if no nick is specified.

Returns: nothing

Module: irc

resetchan <channel> [flags]

Description: clears the channel info Eggdrop is currently storing for a channel, then rereads the channel info from the server. Useful if Eggdrop gets into a bad state on a server with respect to a channel userlist, for example. If flags are specified, only the required information will be reset, according to the given flags. Available flags:

b

channel bans

e

channel exempts

I

channel invites

m

channel modes

w

memberlist (who & away info)

Returns: nothing

Module: irc

refreshchan <channel> [flags]

Description: An alternative to resetchan, refresh rereads the channel info from the server without first clearing out the previously stored information. Useful for updating a user’s away status without resetting their idle time, for example. If flags are specified, only the required information will be refreshed, according to the given flags. Available flags:

b

channel bans

e

channel exempts

I

channel invites

m

channel modes

t

channel topic

w

memberlist (who & away info)

Returns: nothing

Module: irc

getchanhost <nickname> [channel]

Returns: user@host of the specified nickname (the nickname is not included in the returned host). If a channel is not specified, bot will check all of its channels. If the nickname is not on the channel(s), “” is returned.

Module: irc

getchanjoin <nickname> <channel>

Returns: timestamp (unixtime format) of when the specified nickname joined the channel if available, 0 otherwise. Note that after a channel reset this information will be lost, even if previously available.

Module: irc

onchansplit <nick> [channel]

Returns: 1 if that nick is split from the channel (or any channel if no channel is specified); 0 otherwise

Module: irc

chanlist <channel> [flags][<&|>chanflags]

Description: lists all users on a channel Eggdrop has joined. flags are any global flags; the ‘&’ or ‘|’ denotes to look for channel specific flags, where ‘&’ will return users having ALL chanflags and ‘|’ returns users having ANY of the chanflags (See Flag Masks for additional information).

Returns: Searching for flags optionally preceded with a ‘+’ will return a list of nicknames that have all the flags listed. Searching for flags preceded with a ‘-’ will return a list of nicknames that do not have have any of the flags (differently said, ‘-’ will hide users that have all flags listed). If no flags are given, all of the nicknames on the channel are returned.

Please note that if you’re executing chanlist after a part or sign bind, the gone user will still be listed, so you can check for wasop, isop, etc.

Module: irc

getchanidle <nickname> <channel>

Returns: number of minutes that person has been idle; -1 if the specified user isn’t on the channel

Module: irc

getchanmode <channel>

Returns: string of the type “+ntik key” for the channel specified

Module: irc

jump [server [[+]port [password]]]

Description: jumps to the server specified, or (if none is specified) the next server in the bot’s serverlist. If you prefix the port with a plus sign (e.g. +6697), SSL connection will be attempted.

Returns: nothing

Module: server

pushmode <channel> <mode> [arg]

Description: sends out a channel mode change (ex: pushmode #lame +o goober) through the bot’s queuing system. All the mode changes will be sent out at once (combined into one line as much as possible) after the script finishes, or when ‘flushmode’ is called.

Returns: nothing

Module: irc

flushmode <channel>

Description: forces all previously pushed channel mode changes to be sent to the server, instead of when the script is finished (just for the channel specified)

Returns: nothing

Module: irc

topic <channel>

Returns: string containing the current topic of the specified channel

Module: irc

validchan <channel>

Description: checks if the bot has a channel record for the specified channel. Note that this does not necessarily mean that the bot is ON the channel.

Returns: 1 if the channel exists, 0 if not

Module: channels

isdynamic <channel>

Returns: 1 if the channel is a dynamic channel; 0 otherwise

Module: channels

setudef <flag/int/str> <name>

Description: initializes a user defined channel flag, string or integer setting. You can use it like any other flag/setting. IMPORTANT: Don’t forget to reinitialize your flags/settings after a restart, or it’ll be lost.

Returns: nothing

Module: channels

renudef <flag/int/str> <oldname> <newname>

Description: renames a user defined channel flag, string, or integer setting.

Returns: nothing

Module: channels

deludef <flag/int/str> <name>

Description: deletes a user defined channel flag, string, or integer setting.

Returns: nothing

Module: channels

getudefs [flag/int/str]

Returns: a list of user defined channel settings of the given type, or all of them if no type is given.

Module: channels

chansettype <setting>

Returns: The type of the setting you specify. The possible types are flag, int, str, pair. A flag type references a channel flag setting that can be set to either + or -. An int type is a channel setting that is set to a number, such as ban-time. A str type is a channel setting that stores a string, such as need-op. A pair type is a setting that holds a value couple, such as the flood settings.

Module: channels

isupport get [key]

Description: - isupport get: Returns a flat key/value list (dict) of settings. - isupport get <key>: Returns the setting’s value as a string. Throws an error if the key is not set.

Returns: string or dict, see description above

Module: server

isupport isset <key>

Description: Returns 0/1 depending on whether the key has a value.

Returns: 0 or 1

Module: server

DCC Commands

putdcc <idx> <text> [-raw]

Description: sends text to the idx specified. If -raw is specified, the text will be sent as is, without forced new lines or limits to line length.

Returns: nothing

Module: core

putidx <idx> <text> -[raw]

Description. Alias for the putdcc command.

Returns: nothing

Module: core

dccbroadcast <message>

Description: sends a message to everyone on the party line across the botnet, in the form of “*** <message>” for local users, “*** (Bot) <message>” for users on other bots with version below 1.8.4, and “(Bot) <message>” for users on other bots with version 1.8.4+ and console log mode ‘l’ enabled

Returns: nothing

Module: core

dccputchan <channel> <message>

Description: sends your message to everyone on a certain channel on the botnet, in a form exactly like dccbroadcast does. Valid channels are 0 through 99999.

Returns: nothing

Module: core

boot <user@bot> [reason]

Description: boots a user from the partyline

Returns: nothing

Module: core

dccsimul <idx> <text>

Description: simulates text typed in by the dcc user specified. Note that in v0.9, this only simulated commands; now a command must be preceded by a ‘.’ to be simulated.

Returns: nothing

Module: core

hand2idx <handle>

Returns: the idx (a number greater than or equal to zero) for the user given if the user is on the party line in chat mode (even if she is currently on a channel or in chat off), the file area, or in the control of a script. -1 is returned if no idx is found. If the user is on multiple times, the oldest idx is returned.

Module: core

idx2hand <idx>

Returns: handle of the user with the given idx

Module: core

valididx <idx>

Returns: 1 if the idx currently exists; 0 otherwise

Module: core

getchan <idx>

Returns: the current party line channel for a user on the party line; “0” indicates he’s on the group party line, “-1” means he has chat off, and a value from 1 to 99999 is a private channel

Module: core

setchan <idx> <channel>

Description: sets a party line user’s channel. The party line user is not notified that she is now on a new channel. A channel name can be used (provided it exists).

Returns: nothing

Module: core

console <idx> [channel] [console-modes]

Description: changes a dcc user’s console mode, either to an absolute mode (like “mpj”) or just adding/removing flags (like “+pj” or “-moc” or “+mp-c”). The user’s console channel view can be changed also (as long as the new channel is a valid channel).

Returns: a list containing the user’s (new) channel view and (new) console modes, or nothing if that user isn’t currently on the partyline

Module: core

resetconsole <idx>

Description: changes a dcc user’s console mode to the default setting in the configfile.

Returns: a list containing the user’s channel view and (new) console modes, or nothing if that user isn’t currently on the partyline

Module: core

echo <idx> [status]

Description: turns a user’s echo on or off; the status has to be a 1 or 0

Returns: new value of echo for that user (or the current value, if status was omitted)

Module: core

strip <idx> [+/-strip-flags]

Description: modifies the strip-flags for a user. The supported strip-flags are:

c

remove all color codes

b

remove all boldface codes

r

remove all reverse video codes

u

remove all underline codes

a

remove all ANSI codes

g

remove all ctrl-g (bell) codes

o

remove all ordinary codes (ctrl+o, terminates bold/color/..)

i

remove all italics codes

*

remove all of the above

Returns: new strip-flags for the specified user (or the current flags, if strip-flags was omitted)

Module: core

page <idx> [number]

Description: This allows you to slow down the number of lines the bot sends

to a user at once via the partyline. When enabled, any commands that send

greater than the specified number of lines will stop when that number is

reached and wait for the user to type another command (or press enter) to

continue. If the user has too many pending lines, he may be booted off the

bot.

Returns: new value of page lines for that user (or the current value, if

status was omitted)

Module: core

putbot <bot-nick> <message>

Description: sends a message across the botnet to another bot. If no script intercepts the message on the other end, the message is ignored.

Returns: nothing

Module: core

putallbots <message>

Description: sends a message across the botnet to all bots. If no script intercepts the message on the other end, the message is ignored.

Returns: nothing

Module: core

killdcc <idx>

Description: kills a partyline or file area connection

Returns: nothing

Module: core

bots

Returns: list of the bots currently connected to the botnet

Module: core

botlist

Returns: a list of bots currently on the botnet. Each item in the list is a sublist with four elements: bot, uplink, version, and sharing status:

bot

the bot’s botnetnick

uplink

the bot the bot is connected to

version

it’s current numeric version

sharing

a “+” if the bot is a sharebot; “-” otherwise

Module: core

islinked <bot>

Returns: 1 if the bot is currently linked; 0 otherwise

Module: core

dccused

Returns: number of dcc connections currently in use

Module: core

dcclist [type]

Returns: a list of active connections, each item in the list is a sublist containing seven elements: {<idx> <handle> <hostname> <[+]port> <type> {<other>} <timestamp>}.

The types are: chat, bot, files, file_receiving, file_sending, file_send_pending, script, socket (these are connections that have not yet been put under ‘control’), telnet, and server. The timestamp is in unixtime format.

Module: core

socklist [type]

Returns: a list of active connections, each item in the list is a sublist containing eight elements (in dict-readable format). The order of items returned should not be considered static or permanent, so it is recommended to access the items as key/value pairs with the dict command, as opposed to something like lindex, to extract values. The possible keys returned are:

idx

integer value assigned to Eggdrop connections

handle

possible values are (telnet), (bots), (users), (script) for a listening socket, or the handle of the connected user for an established connection

host

the hostname of the connection, if it is known; otherwise a *

ip

the ip of the connection

port

the port number associated with the connection (local port for listening connections, remote port for server connections.

secure

1 if SSL/TLS is used for the connect; 0 otherwise

type

the type of connection (TELNET, CHAT, SERVER, etc)

info

extra information associated with the connection

time

timestamp of when the socket was established

Module: core

whom <chan>

Returns: list of people on the botnet who are on that channel. 0 is the default party line. Each item in the list is a sublist with six elements: nickname, bot, hostname, access flag (‘-’, ‘@’, ‘+’, or ‘*’), minutes idle, and away message (blank if the user is not away). If you specify * for channel, every user on the botnet is returned with an extra argument indicating the channel the user is on.

Module: core

getdccidle <idx>

Returns: number of seconds the dcc chat/file system/script user has been idle

Module: core

getdccaway <idx>

Returns: away message for a dcc chat user (or “” if the user is not set away)

Module: core

setdccaway <idx> <message>

Description: sets a party line user’s away message and marks them away. If set to “”, the user is marked as no longer away.

Returns: nothing

Module: core

connect <host> <[+]port>

Description: makes an outgoing connection attempt and creates a dcc entry for it. A ‘control’ command should be used immediately after a successful ‘connect’ so no input is lost. If the port is prefixed with a plus sign, SSL encrypted connection will be attempted.

Returns: idx of the new connection

Module: core

listen [ip] <port> <type> [options [flag]]

Description: opens a listening port to accept incoming telnets; type must be one of “bots”, “all”, “users”, “script”, or “off”. Prefixing the port with a plus sign will make eggdrop accept SSL connections on it. An IP may optionally be listed before the mandatory port argument. If no IP is specified, all available interfaces are used.

listen [ip] <port> bots [mask]

Description: accepts connections from bots only; the optional mask is used to identify permitted bot names. If the mask begins with ‘@’, it is interpreted to be a mask of permitted hosts to accept connections from.

Returns: port number or error message

listen [ip] <port> users [mask]

Description: accepts connections from users only (no bots); the optional mask is used to identify permitted nicknames. If the mask begins with ‘@’, it is interpreted to be a mask of permitted hosts to accept connections from.

Returns: port number or error message

listen [ip] <port> all [mask]

Description: accepts connections from anyone; the optional mask is used to identify permitted nicknames/botnames. If the mask begins with ‘@’, it is interpreted to be a mask of permitted hosts to accept connections from.

Returns: port number or error message

listen [ip] <port> script <proc> [flag]

Description: accepts connections which are immediately routed to a proc. The proc is called with one parameter: the idx of the new connection. The optional flag parameter currently only accepts ‘pub’ as a value. By specifying ‘pub’ as a flag, Eggdrop will skip the ident check for the user regardless of settings in the config file. This will allow any user to attempt a connection, and result in Eggdrop using “-telnet!telnet@host” instead of “-telnet!<ident>@host” as a hostmask to match against the user.

Returns: port number or error message

listen [ip] <port> off

Description: stop listening on a port

Returns: nothing

Module: core

dccdumpfile <idx> <filename>

Description: dumps out a file from the text directory to a dcc chat user. The flag matching that’s used everywhere else works here, too.

Returns: nothing

Module: core

Notes Module

notes <user> [numberlist]

Returns: -1 if no such user, -2 if notefile failure. If a numberlist is not specified, the number of notes stored for the user is returned. Otherwise, a list of sublists containing information about notes stored for the user is returned. Each sublist is in the format of:

{<from> <timestamp> <note text>}

Module: notes

erasenotes <user> <numberlist>

Description: erases some or all stored notes for a user. Use ‘-’ to erase all notes.

Returns: -1 if no such user, -2 if notefile failure, 0 if no such note, or number of erased notes.

Module: notes

listnotes <user> <numberlist>

Description: lists existing notes according to the numberlist (ex: “2-4;8;16-“)

Returns: -1 if no such user, -2 if notefile failure, 0 if no such note, list of existing notes.

Module: notes

storenote <from> <to> <msg> <idx>

Description: stores a note for later reading, notifies idx of any results (use idx -1 for no notify).

Returns: 0 on success; non-0 on failure

Module: notes

Assoc Module

assoc <chan> [name]

Description: sets the name associated with a botnet channel, if you specify one

Returns: current name for that channel, if any

Module: assoc

killassoc <chan>

Description: removes the name associated with a botnet channel, if any exists. Use ‘killassoc &’ to kill all assocs.

Returns: nothing

Module: assoc

Compress Module

compressfile [-level <level>] <src-file> [target-file]

and

uncompressfile <src-file> [target-file]

Description: compresses or un-compresses files. The level option specifies the compression mode to use when compressing. Available modes are from 0 (minimum CPU usage, minimum compression) all the way up to 9 (maximum CPU usage, maximum compression). If you don’t specify the target-file, the src-file will be overwritten.

Returns: nothing

Module: compress

iscompressed <filename>

Description: determines whether <filename> is gzip compressed.

Returns: 1 if it is, 0 if it isn’t, and 2 if some kind of error prevented the checks from succeeding.

Module: compress

Filesys Module

setpwd <idx> <dir>

Description: changes the directory of a file system user, in exactly the same way as a ‘cd’ command would. The directory can be specified relative or absolute.

Returns: nothing

Module: filesys

getpwd <idx>

Returns: the current directory of a file system user

Module: filesys

getfiles <dir>

Returns: a list of files in the directory given; the directory is relative to dcc-path

Module: filesys

getdirs <dir>

Returns: a list of subdirectories in the directory given; the directory is relative to dcc-path

Module: filesys

dccsend <filename> <ircnick>

Description: attempts to start a dcc file transfer to the given nick; the filename must be specified either by full pathname or in relation to the bot’s startup directory

Returns:

0

success

1

the dcc table is full (too many connections)

2

can’t open a socket for the transfer

3

the file doesn’t exist

4

the file was queued for later transfer, which means that person has too many file transfers going right now

5

the file could not be opened or temporary file could not be created

Module: transfer

filesend <idx> <filename> [ircnick]

Description: like dccsend, except it operates for a current filesystem user, and the filename is assumed to be a relative path from that user’s current directory

Returns: 0 on failure; 1 on success (either an immediate send or a queued send)

Module: filesys

fileresend <idx> <filename> [ircnick]

Description: functions like filesend, only that it sends a DCC RESEND instead of a DCC SEND, which allows people to resume aborted file transfers if their client supports that protocol. ircII/BitchX/etc. support it; mIRC does not.

Returns: 0 on failure; 1 on success (either an immediate send or a queued send)

Module: filesys

setdesc <dir> <file> <desc>

Description: sets the description for a file in a file system directory; the directory is relative to dcc-path

Returns: nothing

Module: filesys

getdesc <dir> <file>

Returns: the description for a file in the file system, if one exists

Module: filesys

setowner <dir> <file> <handle>

Description: changes the owner for a file in the file system; the directory is relative to dcc-path

Returns: nothing

Module: filesys

getowner <dir> <file>

Returns: the owner of a file in the file system

Module: filesys

getfileq <handle>

Returns: list of files queued by someone; each item in the list will be a sublist with two elements: nickname the file is being sent to and the filename

Module: transfer

getfilesendtime <idx>

Returns: the unixtime value from when a file transfer started, or a negative number:

-1

no matching transfer with the specified idx was found

-2

the idx matches an entry which is not a file transfer

Module: transfer

mkdir <directory> [<required-flags> [channel]]

Description: creates a directory in the file system. Only users with the required flags may access it.

Returns:

0

success

1

can’t create directory

2

directory exists but is not a directory

-3

could not open filedb

Module: filesys

rmdir <directory>

Description: removes a directory from the file system.

Returns: 0 on success; 1 on failure

Module: filesys

mv <file> <destination>

Description: moves a file from its source to the given destination. The file can also be a mask, such as /incoming/*, provided the destination is a directory.

Returns: If the command was successful, the number of files moved will be returned. Otherwise, a negative number will be returned:

-1

invalid source file

-2

invalid destination

-3

destination file exists

-4

no matches found

Module: filesys

cp <file> <destination>

Description: copies a file from its source to the given destination. The file can also be a mask, such as /incoming/*, provided the destination is a directory.

Returns: If the command was successful, the number of files copied will be returned. Otherwise, a negative number will be returned:

-1

invalid source file

-2

invalid destination

-3

destination file exists

-4

no matches found

Module: filesys

getflags <dir>

Returns: the flags required to access a directory

Module: filesys

setflags <dir> [<flags> [channel]]

Description: sets the flags required to access a directory

Returns: 0 on success; -1 or -3 on failure

Module: filesys

PBKDF2 Module

encpass2 <pass>

Returns: a hash in the format of “$pbkdf2-<digest>$rounds=<rounds>$<salt>$<hash>” 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] <pass> <salt> <rounds> <digest>

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

bind <type> <flags> <keyword/mask> [proc-name]

Description: You can use the ‘bind’ command to attach Tcl procedures to certain events. flags are the flags the user must have to trigger the event (if applicable). proc-name is the name of the Tcl procedure to call for this command (see below for the format of the procedure call). If the proc-name is omitted, no binding is added. Instead, the current binding is returned (if it’s stackable, a list of the current bindings is returned).

Returns: name of the command that was added, or (if proc-name was omitted), a list of the current bindings for this command

Module: core

unbind <type> <flags> <keyword/mask> <proc-name>

Description: removes a previously created bind

Returns: name of the command that was removed

Module: core

binds [type/mask]

Description: By default, lists Tcl binds registered with the Eggdrop. You can specify ‘all’ to view all binds, ‘tcl’ to view Tcl binds, and ‘python’ to view Python binds. Alternately, you can specify a bind type (pub, msg, etc) to view all binds that match that type of bind, or a mask that is matched against the command associated with the bind.

Returns: a list of Tcl binds, each item in the list is a sublist of five elements:

{<type> <flags> <name> <hits> <proc>}

Module: core

logfile [<modes> <channel> <filename>]

Description: creates a new logfile, which will log the modes given for the channel listed. If no logfile is specified, a list of existing logfiles will be returned. “*” indicates all channels. You can also change the modes and channel of an existing logfile with this command. Entering a blank mode and channel (“”) makes the bot stop logging there.

Logfile flags:

b

information about bot linking and userfile sharing

c

commands

d

misc debug information

g

raw outgoing share traffic

h

raw incoming share traffic

j

joins, parts, quits, topic changes, and netsplits on the channel

k

kicks, bans, and mode changes on the channel

l

linked bot messages

m

private msgs, notices and ctcps to the bot

o

misc info, errors, etc (IMPORTANT STUFF)

p

public text on the channel

r

raw incoming server traffic

s

server connects, disconnects, and notices

t

raw incoming botnet traffic

u

raw outgoing botnet traffic

v

raw outgoing server traffic

w

wallops (make sure the bot sets +w in init-server)

x

file transfers and file-area commands

Returns: filename of logfile created, or, if no logfile is specified, a list of logfiles such as: {mco * eggdrop.log} {jp #lame lame.log}

Module: core

maskhost <nick!user@host> [masktype]

Returns: masked hostmask for the string given according to the masktype (the default is 3).

Available types are:

0

*!user@host

1

*!*user@host

2

*!*@host

3

*!*user@*.host

4

*!*@*.host

5

nick!user@host

6

nick!*user@host

7

nick!*@host

8

nick!*user@*.host

9

nick!*@*.host

You can also specify types from 10 to 19 which correspond to types 0 to 9, but instead of using a * wildcard to replace portions of the host, only numbers in hostnames are replaced with the ‘?’ wildcard. Same is valid for types 20-29, but instead of ‘?’, the ‘*’ wildcard will be used. Types 30-39 set the host to ‘*’.

Module: core

timer <minutes> <tcl-command> [count [timerName]]

Description: executes the given Tcl command after a certain number of minutes have passed, at the top of the minute (ie, if a timer is started at 10:03:34 with 1 minute specified, it will execute at 10:04:00. If a timer is started at 10:06:34 with 2 minutes specified, it will execute at 10:08:00). If count is specified, the command will be executed count times with the given interval in between. If you specify a count of 0, the timer will repeat until it’s removed with killtimer or until the bot is restarted. If timerName is specified, it will become the unique identifier for the timer. If no timerName is specified, Eggdrop will assign a timerName in the format of “timer<integer>”.

Returns: a timerName

Module: core

utimer <seconds> <tcl-command> [count [timerName]]

Description: executes the given Tcl command after a certain number of seconds have passed. If count is specified, the command will be executed count times with the given interval in between. If you specify a count of 0, the utimer will repeat until it’s removed with killutimer or until the bot is restarted. If timerName is specified, it will become the unique identifier for the timer. If timerName is not specified, Eggdrop will assign a timerName in the format of “timer<integer>”.

Returns: a timerName

Module: core

timers

Description: lists all active minutely timers.

Returns: a list of active minutely timers, with each timer sub-list containing the number of minutes left until activation, the command that will be executed, the timerName, and the remaining number of repeats.

Module: core

utimers

Description: lists all active secondly timers.

Returns: a list of active secondly timers, with each timer sub-list containing the number of seconds left until activation, the command that will be executed, the timerName, and the remaining number of repeats.

Module: core

killtimer <timerName>

Description: removes the timerName minutely timer from the timer list.

Returns: nothing

Module: core

killutimer <timerName>

Description: removes the timerName secondly timer from the timer list.

Returns: nothing

Module: core

unixtime

Returns: a long integer which represents the number of seconds that have passed since 00:00 Jan 1, 1970 (GMT).

Module: core

duration <seconds>

Returns: the number of seconds converted into years, weeks, days, hours, minutes, and seconds. 804600 seconds is turned into 1 week 2 days 7 hours 30 minutes.

Module: core

strftime <formatstring> [time]

Returns: a formatted string of time using standard strftime format. If time is specified, the value of the specified time is used. Otherwise, the current time is used. Note: The implementation of strftime varies from platform to platform, so the user should only use POSIX-compliant format specifiers to ensure fully portable code.

Module: core

ctime <unixtime>

Returns: a formatted date/time string based on the current locale settings from the unixtime string given; for example “Fri Aug 3 11:34:55 1973”

Module: core

myip

Returns: a long number representing the bot’s IP address, as it might appear in (for example) a DCC request

Module: core

rand <limit>

Returns: a random integer between 0 and limit-1. Limit must be greater than 0 and equal to or less than RAND_MAX, which is generally 2147483647. The underlying pseudo-random number generator is not cryptographically secure.

Module: core

control <idx> <command>

Description: removes an idx from the party line and sends all future input to the Tcl command given. The command will be called with two parameters: the idx and the input text. The command should return 0 to indicate success and 1 to indicate that it relinquishes control of the user back to the bot. If the input text is blank (“”), it indicates that the connection has been dropped. Also, if the input text is blank, never call killdcc on it, as it will fail with “invalid idx”.

Returns: nothing

Module: core

sendnote <from> <to[@bot]> <message>

Description: simulates what happens when one user sends a note to another

Returns:

0

the send failed

1

the note was delivered locally or sent to another bot

2

the note was stored locally

3

the user’s notebox is too full to store a note

4

a Tcl binding caught the note

5

the note was stored because the user is away

Module: core

encrypt <key> <string>

Returns: encrypted string (using the currently loaded encryption module), encoded into ASCII using base-64. As of v1.8.4, the default blowfish encryption module can use either the older ECB mode (currently used by default for compatibility reasons), or the more recent and more-secure CBC mode. You can explicitly request which encryption mode to use by prefixing the encryption key with either “ecb:” or “cbc:”, or by using the blowfish-use-mode setting in the config file. Note: the default encryption mode for this function is planned to transition from ECB to CBC in v1.9.0.

Module: encryption

decrypt <key> <encrypted-base64-string>

Returns: decrypted string (using the currently loaded encryption module). If the default blowfish encryption module is used, this automatically picks the right decryption mode. You may still prefix the key with “ecb:” or “cbc:” or use the blowfish-use-mode setting in the config file (see the encrypt command for more detailed information).

Module: encryption

encpass <password>

Returns: encrypted string (using the currently loaded encryption module)

Module: encryption

die [reason]

Description: causes the bot to log a fatal error and exit completely. If no reason is given, “EXIT” is used.

Returns: none

Module: core

unames

Returns: the current operating system the bot is using

Module: core

dnslookup <ip-address/hostname> <proc> [[arg1] [arg2] … [argN]]

Description: This issues an asynchronous dns lookup request. The command will block if dns module is not loaded; otherwise it will either return immediately or immediately call the specified proc (e.g. if the lookup is already cached).

As soon as the request completes, the specified proc will be called as follows:

<proc> <ipaddress> <hostname> <status> [[arg1] [arg2] … [argN]]

status is 1 if the lookup was successful and 0 if it wasn’t. All additional parameters (called arg1, arg2 and argN above) get appended to the proc’s other parameters.

Returns: nothing

Module: core

md5 <string>

Returns: the 128 bit MD5 message-digest of the specified string

Module: core

callevent <event>

Description: triggers the evnt bind manually for a certain event. You can call arbitrary events here, even ones that are not pre-defined by Eggdrop. For example: callevent rehash, or callevent myownevent123.

Returns: nothing

Module: core

traffic

Returns: a list of sublists containing information about the bot’s traffic usage in bytes. Each sublist contains five elements: type, in-traffic today, in-traffic total, out-traffic today, out-traffic total (in that order).

Module: core

modules

Returns: a list of sublists containing information about the bot’s currently loaded modules. Each sublist contains three elements: module, version, and dependencies. Each dependency is also a sublist containing the module name and version.

Module: core

loadmodule <module>

Description: attempts to load the specified module.

Returns: “Already loaded.” if the module is already loaded, “” if successful, or the reason the module couldn’t be loaded.

Module: core

unloadmodule <module>

Description: attempts to unload the specified module.

Returns: “No such module” if the module is not loaded, “” otherwise.

Module: core

loadhelp <helpfile-name>

Description: attempts to load the specified help file from the help/ directory.

Returns: nothing

Module: core

unloadhelp <helpfile-name>

Description: attempts to unload the specified help file.

Returns: nothing

Module: core

reloadhelp

Description: reloads the bot’s help files.

Returns: nothing

Module: core

restart

Description: rehashes the bot, kills all timers, reloads all modules, and reconnects the bot to the next server in its list.

Returns: nothing

Module: core

rehash

Description: rehashes the bot

Returns: nothing

Module: core

stripcodes <strip-flags> <string>

Description: strips specified control characters from the string given. strip-flags can be any combination of the following:

c

remove all color codes

b

remove all boldface codes

r

remove all reverse video codes

u

remove all underline codes

a

remove all ANSI codes

g

remove all ctrl-g (bell) codes

o

remove all ordinary codes (ctrl+o, terminates bold/color/..)

i

remove all italics codes

*

remove all of the above

Returns: the stripped string.

Module: core

matchaddr <hostmask> <address>

Description: checks if the address matches the hostmask given. The address should be in the form nick!user@host.

Returns: 1 if the address matches the hostmask, 0 otherwise.

Module: core

matchcidr <block> <address> <prefix>

Description: performs a cidr match on the specified ip addresses. IPv6 is supported, if enabled at compile time.

Example: matchcidr 192.168.0.0 192.168.1.17 16

Returns: 1 if the address matches the block prefix, 0 otherwise.

Module: core

matchstr <pattern> <string>

Description: checks if pattern matches string. Only two wildcards are supported: ‘*’ and ‘?’. Matching is case-insensitive. This command is intended as a simplified alternative to Tcl’s string match.

Returns: 1 if the pattern matches the string, 0 if it doesn’t.

Module: core

rfcequal <string1> <string2>

Description: Checks if two strings are equal. Case is ignored, and this uses RFC1459 matching {}|~ == []^, depending on the rfc_compliant setting.

Returns: 1 if equal, 0 if not.

Module: core

status [type]

Description: provides eggdrop status information similar to the .status command in partyline. The available types of information are:

cpu

total cpu time spent by eggdrop

mem

dynamically allocated memory excluding the Tcl interpreter

cache

user entries cache hits (in %)

ipv6

shows whether IPv6 support was compiled in

Returns: the requested information type or all information if type isn’t specified. The format is a flat list of name-value pairs.

Module: core

istls <idx>

Description: checks if a connection is encrypted or cleartext. This command is available on TLS-enabled bots only.

Returns: 1 if the idx is a TLS connection, 0 if it’s plaintext.

Module: core

starttls <idx>

Description: establishes a secure (using TLS) connection over idx. The TLS connection should be first negotiated over the plaintext link, or using other means. Both parties must switch to TLS simultaneously. This command is available on TLS-enabled bots only.

Returns: nothing

Module: core

tlsstatus <idx>

Description: provides information about an established TLS connection This includes certificate and cipher information as well as protocol version. This command is available on TLS-enabled bots only.

Returns: a flat list of name-value pairs

Module: core

Global Variables

NOTE: All config file variables are also global.

botnick

Value: the current nickname the bot is using (for example: “Valis”, “Valis0”, etc.)

Module: server

botname

Value: the current nick!user@host that the server sees (for example: “Valis!valis@crappy.com”)

Module: server

server

Value: the current server’s real name (what server calls itself) and port bot is connected to (for example: “irc.math.ufl.edu:6667”) Note that this does not necessarily match the servers internet address.

Module: server

serveraddress

Value: the current server’s internet address (hostname or IP) and port bot is connected to. This will correspond to the entry in server list (for example: “eu.undernet.org:6667”). Note that this does not necessarily match the name server calls itself.

Module: server

version

Value: current bot version “1.1.2+pl1.10.2201”; first item is the text version, to include a patch string if present, and second item is a numerical version

Module: core

numversion*

Value: the current numeric bot version (for example: “1.10.21”). Numerical version is in the format of “MNNRRPP”, where:

M

major release number

NN

minor release number

RR

sub-release number

PP

patch level for that sub-release

Module: core

uptime

Value: the unixtime value for when the bot was started

Module: core

server-online

Value: the unixtime value when the bot connected to its current server, or ‘0’ if the bot is currently disconnected from a server.

Module: server

lastbind

Value: the last command binding which was triggered. This allows you to identify which command triggered a Tcl proc.

Module: core

isjuped

Value: 1 if bot’s nick is juped(437); 0 otherwise

Module: server

handlen

Value: the value of the HANDLEN define in src/eggdrop.h

Module: core

config

Value: the filename of the config file Eggdrop is currently using

Module: core

configureargs

Value: a string (not list) of configure arguments in shell expansion (single quotes)

Module: core

language

Value: a string containing the language with the highest priority for use by Eggdrop. This commonly reflects what is added with addlang in the config file

Module: core

account-extban

Value: a string containing the value of ACCOUNTEXTBAN provided by the 005 connection message. This is the raw value and, depending on the IRC server formatting, could be in multiple formats such as “a”, or “a,account”.

Module: channel

extban-flags

Value: a string containing the allowed extban flag characters advertised by EXTBAN in the 005 connection message. If EXTBAN advertises a prefix, such as “$,aUq”, the prefix is omitted and this variable contains only the flags, such as “aUq”.

Module: channel

Binds

You can use the ‘bind’ command to attach Tcl procedures to certain events. For example, you can write a Tcl procedure that gets called every time a user says “danger” on the channel. When a bind is triggered, ALL of the Tcl procs that are bound to it will be called. Raw binds are triggered before builtin binds, as a builtin bind has the potential to modify args.

Stackable binds

Some bind types are marked as “stackable”. That means that you can bind multiple commands to the same trigger. Normally, for example, a bind such as ‘bind msg - stop msg:stop’ (which makes a msg-command “stop” call the Tcl proc “msg:stop”) will overwrite any previous binding you had for the msg command “stop”. With stackable bindings, like ‘msgm’ for example, you can bind the same command to multiple procs.

Removing a bind

To remove a bind, use the ‘unbind’ command. For example, to remove the bind for the “stop” msg command, use ‘unbind msg - stop msg:stop’.

Flag Masks

In the Bind Types section (and other commands, such as matchattr), you will see several references to the “flags” argument. The “flags” argument takes a flag mask, which is a value that represents the type of user that is allowed to trigger the procedure associated to that bind. The flags can be any of the standard Eggdrop flags (o, m, v, etc). Additionally, when used by itself, a “-” or “*” can be used to skip processing for a flag type. A flag mask has three sections to it- global, channel, and bot flag sections. Each section is separated by the | or & logical operators ( the | means “OR” and the & means “AND; if nothing proceeds the flag then Eggdrop assumes it to be an OR). Additionally, a ‘+’ and ‘-’ can be used in front of a flag to check if the user does (+) have it, or does not (-) have it.

The easiest way to explain how to build a flag mask is by demonstration. A flag mask of “v” by itself means “has a global v flag”. To also check for a channel flag, you would use the flag mask “v|v”. This checks if the user has a global “v” flag, OR a channel “v” flag (again, the | means “OR” and ties the two types of flags together). You could change this mask to be “v&v”, which would check if the user has a global “v” flag AND a channel “v” flag. Lastly, to check if a user ONLY has a channel flag, you would use “*|v” as a mask, which would not check global flags but does check if the user had a channel “v” flag.

You will commonly see flag masks for global flags written “ov”; this is the same as “|ov” or “*|ov”.

Some additional examples:

Flag Mask

Action

m, +m, m|*

Checks if the user has the m global flag

+mn

Checks if the user has the m OR n global flag

|+mn

Checks if the user has the m OR n channel flag

|+mn #foo

Checks if the user has the m OR n channel flag for #foo

&+mn

Checks if the user has the m AND n channel flag

&mn #foo

Checks if the user has the m AND n channel flag for #foo

|+o #foo

Checks if the user has the o channel flag for #foo

+o|+n #foo

Checks if the user has the o global flag OR the n channel flag for #foo

+m&+v #foo

Checks if the user has the m global flag AND the v channel flag for #foo

-m

Checks if the user does not have the m global flag

|-n #foo

Checks if the user does not have the n channel flag for #foo

+m|-n #foo

Checks if the user has the global m flag OR does not have a channel n flag for #foo

-n&-m #foo

Checks if the user does not have the global n flag AND does not have the channel m flag for #foo

||+b

Checks if the user has the bot flag b

As a side note, Tcl scripts historically have used a ‘-’ to skip processing of a flag type (Example: -|o). It is unknown where and why this practice started, but as a style tip, Eggdrop developers recommend using a ‘*’ to skip processing, so as not to confuse a single “-” meaning “skip processing” with a preceding “-ov” which means “not these flags”.

Bind Types

The following is a list of bind types and how they work. Below each bind type is the format of the bind command, the list of arguments sent to the Tcl proc, and an explanation.

  1. MSG

bind msg <flags> <command> <proc>

procname <nick> <user@host> <handle> <text>

Description: used for /msg commands. The first word of the user’s msg is the command, and everything else becomes the text argument.

Module: server

  1. DCC

bind dcc <flags> <command> <proc>

procname <handle> <idx> <text>

Description: used for partyline commands; the command is the first word and everything else becomes the text argument. The idx is valid until the user disconnects. After that, it may be reused, so be careful about storing an idx for long periods of time.

Module: core

  1. FIL

bind fil <flags> <command> <proc>

procname <handle> <idx> <text>

Description: the same as DCC, except this is triggered if the user is in the file area instead of the party line

Module: filesys

  1. PUB

bind pub <flags> <command> <proc>

procname <nick> <user@host> <handle> <channel> <text>

Description: used for commands given on a channel. The first word becomes the command and everything else is the text argument.

Module: irc

  1. MSGM (stackable)

bind msgm <flags> <mask> <proc>

procname <nick> <user@host> <handle> <text>

Description: matches the entire line of text from a /msg with the mask. This is useful for binding Tcl procs to words or phrases spoken anywhere within a line of text. If the proc returns 1, Eggdrop will not log the message that triggered this bind. MSGM binds are processed before MSG binds. If the exclusive-binds setting is enabled, MSG binds will not be triggered by text that a MSGM bind has already handled.

Module: server

  1. PUBM (stackable)

bind pubm <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <text>

Description: just like MSGM, except it’s triggered by things said on a channel instead of things /msg’d to the bot. The mask is matched against the channel name followed by the text and can contain wildcards. If the proc returns 1, Eggdrop will not log the message that triggered this bind. PUBM binds are processed before PUB binds. If the exclusive-binds setting is enabled, PUB binds will not be triggered by text that a PUBM bind has already handled.

Examples:
bind pubm * “#eggdrop Hello*” myProc

Listens on #eggdrop for any line that begins with “Hello”

bind pubm * “% Hello*” myProc

Listens on any channel for any line that begins with “Hello”

bind pubm * “% !command” myProc

Listens on any channel for a line that ONLY contains “!command”

Module: irc

  1. NOTC (stackable)

bind notc <flags> <mask> <proc>

procname <nick> <user@host> <handle> <text> <dest>

Description: dest will be a nickname (the bot’s nickname, obviously) or a channel name. mask is matched against the entire text of the notice and can contain wildcards. It is considered a breach of protocol to respond to a /notice on IRC, so this is intended for internal use (logging, etc.) only. Note that server notices do not trigger the NOTC bind. If the proc returns 1, Eggdrop will not log the message that triggered this bind.

New Tcl procs should be declared as:

proc notcproc {nick uhost hand text {dest ""}} {
  global botnick; if {$dest == ""} {set dest $botnick}
  ...
}

for compatibility.

Module: server

  1. JOIN (stackable)

bind join <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel>

Description: triggered by someone joining the channel. The mask in the bind is matched against “#channel nick!user@host” and can contain wildcards.

Module: irc

  1. PART (stackable)

bind part <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <msg>

Description: triggered by someone leaving the channel. The mask is matched against “#channel nick!user@host” and can contain wildcards. If no part message is specified, msg will be set to “”.

New Tcl procs should be declared as:

proc partproc {nick uhost hand chan {msg ""}} { ... }

for compatibility.

Module: irc

  1. SIGN (stackable)

bind sign <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <reason>

Description: triggered by a signoff, or possibly by someone who got netsplit and never returned. The signoff message is the last argument to the proc. Wildcards can be used in the mask, which is matched against ‘#channel nick!user@host’. If a “*” is used for the channel in the mask, this bind is triggered once for every channel that the user is in the bot with; in other words if the bot is in two channels with the target user, the bind will be triggered twice. To trigger a proc only once per signoff, regardless of the number of channels the Eggdrop and user share, use the RAWT bind with SIGN as the keyword.

Module: irc

  1. TOPC (stackable)

bind topc <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <topic>

Description: triggered by a topic change. mask can contain wildcards and is matched against ‘#channel <new topic>’.

Module: irc

  1. KICK (stackable)

bind kick <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <target> <reason>

Description: triggered when someone is kicked off the channel. The mask is matched against ‘#channel target reason’ where the target is the nickname of the person who got kicked (can contain wildcards). The proc is called with the nick, user@host, and handle of the kicker, plus the channel, the nickname of the person who was kicked, and the reason.

Module: irc

  1. NICK (stackable)

bind nick <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <newnick>

Description: triggered when someone changes nicknames. The mask is matched against ‘#channel newnick’ and can contain wildcards. Channel is “*” if the user isn’t on a channel (usually the bot not yet in a channel). If a “*” is used for the channel in the mask, this bind is triggered once for every channel that the user is in the bot with; in other words if the bot is in two channels with the target user, the bind will be triggered twice. To trigger a proc only once per nick change, regardless of the number of channels the Eggdrop and user share, use the RAWT bind with NICK as the keyword.

Module: irc

  1. MODE (stackable)

bind mode <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel> <mode-change> <target>

Description: mode changes are broken down into their component parts before being sent here, so the <mode-change> will always be a single mode, such as “+m” or “-o”. target will show the argument of the mode change (for o/v/b/e/I) or “” if the set mode does not take an argument. The bot’s automatic response to a mode change will happen AFTER all matching Tcl procs are called. The mask will be matched against ‘#channel +/-modes’ and can contain wildcards.

If it is a server mode, nick will be “”, user@host is the server name, and handle is *.

Note that “target” was added in 1.3.17 and that this will break Tcl scripts that were written for pre-1.3.17 Eggdrop that use the mode binding. Also, due to a typo, mode binds were broken completely in 1.3.17 but were fixed in 1.3.18. Mode bindings are not triggered at all in 1.3.17.

One easy example (from guppy) of how to support the “target” parameter in 1.3.18 and later and still remain compatible with older Eggdrop versions is:

Old script looks as follows:

bind mode - * mode_proc
proc mode_proc {nick uhost hand chan mode} { ... }

To make it work with 1.3.18+ and stay compatible with older bots, do:

bind mode - * mode_proc_fix
proc mode_proc_fix {nick uhost hand chan mode {target ""}} {
  if {$target != ""} {append mode " $target"}
  mode_proc $nick $uhost $hand $chan $mode
}
proc mode_proc {nick uhost hand chan mode} { ... }

Module: irc

  1. CTCP (stackable)

bind ctcp <flags> <keyword> <proc>

procname <nick> <user@host> <handle> <dest> <keyword> <text>

Description: dest will be a nickname (the bot’s nickname, obviously) or channel name. keyword is the ctcp command (which can contain wildcards), and text may be empty. If the proc returns 0, the bot will attempt its own processing of the ctcp command.

Module: server

  1. CTCR (stackable)

bind ctcr <flags> <keyword> <proc>

procname <nick> <user@host> <handle> <dest> <keyword> <text>

Description: just like ctcp, but this is triggered for a ctcp-reply (ctcp embedded in a notice instead of a privmsg)

Module: server

  1. RAW (stackable)

bind raw <flags> <mask> <proc>

procname <from> <keyword> <text>

IMPORTANT: While not necessarily deprecated, this bind has been supplanted by the RAWT bind, which supports the IRCv3 message-tags capability, as of 1.9.0. You probably want to be using RAWT, not RAW.

Description: The mask can contain wildcards and is matched against the keyword, which is either a numeric, like “368”, or a keyword, such as “PRIVMSG”. “from” will be the server name or the source nick!ident@host (depending on the keyword); flags are ignored. If the proc returns 1, Eggdrop will not process the line any further (this could cause unexpected behavior in some cases), although RAWT binds are processed before RAW binds (and thus, a RAW bind cannot block a RAWT bind).

Module: server

  1. BOT

bind bot <flags> <command> <proc>

procname <from-bot> <command> <text>

Description: triggered by a message coming from another bot in the botnet. The first word is the command and the rest becomes the text argument; flags are ignored.

Module: core

  1. CHON (stackable)

bind chon <flags> <mask> <proc>

procname <handle> <idx>

Description: when someone first enters the party-line area of the bot via dcc chat or telnet, this is triggered before they are connected to a chat channel (so, yes, you can change the channel in a ‘chon’ proc). mask is matched against the handle and supports wildcards. This is NOT triggered when someone returns from the file area, etc.

Module: core

  1. CHOF (stackable)

bind chof <flags> <mask> <proc>

procname <handle> <idx>

Description: triggered when someone leaves the party line to disconnect from the bot. mask is matched against the handle and can contain wildcards. Note that the connection may have already been dropped by the user, so don’t send output to the idx.

Module: core

  1. SENT (stackable)

bind sent <flags> <mask> <proc>

procname <handle> <nick> <path/to/file>

Description: after a user has successfully downloaded a file from the bot, this binding is triggered. mask is matched against the handle of the user that initiated the transfer and supports wildcards. nick is the actual recipient (on IRC) of the file. The path is relative to the dcc directory (unless the file transfer was started by a script call to ‘dccsend’, in which case the path is the exact path given in the call to ‘dccsend’).

Module: transfer

  1. RCVD (stackable)

bind rcvd <flags> <mask> <proc>

procname <handle> <nick> <path/to/file>

Description: triggered after a user uploads a file successfully. mask is matched against the user’s handle. nick is the IRC nickname that the file transfer originated from. The path is where the file ended up, relative to the dcc directory (usually this is your incoming dir).

Module: transfer

  1. CHAT (stackable)

bind chat <flags> <mask> <proc>

procname <handle> <channel#> <text>

Description: when a user says something on the botnet, it invokes this binding. Flags are ignored; handle could be a user on this bot (“DronePup”) or on another bot (“Eden@Wilde”) and therefore you can’t rely on a local user record. The mask is checked against the entire line of text and supports wildcards. Eggdrop passes the partyline channel number the user spoke on to the proc in “channel#”.

NOTE: If a BOT says something on the botnet, the BCST bind is invoked instead.

Module: core

  1. LINK (stackable)

bind link <flags> <mask> <proc>

procname <botname> <via>

Description: triggered when a bot links into the botnet. botname is the botnetnick of the bot that just linked in; via is the bot it linked through. The mask is checked against the botnetnick of the bot that linked and supports wildcards. flags are ignored.

Module: core

  1. DISC (stackable)

bind disc <flags> <mask> <proc>

procname <botname>

Description: triggered when a bot disconnects from the botnet for whatever reason. Just like the link bind, flags are ignored; mask is matched against the botnetnick of the bot that unlinked. Wildcards are supported in mask.

Module: core

  1. SPLT (stackable)

bind splt <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel>

Description: triggered when someone gets netsplit on the channel. Be aware that this may be a false alarm (it’s easy to fake a netsplit signoff message on some networks); mask may contain wildcards and is matched against ‘#channel nick!user@host’. Anyone who is SPLT will trigger a REJN or SIGN within the next wait-split (defined in the config file) seconds.

Module: irc

  1. REJN (stackable)

bind rejn <flags> <mask> <proc>

procname <nick> <user@host> <handle> <channel>

Description: someone who was split has rejoined. mask can contain wildcards, and is matched against ‘#channel nick!user@host’.

Module: irc

  1. FILT (stackable)

bind filt <flags> <mask> <proc>

procname <idx> <text>

Description: party line and file system users have their text sent through filt before being processed. ‘mask’ is a text mask that can contain wildcards and is used for matching text sent on the partyline. If the proc returns a blank string, the partyline texr is continued to be parsed as-is. Otherwise, the bot will instead use the text returned from the proc for continued parsing.

Module: core

  1. NEED (stackable)

bind need <flags> <mask> <proc>

procname <channel> <type>

Description: this bind is triggered on certain events, like when the bot needs operator status or the key for a channel. The types are: op, unban, invite, limit, and key; the mask is matched against ‘#channel type’ and can contain wildcards. flags are ignored.

Example:

bind need - "% op" needop < handles only need op
bind need - "*" needall   < handles all needs

Module: irc

  1. FLUD (stackable)

bind flud <flags> <type> <proc>

procname <nick> <user@host> <handle> <type> <channel>

Description: any floods detected through the flood control settings (like ‘flood-ctcp’) are sent here before processing. If the proc returns 1, no further action is taken on the flood; if the proc returns 0, the bot will do its normal “punishment” for the flood. The flood types are: pub, msg, join, or ctcp (and can be masked to “*” for the bind); flags are ignored.

Module: server

  1. NOTE (stackable)

bind note <flags> <mask> <proc>

procname <from> <to> <text>

Description: incoming notes (either from the party line, someone on IRC, or someone on another bot on the botnet) are checked against these binds before being processed. The mask is matched against the receiving handle and supports wildcards. If the proc returns 1, Eggdrop will not process the note any further. Flags are ignored.

Module: core

  1. ACT (stackable)

bind act <flags> <mask> <proc>

procname <handle> <channel#> <action>

Description: when someone does an action on the botnet, it invokes this binding. flags are ignored; the mask is matched against the text of the action and can support wildcards.

Module: core

  1. WALL (stackable)

bind wall <flags> <mask> <proc>

procname <from> <msg>

Description: when the bot receives a wallops, it invokes this binding. flags are ignored; the mask is matched against the text of the wallops msg. Note that RFC shows the server name as a source of the message, whereas many IRCds send the nick!user@host of the actual sender, thus, Eggdrop will not parse it at all, but simply pass it to bind in its original form. If the proc returns 1, Eggdrop will not log the message that triggered this bind.

Module: server

  1. BCST (stackable)

bind bcst <flags> <mask> <proc>

procname <botname> <channel#> <text>

Description: when a bot broadcasts something on the botnet (see ‘dccbroadcast’ above), it invokes this binding. flags are ignored; the mask is matched against the message text and can contain wildcards. ‘channel’ argument will always be ‘-1’ since broadcasts are not directed to any partyline channel.

It is also invoked when a BOT (not a person, as with the CHAT bind) ‘says’ something on a channel. In this case, the ‘channel’ argument will be a valid channel, and not ‘-1’.

Module: core

  1. CHJN (stackable)

bind chjn <flags> <mask> <proc>

procname <botname> <handle> <channel#> <flag> <idx> <user@host>

Description: when someone joins a botnet channel, it invokes this binding. The mask is matched against the channel and can contain wildcards. flag is one of: * (owner), + (master), @ (op), or % (botnet master). Flags are ignored.

Module: core

  1. CHPT (stackable)

bind chpt <flags> <mask> <proc>

procname <botname> <handle> <idx> <channel#>

Description: when someone parts a botnet channel, it invokes this binding. The mask is matched against the channel and can contain wildcards. Flags are ignored.

Module: core

  1. TIME (stackable)

bind time <flags> <mask> <proc>

procname <minute 00-59> <hour 00-23> <day 01-31> <month 00-11> <year 0000-9999>

Description: allows you to schedule procedure calls at certain times. mask matches 5 space separated integers of the form: “minute hour day month year”. The month var starts at 00 (Jan) and ends at 11 (Dec). Minute, hour, day, month have a zero padding so they are exactly two characters long; year is four characters. Flags are ignored.

Module: core

  1. AWAY (stackable)

bind away <flags> <mask> <proc>

procname <botname> <idx> <text>

Description: triggers when a user goes away or comes back on the botnet. text is the reason that has been specified (text is “” when returning). mask is matched against the botnet-nick of the bot the user is connected to and supports wildcards. flags are ignored.

Module: core

  1. LOAD (stackable)

bind load <flags> <mask> <proc>

procname <module>

Description: triggers when a module is loaded. mask is matched against the name of the loaded module and supports wildcards; flags are ignored.

Module: core

  1. UNLD (stackable)

bind unld <flags> <mask> <proc>

procname <module>

Description: triggers when a module is unloaded. mask is matched against the name of the unloaded module and supports wildcards; flags are ignored.

Module: core

  1. NKCH (stackable)

bind nkch <flags> <mask> <proc>

procname <oldhandle> <newhandle>

Description: triggered whenever a local user’s handle is changed (in the userfile). mask is matched against the user’s old handle and can contain wildcards; flags are ignored.

Module: core

  1. EVNT (stackable)

bind evnt <flags> <type> <proc>

procname <type> [arg]

Description: triggered whenever one of these events happen. flags are ignored. Pre-defined events triggered by Eggdrop are:

sighup            - called on a kill -HUP <pid>
sigterm           - called on a kill -TERM <pid>
sigill            - called on a kill -ILL <pid>
sigquit           - called on a kill -QUIT <pid>
save              - called when the userfile is saved
rehash            - called just after a rehash
prerehash         - called just before a rehash
prerestart        - called just before a restart
logfile           - called when the logs are switched daily
loaded            - called when the bot is done loading
userfile-loaded   - called after userfile has been loaded
connect-server    - called just before we connect to an IRC server
preinit-server    - called immediately when we connect to the server
init-server       - called when we actually get on our IRC server
disconnect-server - called when we disconnect from our IRC server
fail-server       - called when an IRC server fails to respond
hidden-host       - called after the bot's host is hidden by the server
got-chanlist      - called after Eggdrop receives the channel userlist from the server. Passes a second [arg] value to the Tcl proc

Note that Tcl scripts can trigger arbitrary events, including ones that are not pre-defined or used by Eggdrop.

Module: core

  1. LOST (stackable)

bind lost <flags> <mask> <proc>

procname <handle> <nick> <path> <bytes-transferred> <length-of-file>

Description: triggered when a DCC SEND transfer gets lost, such as when the connection is terminated before all data was successfully sent/received. This is typically caused by a user abort.

Module: transfer

  1. TOUT (stackable)

bind tout <flags> <mask> <proc>

procname <handle> <nick> <path> <bytes-transferred> <length-of-file>

Description: triggered when a DCC SEND transfer times out. This may either happen because the dcc connection was not accepted or because the data transfer stalled for some reason.

Module: transfer

  1. OUT (stackable)

bind out <flags> <mask> <proc>

procname <queue> <message> <queued|sent>

Description: triggered whenever output is sent to the server. Normally the event will occur twice for each line sent: once before entering a server queue and once after the message is actually sent. This allows for more flexible logging of server output and introduces the ability to cancel the message. Mask is matched against “queue status”, where status is either ‘queued’ or ‘sent’. Queues are: mode, server, help, noqueue. noqueue is only used by the putnow tcl command.

Module: server

  1. CRON (stackable)

bind cron <flags> <mask> <proc>

procname <minute 0-59> <hour 0-23> <day 1-31> <month 1-12> <weekday 0-6>

Description: similar to bind TIME, but the mask is evaluated as a cron expression, e.g. “16/2 */2 5-15 7,8,9 4”. It can contain up to five fields: minute, hour, day, month, weekday; delimited by whitespace. Week days are represented as 0-6, where Sunday can be either 0 or 7. Symbolic names are not supported. The bind will be triggered if the mask matches all of the fields, except that if both day and weekday are not ‘*’, only one of them is required to match. If any number of fields are omitted at the end, the match will proceed as if they were ‘*’. All cron operators are supported. Please refer to the crontab manual for their meanings. Flags are ignored.

Module: core

  1. LOG (stackable)

bind log <flags> <mask> <proc>

procname <level> <channel> <message>

Description: triggered whenever a message is sent to a log. The mask is matched against “channel text”. The level argument to the proc will contain the level(s) the message is sent to, or ‘*’ if the message is sent to all log levels at once. If the message wasn’t sent to a specific channel, channel will be set to ‘*’.

Module: core

  1. TLS (stackable)

bind tls <flags> <mask> <proc>

procname <idx>

Description: triggered for tcp connections when a ssl handshake has completed and the connection is secured. The mask is matched against the idx of the connection.

Module: core

  1. DIE (stackable)

bind die <flags> <mask> <proc>

procname <shutdownreason>

Description: triggered when eggdrop is about to die. The mask is matched against the shutdown reason. The bind won’t be triggered if the bot crashes or is being terminated by SIGKILL.

Module: core

  1. IRCAWAY (stackable)

bind ircaway <flags> <mask> <proc>

procname <nick> <user> <hand> <channel> <msg>

Description: triggered when Eggdrop receives an AWAY message for a user from an IRC server, ONLY if the away-notify capability is enabled via CAP (the server must supports this capability, see the ‘cap’ Tcl command for more info on requesting capabilities). “Normal” away messages (301 messages) will not trigger this bind, for those you should instead use a RAWT bind. The mask for the bind is in the format “#channel nick!user@hostname” (* to catch all nicknames). nick is the nickname of the user that triggered the bind, user is the nick!user@host of the user, handle is the handle of the user on the bot (- if the user is not added to the bot), channel is the channel the user was found on (read on for more info on this) and msg is the contents of the away message, if any. If a “*” is used for the channel in the mask, this bind is triggered once for every channel that the user is in the bot with; in other words if the bot is in two channels with the target user, the bind will be triggered twice. To trigger a proc only once per nick change, regardless of the number of channels the Eggdrop and user share, use the RAWT bind with AWAY as the keyword.

Module: irc

  1. INVT (stackable)

bind invt <flags> <mask> <proc>

procname <nick> <user@host> <channel> <invitee>

Description: triggered when eggdrop received an INVITE message. The mask for the bind is in the format “#channel nickname”, where nickname (not a hostmask) is that of the invitee. For the proc, nick is the nickname of the person sending the invite request, user@host is the user@host of the person sending the invite, channel is the channel the invitee is being invited to, and invitee is the target (nickname only) of the invite. The invitee argument was added to support the IRCv3 invite-notify capability, where the eggdrop may be able to see invite messages for other people that are not the eggdrop.

  1. RAWT (stackable)

bind rawt <flags> <mask> <proc>

procname <from> <keyword> <text> <tags>

Description: similar to the RAW bind, but allows an extra field for the IRCv3 message-tags capability. The mask can contain wildcards and is matched against the keyword which is either a numeric, like “368”, or a keyword, such as “PRIVMSG” or “TAGMSG”. “from” will be the server name or the source nick!ident@host (depending on the keyword); flags are ignored. “tag” is a dictionary (flat key/value list) of the message tags with “” for empty values (e.g. “account eggdrop realname LamestBot”). If the proc returns 1, Eggdrop will not process the line any further, to include not being processed by a RAW bind (this could cause unexpected behavior in some cases). As of 1.9.0, it is recommended to use the RAWT bind instead of the RAW bind.

  1. ACCOUNT (stackable)

bind account <flags> <mask> <proc>

procname <nick> <user> <hand> <chan> <account>

Description: this bind will trigger when eggdrop detects a change in the authentication status of a user’s service account. The mask for the bind is in the format “#channel nick!user@hostname.com account” and accepts wildcards. account is either the account name the user is logging in to or “*” if the user is not logged in to an account.

NOTE: the three required IRC components for account tracking are: the WHOX feature, the extended-join IRCv3 capability and the account-notify IRCv3 capability. if only some of the three feature are available, eggdrop provides best-effort account tracking but this bind could be triggered late or never on account changes. Please see doc/ACCOUNTS for additional information.

  1. ISUPPORT (stackable)

bind isupport <flags> <mask> <proc>

procname <key> <isset> <value>

Description: triggered when the value of an isupport key changes. The mask is matched against the isupport key. If the value is not set, isset is 0 and the value is the empty string. Because the empty string is valid value, use isset to distinguish empty string values from a key being unset. The bind is called before the change is processed, so [isupport isset]/[isupport get] return the old value. A return value other than 0 makes Eggdrop ignore the change and revert to the old value. After a disconnect from the server, all isupport values are reset to default, but $::server will be empty, so that case can be caught and ignored.

Module: server

  1. MONITOR (stackable)

bind monitor <flags> <nick> <proc>

procname <nick> <online>

Description: triggered when a server sends a MONITOR status change of a target either coming online or disconnecting (not all servers support MONITOR). flags are ignored, nick is the nickname of the intended MONITOR target and can be used with wildcards. For the proc, nick is the nickname connecting or disconnecting, and online is ‘0’ if the nickname disconnected, or ‘1’ if the nickname connected.

Module: irc

  1. CHGHOST

bind chghost <flags> <mask> <proc>

procname <nick> <old user@host> <handle> <channel> <new user@host>

Description: triggered when a server sends an IRCv3 spec CHGHOST message to change a user’s hostmask. The new host is matched against mask in the form of “#channel nick!user@host” and can contain wildcards. The specified proc will be called with the nick of the user whose hostmask changed; the hostmask the affected user had before the change, the handle of the affected user (or * if no handle is present), the channel the user was on when the bind triggered, and the new hostmask of the affected user. This bind will trigger once for each channel the user is on.

  1. CHANSET

bind chanset <flags> <mask> <proc>

procname <chan> <setting> <value>

Description: triggered when a channel setting is set via the partyline. flags is ignored, mask is the name of channel setting (not including any +/- prefix) and can contain wildcards. The proc will be called with the channel that the setting was set on, the text name of the setting that was changed, and the value it was set to (0/1 for -/+, string, or X:Y formatted value).

Return Values

Several bindings pay attention to the value you return from the proc(using ‘return <value>’). Usually, they expect a 0 or 1, and returning an empty return is interpreted as a 0. Be aware if you omit the return statement, the result of the last Tcl command executed will be returned by the proc. This will not likely produce the results you intended (this is a “feature” of Tcl).

Here’s a list of the bindings that use the return value from procs they trigger:

  1. MSG Return 1 to make Eggdrop log the command as:

    (nick!user@host) !handle! command
    
  2. DCC Return 1 to make Eggdrop log the command as:

    #handle# command
    
  3. FIL Return 1 to make Eggdrop log the command as:

    #handle# files: command
    
  4. PUB Return 1 to make Eggdrop log the command as:

    <<nick>> !handle! command
    
  5. CTCP Return 1 to ask the bot not to process the CTCP command on its own. Otherwise, it would send its own response to the CTCP (possibly an error message if it doesn’t know how to deal with it).

  6. FILT Return “” to indicate the text has been processed, and the bot should just ignore it. Otherwise, it will treat the text like any other.

  7. FLUD Return 1 to ask the bot not to take action on the flood. Otherwise it will do its normal punishment.

  8. RAW Return 1 to ask the bot not to process the server text. This can affect the bot’s performance by causing it to miss things that it would normally act on – you have been warned.

  9. CHON Return 1 to ask the bot not to process the partyline join event.

  10. CHOF Return 1 to ask the bot not to process the partyline part event.

  11. WALL Return 1 to make Eggdrop not log the message that triggered this bind.

  12. NOTE Return 1 to make Eggdrop not process the note any further. This includes stacked note bindings that would be processed after this one, as well as the built-in eggdrop note handling routines.

  13. MSGM Return 1 to make Eggdrop not log the message that triggered this bind.

  14. PUBM Return 1 to make Eggdrop not log the message that triggered this bind.

  15. NOTC Return 1 to make Eggdrop not log the message that triggered this bind.

  16. OUT Return 1 to make Eggdrop drop the message instead of sending it. Only meaningful for messages with status “queued”.

  17. EVNT Return 1 to make Eggdrop not to take the default action for the event. Used for signal type events, ignored for others.

  18. TLS Return 1 to disable verbose ssl information for the handshake.

  19. RAWT Return 1 to ask the bot not to process the server text. This can affet the bot’s performance by causing it to miss things that it would normally act on – you have been warned. Again.

  20. CHANSET Return 1 to prevent the channel setting from being changed.

Control Procedures

Using the ‘control’ command, you can put a DCC connection (or outgoing TCP connection) in control of a script. All text received from the connection is sent to the proc you specify. All outgoing text should be sent with ‘putdcc’.

The control procedure is called with these parameters:

procname <idx> <input-text>

This allows you to use the same proc for several connections. The idx will stay the same until the connection is dropped. After that, it will probably get reused for a later connection.

To indicate that the connection has closed, your control procedure will be called with blank text (the input-text will be “”). This is the only time it will ever be called with “” as the text, and it is the last time your proc will be called for that connection. Don’t call killdcc on the idx when text is blank, it will always fail with “invalid idx”.

If you want to hand control of your connection back to Eggdrop, your proc should return 1. Otherwise, return 0 to retain control.

TCP Connections

Eggdrop allows you to make two types of TCP (“telnet”) connections: outgoing and incoming. For an outgoing connection, you specify the remote host and port to connect to. For an incoming connection, you specify a port to listen on.

All of the connections are event driven. This means that the bot will trigger your procs when something happens on the connection, and your proc is expected to return as soon as possible. Waiting in a proc for more input is a no-no.

To initiate an outgoing connection, use:

set idx [connect <hostname> <[+]port>]

For SSL connections, prefix the port with a plus sign.

$idx now contains a new DCC entry for the outgoing connection.

All connections use non-blocking (commonly called “asynchronous”, which is a misnomer) I/O. Without going into a big song and dance about asynchronous I/O, what this means to you is:

  • assume the connection succeeded immediately

  • if the connection failed, an EOF will arrive for that idx

The only time a ‘connect’ will return an error is if you give it a hostname that can’t be resolved (this is considered a “DNS error”). Otherwise, it will appear to have succeeded. If the connection failed, you will immediately get an EOF.

Right after doing a ‘connect’ call, you should set up a ‘control’ for the new idx (see the section above). From then on, the connection will act just like a normal DCC connection that has been put under the control of a script. If you ever return “1” from the control proc (indicating that you want control to return to Eggdrop), the bot will just close the connection and dispose of it. Other commands that work on normal DCC connections, like ‘killdcc’ and ‘putdcc’, will work on this idx, too. The ‘killdcc’ command will fail with “invalid idx” if you attempt to use it on a closed socket.

To create a listen port, use:

listen <[+]port> script <proc>

By default, a listen port will allow both plaintext and SSL connections. To restrict a port to allow only SSL connections, prefix the port with a plus sign.

Procs should be declared as:

<procname> <newidx>

For example:

listen 6687 script listen:grab

proc listen:grab {newidx} {
  control $newidx listen:control
}

When a new connection arrives in port 6687, Eggdrop will create a new idx for the connection. That idx is sent to ‘listen:grab’. The proc immediately puts this idx under control. Once ‘listen:grab’ has been called, the idx behaves exactly like an outgoing connection would.

Secure connection can be also established after a connection is active. You can connect/listen normally and switch later using the ‘starttls’ command. Your script should first inform the other side of the connection that it wants to switch to SSL. How to do this is application specific.

The best way to learn how to use these commands is to find a script that uses them and follow it carefully. However, hopefully this has given you a good start.

Match Characters

Many of the bindings allow match characters in the arguments. Here are the four special characters:

?

matches any single character

*

matches 0 or more characters of any type

%

matches 0 or more non-space characters (can be used to match a single word) (This character only works in binds, not in regular matching)

~

matches 1 or more space characters (can be used for whitespace between words) (This char only works in binds, not in regular matching)

\*

matches a literal *, but please note that Tcl needs escaping as well, so a bind would have to use “\*” or {*} for a mask argument

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/text-sub.html000066400000000000000000000242671524730042700226350ustar00rootroot00000000000000 Textfile Substitutions — Eggdrop 1.10.2 documentation

Textfile Substitutions Last revised: March 08, 2002

Textfile Substitutions

These %-variables can be inserted into help files, the banner, the MOTD, and other text files. There are four variables that can be used to format text:

%b

display bold

%v

display inverse

%_

display underline

%f

display flashing via telnet; bold underline via IRC

These variables will be interpreted by Eggdrop and replaced by their respective values:

%B

bot’s nickname (i.e. “LamestBot”)

%V

current Eggdrop version (i.e. “eggdrop v1.10.2”)

%E

long form of %V (i.e. “Eggdrop v1.10.2 (C) 1997 Robey Pointer (C) 2010 Eggheads Development Team”)

%C

channels the bot is on (i.e. “#lamest, #botnetcentral”)

%A

whatever is set in the config file by ‘set admin’

%n

whatever is set in the config file by ‘set network’

%T

the current time (i.e. “15:00”)

%N

the current user’s nickname (i.e. “Robey”)

%U

the current operating system the bot is running on

%%

a percent sign (“%”)

You can also encode messages which can only be read by people with certain flags:

%{+m} Only masters would see this. %{-}

%{+A} Only people with the user flag A see this. %{-}

%{+b} This is only displayed to users doing a remote ‘.motd’ from another bot. %{-}

%{+|m} Only channel masters would see this. %{-}

Other variables:

%{cols=N}

start splitting output into N columns

%{cols=N/W}

same as above, but use a screen width of W

%{end}

end columnated or restricted (i.e. %{+m}) block

%{center}

center the following text (70 columns)

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/tls.html000066400000000000000000000460461524730042700216630ustar00rootroot00000000000000 TLS support — Eggdrop 1.10.2 documentation

TLS support Last revised: Mar 1, 2025

TLS support

This document provides information about TLS support which is a new Eggdrop feature since version 1.8.0.

About

Eggdrop can be optionally compiled with TLS support. This requires OpenSSL 0.9.8 or later installed on your system. TLS support includes encryption for IRC, DCC, botnet, telnet and scripted connections as well as certificate authentication for users and bots.

Installation

./configure and install as usual, the configure script will detect if your system meets the requirements and will enable TLS automatically. You can override the autodetection and manually disable TLS with ./configure –disable-tls. You can’t forcefully enable it though. The configure script will look for OpenSSL at the default system locations. If you have it installed at a non-standard location or locally in your home directory, you’ll need to specify the paths to header and library files with the –with-sslinc and –with-ssllib options. You can also use these if you want to override the default OpenSSL installation with a custom one, as they take precedence over any system-wide paths.

Usage

As of v1.9.0, TLS support must be requested explicitly for botnet links. To create a TLS-enabled listening port or connect to a TLS-enabled listening port, you must prefix the port with a plus sign (+). If a port number could normally be omitted as part of a command syntax must be included (and prefixed) to enable TLS.

Scripts can also upgrade a regular plaintext connection to TLS via STARTTLS using the starttls Tcl command.

Prior to v1.9.0, Eggdrop would use STARTTLS to automatically attempt to upgrade a plain connection to an encrypted connection for botnet links, without any additional configuration (This was changed to provide users the flexibility to configure their own environments and assist in debugging).

IRC

To connect to IRC using SSL, specify the port number and prefix it with a plus sign. Example: .jump irc.server.com +6697. The same goes for the server list in the config file.

Some NickServ services allow you to authenticate with a certificate. Eggdrop will use the certificate pair specified in ssl-privatekey/ ssl-certificate for authentication.

Botnet

Eggdrop can use TLS connections to protect botnet links if it is compiled with TLS support. As of version 1.9.0, only raw TLS sockets are used to protect a connection. By prefixing a listen port in the Eggdrop configuration with a plus (+), that specifies that port as a TLS-enabled port, and will only accept TLS connections (no plain text connections will be allowed). With two TLS-enabled Eggdrops, it graphically looks like this:

Leaf bot sets hub port as…

and Hub bot config uses…

the connection will…

port

listen port

be plain, but can be upgraded to TLS manually with the starttls Tcl/bot command

+port

listen +port

connect with TLS

port

listen +port

fail as hub only wants TLS

+port

listen port

fail as leaf only wants TLS

In short, a bot added to your Eggdrop with a +port in the address can only connect to a bot listening with a +port in the config. Conversely, a bot added to your Eggdrop without a + prefix can only connect to a bot listening without a + prefix in the config.

If TLS negotiation fails, the connection is deliberately aborted and no clear text is ever sent by the TLS-requiring party.

Eggdrop can also upgrade a plaintext connection with the starttls Tcl command. To use this, a plaintext connection is first made to a non-TLS port (ie, one that is not prefixed with a plus), then the starttls command is issued to upgrade that link to a TLS connection. In the Eggdrop 1.8 series, Eggdrop automatically attempted a starttls upgrade on all botnet connections. As such, if a 1.8 Eggdrop connects to a plain listening port on a 1.9.0 or later Eggdrop, it will automatically attempt to upgrade the link to TLS.

Secure DCC

Eggdrop supports the SDCC protocol, allowing you to establish DCC chat and file transfers over SSL. Example: /ctcp bot schat Note, that currently the only IRC client supporting SDCC is KVIrc. For information on how to initiate secure DCC chat from KVIrc (rather than from the bot with /ctcp bot chat), consult the KVIrc documentation.

Scripts

Scripts can open or connect to TLS ports the usual way specifying the port with a plus sign. Alternatively, the connection could be established as plaintext and later switched on with the starttls Tcl command. (Note that the other side should also switch to TLS at the same time - the synchronization is the script’s job, not Eggdrop’s.)

Keys, certificates and authentication

You need a private key and a digital certificate whenever your bot will act as a server in a connection of any type. Common examples are hub bots and TLS listening ports. General information about certificates and public key infrastructure can be obtained from Internet. This document only contains eggdrop-specific information on the subject. The easy way to create a key and a certificate is to type ‘make sslcert’ after compiling your bot (If you installed Eggdrop to a non-standard location, use make sslcert DEST=/path/to/eggdrop). This will generate a 4096-bit private key (eggdrop.key) and a certificate (eggdrop.crt) after you fill in the required fields. Alternatively, you can use ‘make sslsilent’ to generate a key and certificate non-interactively, using pre-set values. This is useful when installing Eggdrop via a scripted process.

To authenticate with a certificate instead of using password, you should make a ssl certificate for yourself and enable ssl-cert-auth in the config file. Then either connect to the bot using TLS and type “.fprint +” or enter your certificate fingerprint with .fprint SHA1-FINGERPRINT. To generate a ssl certificate for yourself, you can run the following command from the Eggdrop source directory:

openssl req -new -x509 -nodes -keyout my.key -out my.crt -config ssl.conf

When asked about bot’s handle, put your handle instead. How to use your new certificate to connect to Eggdrop, depends on your irc client. To connect to your bot from the command line using the key/cert files generated in the previous step, you can use the OpenSSL ssl client

openssl s_client -cert my.crt -key my.key -connect host:sslport

SSL/TLS Settings

There are some new settings allowing control over certificate verification and authorization.

ssl-privatekey

file containing Eggdrop’s private key, required for the certificate.

ssl-certificate

Specify the filename where your SSL certificate is located. if your bot will accept SSL connections, it must have a certificate.

ssl-verify-depth

maximum verification depth when checking certificate validity. Determines the maximum certificate chain length to allow.

ssl-capath
ssl-cafile

specify the location of certificate authorities certificates. These are used for verification. Both can be active at the same time. If you don’t set this, validation of the issuer won’t be possible and depending on verification settings, the peer certificate might fail verification.

ssl-ciphers

specify the list of ciphers (in order of preference) allowed for use with ssl.

ssl-cert-auth

enables or disables certificate authorization for partyline/botnet. This works only for SSL connections (SDCC or telnet over SSL). A setting of 1 means optional authorization: If the user/bot has a fingerprint set and it matches the certificate SHA1 fingerprint, access is granted, otherwise ordinary password authentication takes place.

If you set this to 2 however, users without a fingerprint set or with a fingerprint not matching the certificate, will not be allowed to enter the partyline with SSL. In addition to this user and bot certificates will be required to have an UID field matching the handle of the user/bot.

ssl-verify-dcc
ssl-verify-bots
ssl-verify-server
ssl-verify-clients

control ssl certificate verification. A value of 0 disables verification completely. A value of 1 enables full verification. Higher values enable specific exceptions like allowing self-signed or expired certificates. Details are documented in eggdrop.conf.

Copyright (C) 2010 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/tricks.html000066400000000000000000000274761524730042700223660ustar00rootroot00000000000000 Advanced Tips — Eggdrop 1.10.2 documentation

Eggdrop Tricks Last revised: Jun 02, 2021

Advanced Tips

Here are some little tricks that you may or may not know about, which aren’t documented in other areas.

Renaming commands

You can rename a built-in command by binding over it. To rename ‘.status’ to ‘.report’, you’d do:

unbind dcc - status *dcc:status
bind dcc m report *dcc:status

The first line removes the built-in binding on ‘.status’, and the second line binds ‘.report’ to the built-in status function.

Keeping Logs

If you don’t want your logfiles to be deleted after two days and don’t want the bot to create a new logfile each new day, then set ‘keep-all-logs’ to 0 and ‘switch-logfiles-at’ to 2500 in your bot’s config file to make it keeping one logfile all the time. This is not recommended on high traffic channels.

Self-logging

Because of how traditional IRC works, Eggdrop doesn’t capture outgoing messages to its logfile. However, if the server you are on supports the IRCv3 capability “echo-message”, you can request this capability to be enabled by the server in your config file. This will cause the server to send Eggdrop’d public messages back, thereby allowing those messages to be logged.

Modifying Default Strings

You can modify Eggdrop’s output in the partyline, kick messages, and other texts by editing core.english.lang in the language directory.

Modularizing Your Config File

You can export parts of your config file to separate files. For example, if you have several config files which differ from themselves only by the nickname and the used servers, you can export them to an own file and link it with the ‘source’ Tcl command, similar to a script. The advantage of this is that you have to edit/upload only the small file instead of the big one. This technique is also useful if you want to maintain the same channel settings, etc across your botnet.

Variables in Your Config

You can use variables in your config file, since it’s really just a normal Tcl file. For example, you can set ‘userfile’ and ‘chanfile’ to “yourbot.user” and “yourbot.chan” using the following method:

set myvar "yourbot"
set userfile "$myvar.user"
set chanfile "$myvar.chan"

Copyright (C) 1999 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/html/using/twitch-tcl-commands.html000066400000000000000000000606101524730042700247330ustar00rootroot00000000000000 Eggdrop Twitch Tcl Commands — Eggdrop 1.10.2 documentation

Eggdrop Twitch Tcl Commands Last revised: April 25, 2020

Eggdrop Twitch Tcl Commands

This is an exhaustive list of all the Twitch-specific Tcl commands added to Eggdrop. These commands are held separate from the traditional tcl-commands.doc due to the unique nature of Twitch and the fact that these commands WILL NOT work on a normal IRC server. Again, if you try and use these commands on a normal IRC server they will, at best, not work and possibly have unintended consequences.

This list is accurate for Eggdrop v1.9.0, the minimum version for which Twitch functionality is possible.

It should also be noted that if you are debugging scripts via the partyline, the Eggdrop partyline output has not been updated to handle the potentially LONG tag message lines that are sent by a server. This means if you view a variable that has stored all the tag messages sent by the server, it may appear truncated. Rest assured, the variable does contain the full set of tags, they just aren’t being displayed.

Commands

twcmd <chan> <cmd> [arg]

Description: sends cmd to the server, prefixed with a ‘/’. This is replicates the web UI functionality of sending commands such as /vip or /subscribers. Do not include a ‘/’ in the cmd. arg, if required, is a single argument, so enclose multiple strings in quotes. Example: twcmd timeout “badguy 100”

Returns: 0 if command is successfully sent to the Twitch server, 1 on failure

userstate <channel>

Description: provides current userstate for the Eggdrop on the given channel.

Returns: a dict containing key/value pairs for userstate values.

roomstate <channel>

Description: provides current roomstate of a channel Eggdrop is on.

Returns: a dict containing key/value pairs for roomstate values.

twitchmods <channel>

Description: maintains a list of usernames provided by Twitch as those that have moderator status on the provided channel. This list is refreshed upon join, or can manually be refreshed by using the Tcl twcmd to issue a /mods Twitch command. This list is a comprehensive list, the user does not need to be present on the channel to be included on this list.

Returns: A list of usernames designated as having moderator status by Twitch.

twitchvips <channel>

Description: maintains a list of usernames provided by Twitch as those that have VIP status on the provided channel. This list is refreshed upon join, or can manually be refreshed by using the Tcl twcmd to issue a /vips Twitch command. This list is a comprehensive list, the user does not need to be present on the channel to be included on this list.

Returns: A list of usernames designated as having VIP status by Twitch.

ismod <nick> [channel]

Description: checks if a user is on the moderator list maintained by Twitch (the same list accessible by the /mods command entered via the Twith web GUI). This differs from the other “normal” is* Eggdrop Tcl cmds, as this does NOT check if the user is currently on the channel (that status is unreliable on Twitch IRC).

Returns: 1 if someone by the specified nickname is on the moderator list for the channel (or any channel if no channel name is specified); 0 otherwise.

isvip <nick> [channel]

Description: checks if a user is on the VIP list maintained by Twitch (the same list accessible by the /vips command entered via the Twith web GUI). This differs from the other “normal” is* Eggdrop Tcl cmds, as this does NOT check if the user is currently on the channel (that status is unreliable on Twitch IRC).

Returns: 1 if someone by the specified nickname is on the VIP list for the channel (or any channel if no channel name is specified); 0 otherwise.

Binds

You can use the ‘bind’ command to attach Tcl procedures to certain events. The binds listed here are in addition to the binds listed in tcl-commands.doc.

Because Twitch offers an IRC gateway that significantly reduces traditional IRC functionality, the following binds listed in tcl-commands.doc are not compatible with Twitch. If they are in an existing script you are running that is fine, they just likely won’t do anything.

To remove a bind, use the ‘unbind’ command. For example, to remove the bind for the “stop” msg command, use ‘unbind msg - stop msg:stop’.

Flags

Most of the following binds have “flags” listed as an argument for the bind. Flags represents a flagmask that the user, if found, must match in order for the bind to trigger. Example flag masks are:

-

Matches any user existing in Eggdrop’s internal user list (added to the bot)

*

Matches any user, doesn’t need to be added to Eggdrop

+o

Matches a user with the global ‘o’ flag added to their handle on the Eggdrop

|+f

Matches a user with the ‘f’ flag added on a channel to their handle on the Eggdrop

+m|+m

Matches a user with the ‘m’ flag added either globally or on a channel on the Eggdrop

Bind Types

The following is a list of bind types and how they work. Below each bind type is the format of the bind command, the list of arguments sent to the Tcl proc, and an explanation.

  1. CCHT (CLEARCHAT)

bind ccht <flags> <mask> <proc>

procname <nick> <chan>

Description: Called when the chat history for the channel or a user on the channel is cleared. The value of mask specified in the bind is matched against #channel nick!nick@nick.tmi.twitch.tv and can use wildcards. If a user is found that matches mask, they must also match the flags mask. If the CLEARCHAT is only targeted at a specific user and not the channel, that user’s nickname will appear in nick, otherwise nick is empty. chan will contain the channel the CLEARCHAT was used on.

  1. CMSG (CLEARMSG)

bind cmsg <flags> <command> <proc>

procname <nick> <chan> <msgid> <msg>

Description: Called when a specific message on the channel is cleared. The value of mask specified in the bind is matched against #channel nick!nick@nick.tmi.twitch.tv and can use wildcards. If a user is found that matches mask, they must also match the flags mask. nick contains the user’s nickname, and chan will contain the channel the CLEARMSG was used on.

  1. HTGT (HOSTTARGET)

bind htgt <flags> <mask> <proc>

procname <target> <chan> <viewers>

Description: Called when a broadcaster starts or stops hosting another Twitch channel. mask is in the format “#channel target”, where #channel is the hosting channel and target is the name of the broadcaster being hosted by #channel. Similarly for the proc, target is the name of the Twitch channel being hosted by chan. A value of - in target indicates that the broadcaster has stopped hosting another channel. viewers contains the number of viewers in chan that are now watching target when hosting starts, but has been found to not be reliably provided by Twitch (often arbitrarily set to 0).

  1. WSPR (WHISPER)

bind wspr <flags> <command> <proc>

procname <nick> <userhost> <handle> <msg>

Description: Checks the first word of a whisper Eggdrop receives from another Twitch user. The first word of the whisper is matched against command, and the remainder of the text is passed to msg. nick is populated with the login name of the user messaging the Eggdrop, userhost contains nick’s userhost in the format nick!nick@nick.tmi.twitch.tv. handle will match the user’s handle on the bot if present, otherwise it will return a *.

  1. WSPM (WHISPER)

bind wspm <flags> <mask> <proc>

procname <nick> <userhost> <handle> <msg>

Description: Checks the entire contents of a whisper Eggdrop receives from another Twitch user. The contents of the whisper are matched against mask, which can contain wildcards. nick is populated with the login name of the user messaging the Eggdrop, userhost contains nick’s userhost in the format nick!nick@nick.tmi.twitch.tv. handle will match the user’s handle on the bot if present, otherwise it will return a *. The full text of the whisper is stored in msg.

  1. RMST (ROOMSTATE)

bind rmst <flags> <mask> <proc>

procname <chan> <tags>

Description: Called when Eggdrop receives a ROOMSTATE message. mask is in the format of #channel keys and can use wildcards. For example, to trigger this bind on #eggdrop for any change, you would use #eggdroptest * as the mask, or to trigger on #eggdrop specifically for the emote-only setting, you would use "#eggdrop *emote-only*" as the mask. Due to the nature of multiple keys per roomstate and uncertainty of ordering, it is recommended to use multiple binds if you wish to specify multiple key values. chan is the channel Eggdrop received the ROOMSTATE message for, and tags is a list of key/value pairs provided by the ROOMSTATE message, suitable for use as a Tcl dict. flags is ignored.

  1. USST (USERSTATE)

bind usst <flags> <mask> <proc>

procname <chan> <tags>

Description: Called when Eggdrop receives a USERSTATE message. mask is in the format of #channel keys and can use wildcards (see the RMST bind for additional details on format). chan is the channel Eggdrop received the USERSTATE message for, and tags is a list of key/value pairs provided in the USERSTATE message, suitable for use as a Tcl dict. flags is ignored.

  1. USRNTC (USERNOTICE)

bind usrntc <flags> <mask> <proc>

procname <chan> <tags>

Description: Called when Eggdrop received a USERNOTICE message. mask` is in the format of #channel keys and can use wildcards (see the RMST bind for additional details on format). chan is the channel Eggdrop received the USERNOTICE message for, and tags is a list of key/value pairs provided in the USERNOTICE message, suitable for use as a Tcl dict. flags is ignored.

eggheads-eggdrop-9c00fe1/doc/html/using/twitchinfo.html000066400000000000000000000353171524730042700232360ustar00rootroot00000000000000 Twitch — Eggdrop 1.10.2 documentation

Twitch

This module attempts to provide connectivity with the Twitch gaming platform. While Twitch provides an IRC gateway to connect with it’s streaming service, it does not claim to (and certainly does not) follow the IRC RFC in any meaningful way. The intent of this module is not to provide the full spectrum of management functions typically associated with Eggdrop; instead it focuses around the following key functions:

  • Logging of general and Twitch-specific events (raids, blocks, bit donations)

  • Tracking userstate and roomstate values

  • Adding Tcl event binds for a number of Twitch events

Disclaimer

We should also make clear that Eggdrop is in no way affiliated with Twitch in any way, and Twitch fully controls their own platform, to include the IRC gateway. This was just a fun project implemented at the request of some users to interact with the Twitch IRC development gateway as it existed at the time of development. At any time, Twitch could choose to alter or discontinue their IRC connectivity, thereby rendering this Eggdrop module useless. Eggdrop developers are also unable to offer technical support for Twitch-specific issues encountered while using this module.

Registering with Twitch

  1. Register an account with Twitch. At the time of writing, this is done by visiting Twitch and clicking on the Sign Up button.

  2. Generate a token to authenticate your bot with Twitch. At the time of writing, this is done by visiting the Twitch OAuth generator while logged in to the account you just created. The token will be an alphanumeric string and should be treated like a password (…because it is). Make note of it, and keep it safe!

Editing the config file

  1. Find the options to add a server in the server section of the config file. Remove the sample servers listed and add the following line in their place, replacing the alphanumeric string after ‘oauth:’ with the token you created when registering with Twitch in the previous section. Pretending your Twitch token from the previous step is ‘j9irk4vs28b0obz9easys4w2ystji3u’, it should look like this:

    server add irc.chat.twitch.tv 6667 oauth:j9irk4vs28b0obz9easys4w2ystji3u
    

Make sure you leave the ‘oauth:’ there, including the ‘:’.

  1. Spoiler alert- this step tells you to do nothing, but we’re including it for the sake of thoroughness. We know you are the ideal IRC user and have read all the docs for Twitch already, and noticed that Eggdrop has to request capabilities from the Twitch via a CAP request, something that is normally set in the config file. Good news! The Twitch module does this for you already, automatically. No need to edit the cap-request setting in the config. Moving on!

  2. Find the Twitch section of the config file, and enable the loading of the twitch module by removing the ‘#’ in front of the loadmodule command. It should look like this when you are done:

    loadmodule twitch
    
  3. Start your bot as usual, and good luck!

Twitch web UI functions

Twitch is normally accessed via a web UI, and uses commands prefixed with a . or a / to interact with the channel. The Twitch module adds the partyline command twcmd to replicate those Twitch-specific commands. For example, to grant VIP status to a user via Tcl, you would use the command .twcmd vip username. or to restrict chat to subscribers, you would use .twcmd subscribers (Note: no . or / is needed as a prefix to the Twitch command). In other words, .twcmd in Tcl is the interface to the standard Twitch set of commands available through the web UI (Also available as a Tcl command).

Twitch IRC limitations

There are a few things you should know about how Twitch provides service through the IRC gateway that affects how well Eggdrop can function:

  • Twitch does not broadcast JOINs or PARTs for channels over 1,000 users. This renders tracking users on a channel unreliable.

  • Twitch does not broadcast MODE changes for moderator status. This (mostly) renders tracking the status of users infeasible.

  • Twitch stores bans on its servers (and does not accept MODE +b), making the Eggdrop ban list (and exempts/invites) mostly useless

  • Twitch does not allow clients to issue MODE +o/-o commands, preventing Eggdrop from op’ing users through the traditional method

In light of these limitations, Eggdrop developers made an intentional decision to leave some non-compatible capabilities present with the Twitch module, most notably on the Tcl side. For example, if functionality such as the Tcl topic bind were to be removed, already-written scripts that had that feature in them would simply not load, rendering the entire Tcl script useless. By leaving these capabilities in, the hope is that existing Tcl scripts can still be used with Twitch, even if in a degraded capacity. As such, do be careful with the scripts you load as you may face errors you had not encountered before.

eggheads-eggdrop-9c00fe1/doc/html/using/users.html000066400000000000000000000310421524730042700222100ustar00rootroot00000000000000 Users and Flags — Eggdrop 1.10.2 documentation

Last revised: March 07, 2002

Users and Flags

People on IRC are recognized by the bot according to their nick!user@host. That is, if I am on IRC as:

*** Robey is robey@hubcap.clemson.edu (i hate milk)

Eggdrop will identify me according to “Robey!robey@hubcap.clemson.edu” and not only by my nickname.

Eggdrop does not have access levels like some bots. There are no meaningless numbers or titles. Instead, each user has “flags” that entitle them to certain privileges. Think of a flag as a badge. Any user can have any number of flags – you can have no flags, or you can have all of them. Some flags are good, some are bad. Each flag is identified by a letter. A channel flag applies only to a specific channel, and a global flag applies to all channels. The standard global flags are:

Flag

Attribute

Description

n

owner

user has absolute control. Only give this flag to people you trust completely.

m

master

user has access to almost every feature of the bot.

t

botnet-master

user has access to all features dealing with the botnet.

a

auto-op

user is opped automatically upon joining a channel.

o

op

user has op access to all of the bot’s channels.

y

auto-halfop

user is halfopped automatically upon joining a channel

l

halfop

user has halfop access to all of the bot’s channels.

g

auto-voice

user is voiced automatically upon joining a channel.

v

voice

user gets +v automatically on +autovoice channels.

f

friend

user is not punished for flooding, etc.

p

party

user has access to the partyline.

q

quiet

user does not get voice on +autovoice channels.

r

dehalfop

user cannot gain halfops on any of the bot’s channels.

d

deop

user cannot gain ops on any of the bot’s channels.

k

auto-kick

user is kicked and banned automatically.

x

xfer

user has access to the file transfer area of the bot the bot.

j

janitor

user can perform maintenance in the file area of the bot (if it exists) – like a “master” of the file area. Janitors have complete access to the filesystem.

c

common

this marks a user who is connecting from a public site from which any number of people can use IRC. The user will now be recognized by NICKNAME.

b

bot

user is a bot.

w

wasop-test

user needs wasop test for +stopnethack procedure.

z

washalfop-test

user needs washalfop test for +stopnethack procedure.

e

nethack-exempt

user is exempted from stopnethack protection.

u

unshared

user record is not sent to other bots.

h

highlight

use bold text in help/text files.

All global flags other then u, h, b, c, x, j, and p are also channel-specific flags. Flags are set with the chattr command. The syntax for this command is:

chattr <nickname> [attributes] [channel]

There are also 26 global user-defined flags and 26 channel user-defined flags (Capital letters A through Z). These are used by scripts, and their uses vary depending on the script that uses them.

Copyright (C) 2002 - 2025 Eggheads Development Team

eggheads-eggdrop-9c00fe1/doc/man1/000077500000000000000000000000001524730042700167245ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/man1/eggdrop.1000066400000000000000000000151021524730042700204340ustar00rootroot00000000000000.\" To view: groff -man -Tascii eggdrop.1 .\" .\" Copyright (C) 1999 - 2025 Eggheads Development Team .\" .\" This file is free software; you can redistribute it and/or modify it .\" under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but .\" WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU .\" General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .\" .\" As a special exception to the GNU General Public License, if you .\" distribute this file as part of a program that contains a .\" configuration script generated by Autoconf, you may include it under .\" the same distribution terms that you use for the rest of that program. .\" Originally by Robey Pointer, redone by rtc .\" * 1999-11-08 rtc A line wasn't displayed .\" * 2000-10-07 Fabian Updated and slightly re-organized. .\" * 2004-08-08 Wcc Updated. .\" * 2023-03-11 mortmann Typos and fixse .TH eggdrop 1 "March 2023" "Eggheads Development Team" "IRC Tools" .SH NAME eggdrop \- an IRC bot .SH SYNOPSIS .B eggdrop .RB [ options ] .RB [ config-file ] .SH DESCRIPTION .B Eggdrop is the world's most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature-rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms. .PP An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc. .PP One of the features that makes Eggdrop stand out from other bots is module and Tcl scripting support. With scripts and modules, you can make the bot perform almost any task you want. They can do anything from preventing floods to greeting users and banning advertisers from channels. .PP You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently, and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban lists, exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet. .PP Eggdrop needs a config file to run. For an example, have a look at .B eggdrop.conf which is distributed with Eggdrop. .SH OPTIONS .TP .B \-h Display a list of command-line options. .TP .B \-n Don't background. Normally, Eggdrop will move itself into the background when you start it up, meaning you'll get another shell prompt, and you can do other things while the bot is running. With \-n, you won't return to the shell prompt until the bot exits (which won't normally happen until it's killed). By default, \-n will send all log entries to the console. .TP .B \-t Don't background, use terminal. This is just like \-n, except that instead of seeing log entries, your console will simulate a DCC chat with the bot. .TP .B \-c Don't background, show channel info. This is just like \-n, except that instead of seeing log entries, every 10 seconds your screen will clear and you will see the current channel status, sort of like "top". .TP .B \-m Create userfile. If you don't have a userfile, this will make Eggdrop create one and give owner status to the first person that introduces himself or herself to it. You'll need to do this when you first set up your bot. .TP .B \-v Show version info and exit. .SH SIGNALS .TP .B SIGCHLD This signal is ignored by Eggdrop and can be used to determine whether it's running or not. .TP .B SIGTERM By default Eggdrop will save its user and channel file and die. A script can override the default handler by registering with the EVNT bind and returning 1. .TP .B SIGHUP By default Eggdrop will rehash (reload its config file). A script can override the default handler by registering with the EVNT bind and returning 1. .TP .B SIGQUIT By default Eggdrop will restart. A script can override the default handler by registering with the EVNT bind and returning 1. .SH "ENVIRONMENT VARIABLES" .TP .B EGG_LANG This variable can be set to the language in which you want Eggdrop to speak to you. It defaults to English, but Danish, Finnish, French, German, Italian and Portuguese are supported, too. .TP .B EGG_LANGDIR Specifies the directory where all your language files are stored. The default is ./language. .SH "SEE ALSO" tclsh(1), irc(1), ircII(1), ircd(8) .PP There is extensive .B online documentation. Once you get the bot running, open a DCC chat with it, and type: .B .help .PP In addition, the files in the \fBdoc/\fR directory provide \fBdetailed information\fR about how Eggdrop works and how to use it. .SH AUTHORS Written by Robey Pointer, the Eggheads Development Team and various others. See the files \fBAUTHORS\fR for a list of Eggdrop developers and major contributors, \fBTHANKS\fR for a full list of Eggdrop contributors, and the \fBdoc/Changes\fR files for a list of changes made to each version of Eggdrop. .SH "REPORTING BUGS" See \fBdoc/BUG-REPORT\fR. .PP Bugs can be reported directly via GitHub at \fBhttps://github.com/eggheads/eggdrop/issues\fR. .SH COPYRIGHT Copyright (C) 1997 Robey Pointer .br Copyright (C) 1999 - 2025 Eggheads Development Team .PP This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. .PP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. .PP You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. .\" end of man page eggheads-eggdrop-9c00fe1/doc/modules/000077500000000000000000000000001524730042700175405ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/modules/MODULES000066400000000000000000000110021524730042700205650ustar00rootroot00000000000000Modules included with Eggdrop mod/assoc mod/blowfish mod/channels mod/compress mod/console mod/ctcp mod/dns mod/filesys mod/ident mod/irc mod/notes mod/pbkdf2 mod/python mod/seen mod/server mod/share mod/transfer mod/twitch mod/woobie mod/uptime assoc This module provides assoc support, i.e. naming channels on the botnet. blowfish This module has been deprecated in favor of the pbkdf2 module for hashing purposes, such as passwords in the userfile. However, it is still required for encrypting/decrypting strings. channels This module provides channel related support for the bot. Without it, you won't be able to make the bot join a channel or save channel specific userfile information. compress This module provides support for file compression. This allows the bot to transfer compressed user files and, therefore, save a significant amount of bandwidth. console This module provides storage of console settings when you exit the bot or type .store on the partyline. ctcp This module provides the normal ctcp replies that you'd expect. Without it loaded, CTCP CHAT will not work. dns This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections. filesys This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. ident This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon. irc This module provides basic IRC support for your bot. You have to load this if you want your bot to come on IRC. notes This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval. pbkdf2 This modules updates Eggdrop to use PBKDF2 for hashing purposes, such as for userfile passwords. It was specifically designed to work with the blowfish module to make the transition from blowfish to pbkdf2 password hashing as easy as possible. If you are transitioning a userfile from 1.8 or earlier, you should load this AND the blowfish module. By doing so, Eggdrop will seamlessly update the old blowfish hashes to the new PBKDF2 hashes once a user logs in for the first time, and allow you to (eventually) remove the blowfish module altogether. For new bots, you should load this module by itself and not use the blowfish module. The blowfish module is still required if you use Tcl to encrypt/decrypt strings in Tcl (ie, some scripts). Eggdrop will not start without a password-hashing module loaded. python This module adds a python interpreter to Eggdrop, enabling you to run python scripts on your Eggdrop! seen This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot's userlist. If you are looking for a better and more advanced seen module, try the gseen module, originally written by G'Quann and forked/updated for modern Eggdrop by mortmann. You can find it at https://github.com/michaelortmann/gseen.mod server This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define. share This module provides userfile sharing support between two directly linked bots. transfer The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing. twitch The Twitch module modifies Eggdrop to interact with the Twitch service. Twitch uses a modified implementation of IRC so not all functionality will be present. Please read doc/TWITCH for specifics on how to best use the Tiwtch module. uptime This module reports uptime statistics to the uptime contest web site at https://www.eggheads.org/uptime. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn't listed, try again later. See doc/settings/mod.uptime for more information, including details on what information is sent to the uptime server. woobie This is for demonstrative purposes only. If you are looking for starting point in writing modules, woobie is the right thing. eggheads-eggdrop-9c00fe1/doc/modules/mod.assoc000066400000000000000000000004521524730042700213520ustar00rootroot00000000000000Last revised: January 1, 2002 Assoc Module This module provides assoc support, i.e. naming channels on the botnet. This module requires: none Put this line into your Eggdrop configuration file to load the assoc module: loadmodule assoc Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.blowfish000066400000000000000000000010231524730042700220520ustar00rootroot00000000000000Last revised: January 28, 2003 Blowfish Module Eggdrop can encrypt your userfile, so users can have secure passwords. Please note that when you change your encryption method later (i.e. using other modules like a md5 module), you can't use your current userfile anymore. Eggdrop will not start without an encryption module. This module requires: none Put this line into your Eggdrop configuration file to load the blowfish module: loadmodule blowfish Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.channels000066400000000000000000000352021524730042700220360ustar00rootroot00000000000000Last revised: October 25, 2010 Channels Module This module provides channel related support for the bot. Without it, you won't be able to make the bot join a channel or save channel specific userfile information. This module requires: none Put this line into your Eggdrop configuration file to load the channels module: loadmodule channels There are also some variables you can set in your config file: set chanfile "LamestBot.chan" Enter here the filename where dynamic channel settings are stored. set force-expire 0 Set this setting to 1 if you want your bot to expire bans/exempts/invites set by other opped bots on the channel. set share-greet 0 Set this setting to 1 if you want your bot to share user greets with other bots on the channel if sharing user data. set use-info 1 Set this setting to 1 if you want to allow users to store an info line. set allow-ps 0 Set this setting to 1 if you want to allow both +p and +s channel modes to be enforced at the same time by the chanmode channel setting. Make sure your server supports +ps mixing or you may get endless mode floods. channel add #channel { SETTINGS } This command adds a static channel to your bot (it cannot be removed via the partyline- we recommend using the .+chan command on the partyline instead). This command will add the channel preconfigured with the specified settings. A full list of settings is defined in Channel Settings. CHANNEL SETTINGS There are two types of channel settings: value-based settings (where you configure a setting with a number or string), and enable/disable-based settings (where you turn a setting on or off). These settings can be configured via Tcl using the 'channel set' command: channel set This command modifies a specific channel setting for a channel. There are many different options for channels which you can define. Some settings are enabled or disabled by a plus or minus in front of them, and others directly take text or integer values. Value-based Channel Settings chanmode +/- This setting makes the bot enforce channel modes. It will always enforce the + and remove the - modes. idle-kick 0 This setting will make the bot check every minute for idle users. Set this to 0 to disable idle check. stopnethack-mode 0 This setting will make the bot de-op anyone who enters the channel with serverops. There are seven different modes for this settings: --- -------------------------------------------------------- 0 turn off 1 isoptest (allow serverop if registered op) 2 wasoptest (allow serverop if user had op before split) 3 allow serverop if isop or wasop 4 allow serverop if isop and wasop. 5 If the channel is -bitch, see stopnethack-mode 3 If the channel is +bitch, see stopnethack-mode 1 6 If the channel is -bitch, see stopnethack-mode 2 If the channel is +bitch, see stopnethack-mode 4 --- -------------------------------------------------------- revenge-mode 0 This settings defines how the bot should punish bad users when revenging. There are four possible settings: --- ------------------------------------------------------------------- 0 Deop the user. 1 Deop the user and give them the +d flag for the channel. 2 Deop the user, give them the +d flag for the channel, and kick them. 3 Deop the user, give them the +d flag for the channel, kick, and ban them. --- ------------------------------------------------------------------- ban-type 3 This setting defines what type of bans should eggdrop place for +k users or when revenge-mode is 3. Available types are: ------------------- ------------------------ 0 *!user@host 1 *!*user@host 2 *!*@host 3 *!*user@*.host 4 *!*@*.host 5 nick!user@host 6 nick!*user@host 7 nick!*@host 8 nick!*user@*.host 9 nick!*@*.host ------------------- ------------------------ You can also specify types from 10 to 19 which correspond to types 0 to 9, but instead of using a * wildcard to replace portions of the host, only numbers in hostnames are replaced with the '?' wildcard. Same is valid for types 20-29, but instead of '?', the '' wildcard will be used. Types 30-39 set the host to ''. ban-time 120 Set here how long temporary bans will last (in minutes). If you set this setting to 0, the bot will never remove them. exempt-time 60 Set here how long temporary exempts will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the exempts every X minutes, but will not remove the exempt if a ban is set on the channel that matches that exempt. Once the ban is removed, then the exempt will be removed the next time the bot checks. Please note that this is an IRCnet feature. invite-time 60 Set here how long temporary invites will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the invites every X minutes, but will not remove the invite if a channel is set to +i. Once the channel is -i then the invite will be removed the next time the bot checks. Please note that this is an IRCnet feature. aop-delay (minimum:maximum) This is used for autoop, autohalfop, autovoice. If an op or voice joins a channel while another op or voice is pending, the bot will attempt to put both modes on one line. -------------- ----------------------------------------- aop-delay 0 No delay is used. aop-delay X An X second delay is used. aop-delay X:Y A random delay between X and Y is used. -------------- ----------------------------------------- need-op { putserv "PRIVMSG #lamest :op me cos i'm lame!" } This setting will make the bot run the script enclosed in braces if it does not have ops. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-invite { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs an invite to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-key { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs the key to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-unban { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs to be unbanned on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-limit { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs the limit to be raised on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. flood-chan 15:60 Set here how many channel messages in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables text flood protection for the channel, where X is an integer >= 0. flood-deop 3:10 Set here how many deops in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables deop flood protection for the channel, where X is an integer >= 0. flood-kick 3:10 Set here how many kicks in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables kick flood protection for the channel, where X is an integer >= 0. flood-join 5:60 Set here how many joins in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables join flood protection for the channel, where X is an integer >= 0. flood-ctcp 3:60 Set here how many channel ctcps in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables ctcp flood protection for the channel, where X is an integer >= 0. flood-nick 5:60 Set here how many nick changes in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables nick flood protection for the channel, where X is an integer >= 0. Enable/Disable Channel Settings These settings should be preceded by a + or - to enable or disable the setting, respctively. enforcebans When a ban is set, kick people who are on the channel and match the ban dynamicbans Only activate bans on the channel when necessary? This keeps the channel's ban list from getting excessively long. The bot still remembers every ban, but it only activates a ban on the channel when it sees someone join who matches that ban. userbans Allow bans to be made by users directly? If turned off, the bot will require all bans to be made through the bot's console. dynamicexempts Only activate exempts on the channel when necessary? This keeps the channel's exempt list from getting excessively long. The bot still remembers every exempt, but it only activates a exempt on the channel when it sees a ban set that matches the exempt. The exempt remains active on the channel for as long as the ban is still active. userexempts Allow exempts to be made by users directly? If turned off, the bot will require all exempts to be made through the bot's console. dynamicinvites Only activate invites on the channel when necessary? This keeps the channel's invite list from getting excessively long. The bot still remembers every invite, but the invites are only activated when the channel is set to invite only and a user joins after requesting an invite. Once set, the invite remains until the channel goes to -i. userinvites Allow invites to be made by users directly? If turned off, the bot will require all invites to be made through the bot's console. autoop Op users with the +o flag as soon as they join the channel This is insecure and not recommended. autohalfop Halfop users with the +l flag as soon as they join the channel? This is insecure and not recommended. bitch Only let users with the +o flag have op on the channel greet Say a user's info line when they join the channel protectops Re-op a user with the +o flag if they get deopped? protecthalfops Re-halfop a user with the +l flag if they get dehalfopped protectfriends Re-op a user with the +f flag if they get deopped statuslog Log the channel status line every 5 minutes? This shows the bot's status on the channel (op, voice, etc.), the channel's modes, and the total number of members, ops, voices, regular users, and +b, +e, and +I modes on the channel. A sample status line follows: [01:40] @#lamest (+istn) : [m/1 o/1 v/4 n/7 b/1 e/5 I/7] revenge Remember people who deop/kick/ban the bot, valid ops, or friends and punish them? Users with the +f flag are exempt from revenge. revengebot This is similar to to the 'revenge' option, but it only triggers if a bot gets deopped, kicked or banned. autovoice Voice users with the +v flag when they join the channel? secret Prevent this channel from being listed on the botnet? shared Share channel-related user info for this channel? cycle Cycle the channel when it has no ops? dontkickops Do you want the bot not to be able to kick users who have the +o flag, letting them kick-flood for instance to protect the channel against clone attacks? inactive This prevents the bot from joining the channel (or makes it leave the channel if it is already there). It can be useful to make the bot leave a channel without losing its settings, channel-specific user flags, channel bans, and without affecting sharing. seen Respond to seen requests in the channel? The seen module must be loaded for this to work. nodesynch Allow non-ops to perform channel modes? This can stop the bot from fighting with services such as ChanServ, or from kicking IRCops when setting channel modes without having ops. static Allow only permanent owners to remove the channel DEFAULT CHANNEL VALUES The following settings are used as default values when you .+chan #chan or .tcl channel add #chan. You can modify these default values by adding the below values to your config fil. Look in the Channel Settings for an explanation of each option. set default-flood-chan 15:60 set default-flood-deop 3:10 set default-flood-kick 3:10 set default-flood-join 5:60 set default-flood-ctcp 3:60 set default-flood-nick 5:60 set default-aop-delay 5:30 set default-idle-kick 0 set default-chanmode "nt" set default-stopnethack-mode 0 set default-revenge-mode 0 set default-ban-type 3 set default-ban-time 120 set default-exempt-time 60 set default-invite-time 60 set default-chanset { -autoop -autovoice -bitch +cycle +dontkickops +dynamicbans +dynamicexempts +dynamicinvites -enforcebans +greet -inactive -nodesynch -protectfriends +protectops -revenge -revengebot -secret -seen +shared -statuslog +userbans +userexempts +userinvites -protecthalfops -autohalfop -static } Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.compress000066400000000000000000000014031524730042700220720ustar00rootroot00000000000000Last revised: May 27, 2004 Compress Module This module provides support for file compression. It can be used to compress files via Tcl or to transfer the userfile compressed during the share process, saving bandwidth. This module requires: share Put this line into your Eggdrop configuration file to load the compress module: loadmodule compress There are also some variables you can set in your config file: set share-compressed 1 Allow compressed sending of user files? The user files are compressed with the compression level defined in 'compress-level'. set compress-level 9 This is the default compression level used. These levels are the same as those used by GNU gzip. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.console000066400000000000000000000014451524730042700217070ustar00rootroot00000000000000Last revised: January 1, 2002 Console Module This module provides storage of console settings when you exit the bot or type .store on the partyline. This module requires: none Put this line into your Eggdrop configuration file to load the console module: loadmodule console There are also some variables you can set in your config file: set console-autosave 1 Save users console settings automatically? Otherwise, they have to use the .store command. set force-channel 0 If a user doesn't have any console settings saved, which channel do you want them automatically put on? set info-party 0 Enable this setting if a user's global info line should be displayed when they join a botnet channel. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.ctcp000066400000000000000000000021731524730042700211750ustar00rootroot00000000000000Last revised: February 12, 2002 CTCP Module This module provides the normal ctcp replies that you would expect. Without it loaded, CTCP CHAT will not work. This module requires: server Put this line into your Eggdrop configuration file to load the ctcp module: loadmodule ctcp There are also some variables you can set in your config file: set ctcp-mode 0 Set here how the ctcp module should answer ctcps. There are 3 possible operating modes: --- -------------------------------------------------------------- 0 Normal behavior is used. 1 The bot ignores all ctcps, except for CHAT and PING requests by users with the +o flag. 2 Normal behavior is used, however the bot will not answer more than X ctcps in Y seconds (defined by 'set flood-ctcp'). --- -------------------------------------------------------------- There are also several variables to help make your bot less noticeable. They are: ctcp-version, ctcp-finger, and ctcp-userinfo. You can use set to set them to values you'd like. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.dns000066400000000000000000000027771524730042700210420ustar00rootroot00000000000000Last revised: September 26, 2010 DNS Module This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections. This module requires: none Put this line into your Eggdrop configuration file to load the dns module: loadmodule dns There are also some variables you can set in your config file: set dns-servers "8.8.8.8 8.8.4.4" In case your bot has trouble finding dns servers or you want to use specific ones, you can set them here. The value is a list of dns servers. The relative order doesn't matter. You can also specify a non-standard port. The default is to use the system specified dns servers. You don't need to modify this normally. set dns-cache 86400 Specify how long should the DNS module cache replies at maximum. The value must be in seconds. Note that it will respect the TTL of the reply and this is just an upper boundary. set dns-negcache 600 Specify how long should the DNS module cache negative replies (NXDOMAIN, DNS Lookup failed). The value must be in seconds. set dns-maxsends 4 How many times should the DNS module resend the query for a given domain if it receives no reply? set dns-retrydelay 3 Specify how long should the DNS module wait for a reply before resending the query. The value must be in seconds. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.filesys000066400000000000000000000141461524730042700217250ustar00rootroot00000000000000Last revised: Dec 30, 2017 Filesys Module This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. This module requires: transfer CONFIG FILE SETUP Put this line into your Eggdrop configuration file to load the filesys module: loadmodule filesys There are also some variables you can set in your config file: set files-path "/home/mydir/eggdrop/filesys" Set here the 'root' directory for the file system. set incoming-path "/home/mydir/eggdrop/filesys/incoming" If you want to allow uploads, set this to the directory uploads should be put into. Set this to "" if you don't want people to upload files to your bot. set upload-to-pwd 0 If you don't want to have a central incoming directory, but instead want uploads to go to the current directory that a user is in, set this setting to 1. set filedb-path "" Eggdrop creates a '.filedb' file in each subdirectory of your file area to keep track of its own file system information. If you can't do that (for example, if the dcc path isn't owned by you, or you just don't want it to do that) specify a path here where you'd like all of the database files to be stored instead. set max-file-users 20 Set here the maximum number of people that can be in the file area at once. Setting this to 0 makes it effectively infinite. set max-filesize 1024 Set here the maximum allowable file size that will be received (in KB). Setting this to 0 makes it effectively infinite. PARTYLINE USAGE .files Moves you into the file transfer sub-system, if it has been enabled on this bot. From there you can browse through the files online and use dcc file transfers to download and upload. .cancel [file] ... Tells the bot to stop sending a file that is pending (either queued, waiting, or in the process of being transferred). .cd Changes your current directory if possible. this works exactly like the unix command. .cp Copies a file or group of files from one place to another. .desc Changes the description for a file. if you are a master or file janitor, you can change the description for any file. otherwise you can only change the descriptions for files you have uploaded. The description is restricted to 3 lines of 60 characters each, and is broken up between words. you can force a line break by putting a '|' in the comment. .filestats [clear] Reports on the users upload & download statistics. Optional argument 'clear' clears a users upload and download statistics. .stats Clears a users upload & download statistics. .get [nickname] Sends you the file(s) requested, over IRC. you should get a DCC SEND notice on IRC, and have your client accept it. if your nickname on IRC is different than the one you use on the bot, you should specify a nickname too. you can also use that to send files to other people. if a file you want is actually on another bot, it may take a little bit longer so be patient. if that bot isn't on the botnet right now, it will say the file isn't available. There is a limit to the number of transfers you can have going simultaneously, so if you go over this limit, the remainder of your file requests will be queued. As the first files finish transferring, the queued files will be sent. .hide [files] ... Marks a file as hidden, so that normal users can't see it. Only a master or file janitor using %b'lsa'%b can see hidden files. .ln Creates a link to a file on another bot. The filepath has to be complete, like '/gifs/uglyman.gif'. If the bot is not connected to the botnet, nobody will be able to download the file until that bot connects again. The local filename can be anything you want. Example: ln Snowbot:/gifs/uglyman.gif ugly.gif .ls [filemask] Displays the files in the current directory. Subdirectories are shown with "" next to them, and other files will display their size (typically in kilobytes), who uploaded them (and when), and how many times each file has been downloaded. If a description of the file exists, it is displayed below the filename. You can restrict the file listing by specifying a mask, just like in unix. .mkdir [flags [channel]] Creates a subdirectory from this one, with the given name. If flags are specified, then those flags are required to enter or even see the directory. You can even specify a channel that the flags are matched against. You can use the %b'mkdir'%b command again to alter or remove those flags. .mv Moves a file or group of files from one place to another (it can also be used to rename files). .pending Gives you a listing of every file you've requested which is still waiting, queued, or in the process of transferring. It shows you the nickname on IRC that the file is being sent to, and, if the transfer is in progress, tells you how far along the transfer is. .pwd Tells you what your current directory is. .quit Exits the file system. rm [files] ... Erase a file for good. .rmdir Removes an existing directory, if there are no files in it. .share [files] ... Marks a file as shared. This means that other bots can get the file remotely for users on their file systems. By default, files are marked as unshared. .optimize Cleans up the current directory's database. If you have a large directory with many files you may want to use this command if you experience slow-downs/delays over time. Normally, the db should clean up itself though. .unhide Makes a file be not hidden any more. .unshare [file] ... Removes the shared tag from a file. .filesys module This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. The following commands are provided by the filesys module: For filesystem users: files Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.ident000066400000000000000000000064021524730042700213460ustar00rootroot00000000000000Last revised: June 27, 2019 Ident Module This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon. Traditionally, an IRC server queries port 113 on an irc client's host to determine the ident for the client. The client's host normally replies with the username of the account. If the host machine is running oident as its ident service, Eggdrop can now interact with oident to instead reply with a custom ident specified by Eggdrop (oidentd is an external service not specific to Eggdrop nor maintained by Eggheads developers, thus no support for it can be given). Alternatively, this module can also allow Eggdrop to act as its own ident daemon, answering queries from the IRC server directly. While this option may be of use to a very specific group of people, it requires additional external configuration via root access. Because most modern OSs do not allow user-level programs to bind to port 113, required by this option, a user must either: - Allow Eggdrop to bind to ports <1024 via the command: sudo setcap "cap_net_bind_service=+ep" /path/to/eggdrop Warning: this will allow Eggdrop to any port below 1024, potentially allowing a user to impersonate a standard system service with Eggdrop - Use iptables to re-route ident queries destined for port 113 to a >1024 port that Eggdrop can bind to, for example by running: sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport \ 113 -j REDIRECT --to-port ' Additionally, using Eggdrop as a built-in ident daemon can cause issues when running multiple bots from the same account. The ident server is only activated and bound to the configured ident port while Eggdrop is connecting to an IRC server, but only one Eggdrop can bind to port 113 at a time. If multiple bots were to start at the same time, such as after a netsplit or host reboot, only one bot can bind to port 113 at a time, creating ident conflicts. Thus, this option is best suited for single-bot environments. This module requires: - oident to be running on your shell host, configured to allow user spoofing, for the oidentd method. Put this line into your Eggdrop configuration file to load the ident module: loadmodule ident There are also some variables you can set in your config file: set ident-method 0 This sets which ident method you wish to use: 0 = oidentd / Your bot will overwrite $HOME/.oidentd.conf right before opening the socket to the IRC server with the global reply. 1 = Builtin / Your bot will automatically turn its builtin identd on and off when needed so it shouldn't conflict with other identds that do the same. Ident port 113 is normally restricted to privileged processes on UNIX systems. set ident-port 113 This sets the port which port Eggdrop will attempt to bind to for option 1 of ident-method. Most OSs do not allow non-privileged programs such as Eggdrop to bind to ports <1024; please read the beginning of this document for potential ways to implement this setting. Copyright (C) 2019 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.irc000066400000000000000000000133241524730042700210210ustar00rootroot00000000000000Last revised: August 21, 2004 IRC Module This module controls the bots interaction on IRC. It allows the bot to join channels, etc. You have to load this if you want your bot to come on irc. This module requires: server, channels Put this line into your Eggdrop configuration file to load the irc module: loadmodule irc There are also some variables you can set in your config file: set bounce-bans 1 Set this to 1 if you want to bounce all server bans. set bounce-exempts 0 Set this to 1 if you want to bounce all server exemptions (+e modes). This is disabled if use-exempts is disabled. set bounce-invites 0 Set this to 1 if you want to bounce all server invitations (+I modes). This is disabled if use-invites is disabled. set bounce-modes 0 Set this to 1 if you want to bounce all server modes. set max-modes 30 There is a global limit for +b/+e/+I modes. This limit should be set to the same value as max-bans for networks that do not support +e/+I. set max-bans 30 Set here the maximum number of bans you want the bot to set on a channel. Eggdrop will not place any more bans if this limit is reached. Undernet currently allows 45 bans, IRCnet allows 30, EFnet allows 100, and DALnet allows 100. set max-exempts 20 Set here the maximum number of exempts you want Eggdrop to set on a channel. Eggdrop will not place any more exempts if this limit is reached. set max-invites 20 Set here the maximum number of invites you want Eggdrop to set on a channel. Eggdrop will not place any more invites if this limit is reached. set use-exempts 0 set use-invites 0 These settings should be left commented unless the default values are being overridden. By default, exempts and invites are on for EFnet and IRCnet, but off for all other large networks. This behavior can be modified with the following 2 flags. If your network doesn't support +e/+I modes then you will be unable to use these features. set learn-users 0 If you want people to be able to add themselves to the bot's userlist with the default userflags (defined above in the config file) via the 'hello' msg command, set this to 1. set wait-split 600 Set here the time (in seconds) to wait for someone to return from a netsplit (i.e. wasop will expire afterwards). Set this to 1500 on IRCnet since its nick delay stops after 30 minutes. set wait-info 180 Set here the time (in seconds) that someone must have been off-channel before re-displaying their info line. set mode-buf-length 200 Set this to the maximum number of bytes to send in the arguments of modes sent to the server. Most servers default this to 200. unbind msg - hello *msg:hello bind msg - myword *msg:hello Many IRCops find bots by seeing if they reply to 'hello' in a msg. You can change this to another word by un-commenting these two lines and changing "myword" to the word wish to use instead of'hello'. It must be a single word. unbind msg - ident *msg:ident unbind msg - addhost *msg:addhost Many takeover attempts occur due to lame users blindly /msg ident'ing to the bot and attempting to guess passwords. We now unbind this command by default to discourage them. You can enable this command by un-commenting these two lines. set opchars "@" #set opchars "@&~" Some IRC servers are using some non-standard op-like channel prefixes/modes. Define them here so the bot can recognize them. Just "@" should be fine for most networks. Un-comment the second line for some UnrealIRCds. set no-chanrec-info 0 If you are so lame you want the bot to display peoples info lines, even when you are too lazy to add their chanrecs to a channel, set this to 1. NOTE This means every user with an info line will have their info line displayed on EVERY channel they join (provided they have been gone longer than wait-info). These were the core irc module settings. There are more settings for 'net-type' IRCnet and Efnet. net-type has to be set in the server module config section. Use the following settings only if you set 'net-type' to IRCnet! set prevent-mixing 1 At the moment, the current IRCnet IRCd version (2.10) doesn't support the mixing of b, o and v modes with e and I modes. This might be changed in the future, so use 1 at the moment for this setting. Use the following settings only if you set 'net-type' to Efnet! set kick-method 1 If your network supports more users per kick command then 1, you can change this behavior here. Set this to the number of users to kick at once, or set this to 0 for all at once. set modes-per-line 3 Some networks allow you to stack lots of channel modes into one line. They're all guaranteed to support at least 3, so that's the default. If you know your network supports more, you may want to adjust this. This setting is limited to 6, although if you want to use a higher value, you can modify this by changing the value of MODES_PER_LINE_MAX in src/chan.h and recompiling the bot. set include-lk 1 Some networks don't include the +l limit and +k or -k key modes in the modes-per-line (see above) limitation. Set include-lk to 0 for these networks. set use-354 0 Set this to 1 if your network uses IRCu2.10.01 specific /who requests. Eggdrop can, therefore, ask only for exactly what's needed. set rfc-compliant 1 If your network doesn't use rfc 1459 compliant string matching routines, set this to 0. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.notes000066400000000000000000000023161524730042700213730ustar00rootroot00000000000000Last revised: January 1, 2002 Notes Module This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval. This module requires: none Put this line into your Eggdrop configuration file to load the notes module: loadmodule notes There are also some variables you can set in your config file: set notefile "LamestBot.notes" Set here the filename where private notes between users are stored. set max-notes 50 Set here the maximum number of notes to allow to be stored for each user (to prevent flooding). set note-life 60 Set here how long (in days) to store notes before expiring them. set allow-fwd 0 Set this to 1 if you want to allow users to specify a forwarding address for forwarding notes to another account on another bot. set notify-users 1 Set this to 1 if you want the bot to let people know hourly if they have any notes. set notify-onjoin 1 Set this to 1 if you want the bot to let people know on join if they have any notes. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.pbkdf2000066400000000000000000000030071524730042700214110ustar00rootroot00000000000000Last revised: October 28, 2020 PBKDF2 Module Eggdrop encrypts its userfile, so users can have secure passwords. Eggdrop will not start without an encryption module. As of Eggdrop 1.9.0, a new encryption module was added, the PBKDF2 module. The PBKDF2 module was designed to work with the previous encryption module (blowfish) to make the transition easier. If this is a new bot, you can safely load just the PBKDF2 module. If you are transitioning a userfile from 1.8 or earlier, you should load this AND the blowfish module. By doing so, Eggdrop will seamlessly update the old blowfish hashes to the new PBKDF2 hashes once a user logs in for the first time, and allow you to (eventually) remove the blowfish module altogether. Outside of the specific case noted above, please note that if you change your encryption method later (i.e. using other modules like a rijndael module), you can't use your current userfile anymore. Put this line into your Eggdrop configuration file to load the server module:: loadmodule pbkdf2 There are also some variables you can set in your config file: set pbkdf2-method "SHA256" Cryptographic hash function used. Use openssl list -digest-algorithms to view all available options set pbkdf2-rounds 1600 Number of rounds. The higher the number, the longer hashing takes; but also generally the higher the protection against brute force attacks. This module requires: none Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.python000066400000000000000000000154551524730042700215740ustar00rootroot00000000000000Using 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. The minimum supported Python version is 3.8 and we do require the Global Interpreter Lock for thread safety, even if you use latest Python. 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 If you need to install dependencies we recommend using virtual environments, see https://docs.python.org/3/library/venv.html for more details. To create the virtual environment in a hidden directory called .venv (only necessary once): cd eggdrop && python3 -m venv .venv To install a python package in the above .venv directory (in this example, the requests package): cd eggdrop && source .venv/bin/activate && pip install requests Starting eggdrop with activated venv (must be run every time for proper functionality): cd eggdrop && source .venv/bin/activate && ./eggdrop You always need to start Eggdrop with the activated venv to set the necessary environment variables. RELOADING PYTHON SCRIPTS Unfortunately, reloading python scripts with rehash like Tcl scripts is currently unsupported. Scripts can unbind their existing binds and re-bind them on being loaded again (see the bind section). For now, you should restart your bot when the Python scripts change. You can (should?) also write scripts that manually unload their binds upon a reshash, example code looks like this: # Create a list to track the join binds if 'JOIN_BINDS' in globals(): for joinbind in JOIN_BINDS: joinbind.unbind() del JOIN_BINDS JOIN_BINDS = list() <...> # Create the binds in the script like this JOIN_BINDS.append(bind("join", "*", "*", joinGreetUser)) JOIN_BINDS.append(bind("join", "o", "*", joinGreetOp)) MULTITHREADING AND ASYNC pysource loads a Python script in the main Eggdrop thread but is free to use both async Python and threads. 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 bind_types. The eggdrop.tcl.bind command should not be used as it will attempt to call a Tcl proc. The bind command returns a PythonBind object that has an unbind method: x = bind("pub", "*", "!foo", myproc) x.unbind() 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: 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). Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.seen000066400000000000000000000011201524730042700211650ustar00rootroot00000000000000Last revised: Mar 14, 2023 Seen Module This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot's userlist. If you are looking for a better and more advanced seen module, try the gseen module originally written by G'Quann and forked/updated to work with modern Eggdrop by mortmann. You can find it at https://github.com/michaelortmann/gseen.mod. This module requires: none Put this line into your Eggdrop configuration file to load the seen module: loadmodule seen Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.server000066400000000000000000000213111524730042700215450ustar00rootroot00000000000000Last revised: October 25, 2010 Server Module This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define. This module requires: none Put this line into your Eggdrop configuration file to load the server module: loadmodule server There are also some variables you can set in your config file: set net-type Efnet What is your network? Possible allowed values are Efnet, IRCnet, Undernet, DALnet, Libera, freenode, Quakenet, Rizon, Other. If the network you use is not listed, using "Other" is a good sane choice and can be customized with settings both here and in the IRC module sections of the config file. set nick "LamestBot" Set the nick the bot uses on IRC, and on the botnet unless you specify a separate botnet-nick, here. set altnick "L?m?stB?t" Set the alternative nick which the bot uses on IRC if the nick specified by 'set nick' is unavailable. All '?' characters will be replaced by random numbers. set keep-nick 1 This setting makes the bot try to get his original nickname back if its primary nickname is already in use. set realname "/msg LamestBot hello" Set here what to display in the real-name field for the bot. bind evnt - init-server evnt:init_server proc evnt:init_server {type} { global botnick putquick "MODE $botnick +i-ws" } ... is Tcl scripting that is run immediately after connecting to a server. set init-server { putquick "MODE $botnick +i-ws" } ... is Tcl scripting that is run immediately after connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). set connect-server { putlog "Connecting to server." } ... is Tcl scripting that is run immediately before connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). set disconnect-server { putlog "Disconnected from server." } ... is a Tcl script that is run immediately after disconnecting from a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). set servers { you.need.to.change.this:6667 another.example.com:7000:password [2001:db8:618:5c0:263::]:6669:password ssl.example.net:+6697 } ... is the bot's server list. The bot will start at the first server listed, and cycle through them whenever it gets disconnected. You need to change these servers to YOUR network's servers. The format is: server[:port[:password]] Prefix the port with a plus sign to attempt a SSL connection: server:+port[:password] Both the port and password fields are optional; however, if you want to set a password or use SSL you must also set a port. If a port isn't specified it will default to your default-port setting. set default-port 6667 Set the default port which should be used if none is specified with '.jump' or in 'set servers'. set msg-rate 2 Number of seconds to wait between transmitting queued lines to the server. Lower this value at your own risk. ircd is known to start flood control at 512 bytes/2 seconds. set ssl-verify-servers 0 Control certificate verification for servers. You can set this by adding together the numbers for all exceptions you want to enable. By default certificate verification is disabled and all certificates are assumed to be valid. The numbers are the following: The numbers are the following: ---- --------------------------------------------- 0 disable verification 1 enable certificate verification 2 allow self-signed certificates 4 don't check peer common or alt names 8 allow expired certificates 16 allow certificates which are not valid yet 32 allow revoked certificates ---- --------------------------------------------- set server-cycle-wait 60 This setting defines how long Eggdrop should wait before moving from one server to another on disconnect. If you set 0 here, Eggdrop will not wait at all and will connect instantly. Setting this too low could result in your bot being K:Lined. set server-timeout 60 Set here how long Eggdrop should wait for a response when connecting to a server before giving up and moving on to next server. set check-stoned 1 Set this to 1 if Eggdrop should check for stoned servers? (where the server connection has died, but Eggdrop hasn't been notified yet). set serverror-quit 1 If you want your bot to exit the server if it receives an ERROR message, set this to 1. set max-queue-msg 300 Set here the maximum number of lines to queue to the server. If you're going to dump large chunks of text to people over IRC, you will probably want to raise this. 300 is fine for most people though. set quiet-reject 1 This setting makes the bot squelch the error message when rejecting a DCC CHAT, SEND or message command. Normally, Eggdrop notifies the user that the command has been rejected because they don't have access. Note that sometimes IRC server operators detect bots that way. set flood-msg 5:60 Set here how many msgs in how many seconds from one host constitutes a flood. If you set this to 0:0, msg flood protection will be disabled. set flood-ctcp 3:60 Set here how many ctcps in how many seconds from one host are recognized as a flood. Not specifying or using number = 0 will not check against ctcp floods. set answer-ctcp 3 Set how many ctcps should be answered at once here. set lowercase-ctcp 0 If you want your bot to answer lower case ctcp requests (non rfc-compliant), set this setting to 1. mIRC will do this, most other clients will not. set trigger-on-ignore 0 If you want Eggdrop to trigger binds for ignored users, set this to 1. set exclusive-binds 0 This setting configures PUBM and MSGM binds to be exclusive of PUB and MSG binds. This means if a MSGM bind with the mask "help" exists and is triggered, any MSG bindings with "help" in their mask will not be triggered. Don't enable this unless you know what you are doing! set double-mode 0 Allow identical messages in the mode queue? set double-server 0 Allow identical messages in the server queue? set double-help 0 Allow identical messages in the help queue? set use-penalties 1 (default on net-type IRCnet) This enables Eggdrop's penalty calculation. Every command Eggdrop sends to the IRC server raises its penalty points. If Eggdrop reaches a server limit, it gets disconnected with "excess flood" message. Eggdrop is able to count internal those penalty points, too and take measures against excess flooding. Note: it's highly advised to turn this on! set optimize-kicks 1 This optimizes the kick queue. It also traces nick changes and parts in the channel and changes the kick queue accordingly. There are three different options for this setting: --- ------------------------------------------------------------------ 0 Turn it off. 1 Optimize the kick queue by summarizing kicks. 2 Trace nick changes and parts on the channel and change the queue accordingly. For example, bot will not try to kick users who have already parted the channel. --- ------------------------------------------------------------------ ATTENTION: Setting 2 is very CPU intensive. There are additional settings for 'net-type' Efnet. net-type 5 specific features: Attention: Use this settings only if you set 'net-type' to Efnet! set check-mode-r 1 This settings defines how umode +r is understood by Eggdrop. Some networks use +r to indicate a restricted connection. If this is your case, and you want your bot to leave restricted servers and jump to the next server on its list, then set it to 1. Please note, this setting is automatically set to 0 for net-type of Efnet, Undernet, and DALnet, and set to 1 for net-type IRCNet. set nick-len 9 This setting allows you to specify the maximum nick-length supported by your network. The default setting is 9. The maximum supported length by Eggdrop is 32. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.share000066400000000000000000000034651524730042700213530ustar00rootroot00000000000000Last revised: November 27, 2003 Share Module This module provides userfile sharing support between two directly linked bots. This module requires: transfer, channels Put this line into your Eggdrop configuration file to load the share module: loadmodule share 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. set resync-time 900 This setting specifies how long to hold another bots resync data before flushing it. This is a setting for allow-resync. set private-global 0 When sharing user lists, DON'T ACCEPT global flag changes from other bots? NOTE: The bot will still send changes made on the bot, it just won't accept any global flag changes from other bots. This overrides the private-globals setting (below). set private-globals "mnot" When sharing user lists, if private-global isn't set, which global flag changes from other bots should be ignored? set private-user 0 When sharing user lists, don't accept ANY userfile changes from other bots? Paranoid people should use this feature on their hub bot. This will force all userlist changes to be made via the hub. set override-bots 0 This setting makes the bot discard its own bot records in favor of the ones sent by the hub. NOTE: No passwords or botflags are shared, only ports and address are added to sharing procedure. This only works with hubs that are v1.5.1 or higher. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.transfer000066400000000000000000000021601524730042700220640ustar00rootroot00000000000000Last revised: November 29, 2023 Transfer Module The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing. This module requires: none Put this line into your Eggdrop configuration file to load the transfer module: loadmodule transfer There are also some variables you can set in your config file: set max-dloads 3 Set here the maximum number of simultaneous downloads to allow for each user. set dcc-block 0 Set here the block size for dcc transfers. ircII uses 512 bytes, but admits that may be too small. 1024 is standard these days. Set this to 0 to use turbo-dcc (recommended). set xfer-timeout 30 Set here the time (in seconds) to wait before an inactive transfer times out. set sharefail-unlink 1 By default, Eggdrop will abort the linking process if userfile sharing is enabled but the userfile transfer fails. Set this to 0 to keep the bots linked if the userfile transfer fails and retry every minute (both bots must be v1.9.0 or higher). Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.twitch000066400000000000000000000043111524730042700215420ustar00rootroot00000000000000Last revised: April 20, 2020 Twitch Module This module attempts to provide connectivity with the Twitch gaming platform. While Twitch provides an IRC gateway to connect with it's streaming service, it does not claim to (and certainly does not) follow the IRC RFC in any meaningful way. The intent of this module is not to provide the full spectrum of management functions typically associated with Eggdrop; instead it focuses around the following key functions: - Logging of general and Twitch-specific events (raids, blocks, bit donations) - Tracking userstate and roomstate values - Adding Tcl event binds for a number of Twitch events This module requires: irc.mod Put this line into your Eggdrop configuration file to load the twitch module: loadmodule twitch and set net-type "Twitch" in your config file. LIMITATIONS There are a few things you should know about how Twitch provides service through the IRC gateway that affects how well Eggdrop can function: * Twitch does not broadcast JOINs or PARTs for channels over 1,000 users. This renders tracking users on a channel unreliable. * Twitch does not broadcast MODE changes for moderator status. This (mostly) renders tracking the status of users infeasible. (See Tcl below section for workaround) * Twitch stores bans on its servers (and does not accept MODE +b), making the Eggdrop ban list (and exempts/invites) mostly useless * Twitch does not allow clients to issue MODE +o/-o commands, preventing Eggdrop from op'ing users through the traditional method ... but the good news is, we have extended much of the Twitch functionality to Tcl! TCL API That last section was a little bit of a downer, but don't worry, we added a TON of functionality to the Tcl API. This module adds binds for the following Twitch events: - CLEARCHAT - CLEARMSG - HOSTTARGET - WHISPER - ROOMSTATE - USERSTATE - USERNOTICE PARTYLINE COMMANDS This module adds the following commands to the partyline: * userstate - Lists current userstate on a channel * roomsstate - Lists current roomstate for a channel * twcmd - Issues a traditional Twitch web interface command to the Twitch server (/ban, /block, /host, etc) Copyright (C) 2020 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.uptime000066400000000000000000000015161524730042700215470ustar00rootroot00000000000000Last revised: Mar 14, 2023 Uptime Module This module reports uptime statistics to the uptime contest web site at https://www.eggheads.org/uptime. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn't listed, try again later. Information sent to the server includes the bot's uptime, botnet-nick, server, version, and IP address. This information is stored in a temporary logfile for debugging purposes only. The only publicly available information will be the bot's botnet-nick, version and uptime. If you do not wish for this information to be sent, comment out the 'loadmodule uptime' line in your bot's config file. This module requires: server Put this line into your Eggdrop configuration file to load the uptime module: loadmodule uptime Copyright (C) 2001 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/mod.woobie000066400000000000000000000005451524730042700215310ustar00rootroot00000000000000Last revised: December 31, 2001 Woobie Module This is for demonstrative purposes only. If you are looking for starting point in writing modules, woobie is the right thing. This module requires: none Put this line into your Eggdrop configuration file to load the woobie module: loadmodule woobie Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/modules/writing-modules000066400000000000000000000227221524730042700226210ustar00rootroot00000000000000How to Write an Eggdrop Module Note: This is for a simple module of 1 source file. 1. Create a src/mod/MODULE.mod directory in your Eggdrop directory (where MODULE is the module name) and cd to it. 2. Copy the file 'Makefile' from src/mod/woobie.mod and replace all occurrences of 'woobie' with your module name. This should ensure that your module gets compiled. 3. Next, you want to create a file called MODULE.c (MODULE is the module name again). 4. You MUST include the following in your source code: #define MODULE_NAME "module-name" This should be defined to the same name you will be using when you load your module. #define MAKING_MODULENAME MODULENAME is the name of your module (MODULE_NAME), but in all caps. #include "../module.h" This provides access to Eggdrop's global function table. Examine src/mod/module.h closely to find a list of functions available. #include any other standard c header files you might need. Note that stdio.h, string.h, stdlib.h, and sys/types.h are already included. Function *global; This variable provides access to all the Eggdrop functions; without it, you can't call any Eggdrop functions (the module won't even load). MODULE REQUIREMENTS In most modules, all functions/variables (except global and MODULE_start) should be static. This will drastically reduce the size of modules on decent systems. Throughout this step, MODULE refers to the module name. Note that "MODULE_NAME" should literally be "MODULE_NAME". MODULE_start char *MODULE_start(Function *func_table) This function is called when the module is first loaded. There are several things that need to be done in this function global = func_table; This allows you to make calls to the global function table. module_register(MODULE_NAME, MODULE_table, MAJOR, MINOR); This records details about the module for other modules and Eggdrop itself to access. MAJOR and MINOR are ints, where MAJOR is the module's major version number and MINOR is a minor version number. MODULE_table is a function table (see below). module_depend(MODULE_NAME, "another-module", MAJOR, MINOR); This lets Eggdrop know that your module NEEDS "another-module" of major version 'MAJOR' and at least minor version 'MINOR' to run, and hence should try to load it if it's not already loaded. This will return 1 on success, or 0 if it can't be done (at which stage you should return an error). Any other initialization stuff you desire should also be included in this function. See below for various things you can do. You also will need to return a value. Returning NULL implies the module loaded successfully. Returning a non-NULL STRING is an error message. The module (and any other dependent modules) will stop loading and an error will be returned. MODULE_table static Function *MODULE_table = { MODULE_start, MODULE_close, MODULE_expmem, MODULE_report, any_other_functions, you_want_to_export }; This is a table of functions which any other module can access. The first 4 functions are FIXED. You MUST have them; they provide important module information. MODULE_close () static char *MODULE_close () This is called when the module is unloaded. Apart from tidying any relevant data (I suggest you be thorough, we don't want any trailing garbage from modules), you MUST do the following: module_undepend(MODULE_NAME); This lets Eggdrop know your module no longer depends on any other modules. Return a value. NULL implies success; any non-NULL STRING implies that the module cannot be unloaded for some reason, and hence the bot should not unload it (see the blowfish module for an example). MODULE_expmem static int MODULE_expmem () This should tally all memory you allocate/deallocate within the module (using nmalloc, nfree, etc) in bytes. It's used by memory debugging to track memory faults, and it is used by .status to total up memory usage. MODULE_report static void MODULE_report (int idx) This should provide a relatively short report of the module's status (for the module and status commands). These functions are available to modules. MANY more available functions can be found in src/mod/module.h. Additional functions nmalloc void *nmalloc(int j); This allocates j bytes of memory. nfree void nfree(void *a); This frees an nmalloc'd block of memory. Context Context; Actually a macro -- records the current position in execution (for debugging). Using Context is no longer recommended, because it uses too many resources and a core file provides much more information. dprintf void dprintf(int idx, char *format, ...) This acts like a normal printf() function, but it outputs to log/socket/idx. idx is a normal dcc idx, or if < 0 is a sock number. Other destinations: DP_LOG - send to log file DP_STDOUT - send to stdout DP_MODE - send via mode queue to the server DP_SERVER - send via normal queue to the server DP_HELP - send via help queue to server module_entry const module_entry *module_find(char *module_name, int major, int minor); Searches for a loaded module (matching major, >= minor), and returns info about it. Members of module_entry: char *name; - module name int major; - real major version int minor; - real minor version Function *funcs; - function table (see above) module_rename void module_rename(char *old_module_name, char *new_module_name) This renames a module from old_module_name to new_module_name. add_hook/del_hook void add_hook(int hook_num, Function *funcs) void del_hook(int hook_num, Function *funcs) These are used for adding or removing hooks to/from Eggdrop code that are triggered on various events. Valid hooks are: HOOK_SECONDLY - called every second HOOK_MINUTELY - called every minute HOOK_5MINUTELY - called every 5 minutes HOOK_HOURLY - called every hour (hourly-updates minutes past) HOOK_DAILY - called when the logfiles are switched HOOK_READ_USERFILE - called when the userfile is read HOOK_USERFILE - called when the userfile is written HOOK_PRE_REHASH - called just before a rehash HOOK_REHASH - called just after a rehash HOOK_IDLE - called whenever the dcc connections have been idle for a whole second HOOK_BACKUP - called when a user/channel file backup is done HOOK_LOADED - called when Eggdrop is first loaded HOOK_DIE - called when Eggdrop is about to die module_load/unload char *module_unload (char *module_name); char *module_load (char *module_name); Tries to load or unload the specified module; returns 0 on success, or an error message. add/rem_tcl_commands void add_tcl_commands(tcl_cmds *tab); void rem_tcl_commands(tcl_cmds *tab); Provides a quick way to create and remove a table of Tcl commands. The table is in the form of: {char *func_name, Function *function_to_call} Use { NULL, NULL } to indicate the end of the list. add/rem_tcl_ints void add_tcl_ints(tcl_ints *); void rem_tcl_ints(tcl_ints *); Provides a quick way to create and remove a table of links from C int variables to Tcl variables (add_tcl_ints checks to see if the Tcl variable exists and copies it over the C one). The format of table is: {char *variable_name, int *variable, int readonly} Use {NULL, NULL, 0} to indicate the end of the list. add/rem_tcl_strings void add_tcl_strings(tcl_strings *); void rem_tcl_strings(tcl_strings *); Provides a quick way to create and remove a table of links from C string variables to Tcl variables (add_tcl_ints checks to see if the Tcl variable exists and copies it over the C one). The format of table is: {char *variable_name, char *string, int length, int flags} Use {NULL, NULL, 0, 0} to indicate the end of the list. Use 0 for length if you want a const string. Use STR_DIR for flags if you want a '/' constantly appended; use STR_PROTECT if you want the variable set in the config file, but not during normal usage. add/rem_builtins void add_builtins(p_tcl_hash_list table, cmd_t *cc); void rem_builtins(p_tcl_hash_list table, cmd_t *cc); This adds binds to one of Eggdrop's bind tables. The format of the table is: {char *command, char *flags, Function *function, char *displayname} Use {NULL, NULL, NULL, NULL} to indicate the end of the list. This works EXACTLY like the Tcl 'bind' command. displayname is what Tcl sees this function's proc name as (in .binds all). function is called with exactly the same args as a Tcl binding is with type conversion taken into account (e.g. idx's are ints). Return values are much the same as Tcl bindings. Use int 0/1 for those which require 0/1, or char * for those which require a string (auch as filt). Return nothing if no return value is required. putlog void putlog (int logmode, char *channel, char *format, ...) Adds text to a logfile (determined by logmode and channel). This text will also output to any users' consoles if they have the specified console mode enabled. WHAT TO DO WITH A MODULE? If you have written a module and feel that you wish to share it with the rest of the Eggdrop community, find us in #eggdrop on Libera. Make sure you have a nice descriptive text (modulename.desc) to describe it, and make sure to mention in your text file which version Eggdrop the module is written for. eggheads-eggdrop-9c00fe1/doc/settings/000077500000000000000000000000001524730042700177305ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/settings/CONTENTS000066400000000000000000000032241524730042700211110ustar00rootroot00000000000000Directory Contents - doc/settings/ Last revised: April 16, 2003 _____________________________________________________________________ Directory Contents - doc/settings core.settings All module-independent Eggdrop settings are documented here. mod.assoc Documentation for the assoc module is provided in this file. mod.blowfish Documentation for the blowfish module is provided in this file. mod.channels Documentation for the channels module is provided in this file. mod.compress Documentation for the compress module is provided in this file. mod.console Documentation for the console module is provided in this file. mod.ctcp Documentation for the ctcp module is provided in this file. mod.dns Documentation for the dns module is provided in this file. mod.filesys Documentation for the filesys module is provided in this file. mod.irc Documentation for the irc module is provided in this file. mod.notes Documentation for the notes module is provided in this file. mod.seen Documentation for the seen module is provided in this file. mod.server Documentation for the server module is provided in this file. mod.share Documentation for the share module is provided in this file. mod.transfer Documentation for the transfer module is provided in this file. mod.uptime Documentation for the uptime module is provided in this file. mod.woobie Documentation for the woobie module is provided in this file. ________________________________________________________________________ Copyright (C) 2003 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/core.settings000066400000000000000000000606351524730042700224540ustar00rootroot00000000000000Last revised: October 25, 2010 Eggdrop Core Settings This file describes the syntax and all the settings of your Eggdrop configuration file. Please note that you don't need to set all of these variables to make your bot work properly. EXECUTABLE PATH The first line in an Eggdrop configuration file should contain a fully qualified path to your Eggdrop executable. It has to be implemented in the way the example shows to make the config file work as a shell script. Example: #! /path/to/eggdrop BASIC SETTINGS You can change the basic Eggdrop appearance and behavior in this section. set username "lamest" This setting defines the username the bot uses on IRC. This setting has no effect if an ident daemon is running on your bot's machine. set admin "Lamer " This setting defines which contact person should be shown in .status, /msg help, and other places. You really should include this information. set network "SomeIrcNetwork" This setting is used only for info to share with others on your botnet. Set this to the IRC network your bot is connected to. set timezone "EST" This setting defines which timezone is your bot in. It's used for internal routines as well as for logfile timestamping and scripting purposes. The timezone string specifies the name of the timezone and must be three or more alphabetic characters. For example, Central European Time(UTC+1) should be "CET". set offset "5" The offset setting specifies the time value to be added to the local time to get Coordinated Universal Time (UTC aka GMT). The offset is positive if the local timezone is west of the Prime Meridian and negative if it is east. The value (in hours) must be between -23 and 23. For example, if the timezone is UTC+1, the offset is -1. set env(TZ) "$timezone $offset" (disabled by default) If you don't want to use the timezone setting for scripting purposes only, but instead everywhere possible, then use this setting. set vhost4 "99.99.0.0" set vhost4 "virtual.host.com"   If you're using virtual hosting (your machine has more than 1 IP), you may want to specify the particular IP to bind to. You can specify either by hostname or by IP. Note that this is not used for listening. Use the 'listen' command to specify the listening address. set vhost6 "2001:db8:618:5c0:263::" set vhost6 "my.ipv6.host.com"   IPv6 vhost to bind to for outgoing IPv6 connections. You can set it to any valid IPv6 address or hostname, resolving to an IPv6 address. Note that this is not used for listening. Use the 'listen' command to specify the listening address. set prefer-ipv6 "1" Prefer IPv6 over IPv4 for connections and dns resolution. If the preferred protocol family is not supported, other possible families will be tried. addlang "english" If you want to have your Eggdrop messages displayed in another language, change this command to match your preferences. An alternative would be to set the environment variable EGG_LANG to that value. Languages included with Eggdrop: Danish, English, French, Finnish, German. LOG FILES Eggdrop is capable of logging various things, from channel chatter to partyline commands and file transfers. Logfiles are normally kept for 24 hours. Afterwards, they will be renamed to "(logfilename).yesterday". After 48 hours, they will be overwritten by the logfile of the next day. set max-logs 20 This is the maximum number of concurrent logfiles that can be opened for writing at one time. At most, this value should be the maximum number of channels you expect to create log files for. There is no value for 'infinity'; very few cases should ever require more than 20. A decrease to this value while running will require a restart (not rehash) of the bot. However, don't decrease it below 5. set max-logsize 0 This is the maximum size of your logfiles. Set it to 0 to disable. This value is in kilobytes, so '550' would mean cycle logs when it reaches the size of 550 kilobytes. Note that this only works if you have keep-all-logs set to 0 (OFF). set quick-logs 0 This could be good if you have had a problem with logfiles filling your quota/hard disk or if you log +p and publish it to the web, and you need more up-to-date info. Note that this setting might increase the CPU usage of your bot (on the other hand it will decrease your RAM usage). set raw-log 0 This setting allows you the logging of raw incoming server traffic via console/log flag 'r', raw outgoing server traffic via console/log mode 'v', raw incoming botnet traffic via console/log mode 't', raw outgoing botnet traffic via console/log mode 'u', raw outgoing share traffic via console/log mode 'g', and raw incoming share traffic via console/log mode 'h'. These flags can create a large security hole, allowing people to see user passwords. This is now restricted to +n users only. Please choose your owners with care. logfile "logs/logfile" This setting tells the bot what should be logged, from where, and to which file. Logfile flags: --- ------------------------------------------------------ b information about bot linking and userfile sharing c commands d misc debug information g raw outgoing share traffic h raw incoming share traffic j joins, parts, quits, and netsplits on the channel k kicks, bans, and mode changes on the channel l linked bot messages m private msgs, notices and ctcps to the bot o misc info, errors, etc (IMPORTANT STUFF) p public text on the channel r raw incoming server traffic s server connects, disconnects, and notices t raw incoming botnet traffic u raw outgoing botnet traffic v raw outgoing server traffic w wallops (make sure the bot sets +w in init-server) x file transfers and file-area commands --- ------------------------------------------------------ Note that modes d, h, r, t, and v can fill disk quotas quickly. There are also eight user-defined levels (1-8) which can be used by Tcl scripts. Each logfile belongs to a certain channel. Events of type 'k', 'j', and 'p' are logged to whatever channel they happened on. Most other events are currently logged to every channel. You can make a logfile belong to all channels by assigning it to channel "*". Examples: logfile mco * "logs/eggdrop.log" logfile jpk #lamest "logs/lamest.log" In 'eggdrop.log', put private msgs/ctcps, commands, misc info, and errors from any channel. In 'lamest.log', log all joins, parts, kicks, bans, public chatter, and mode changes from #lamest. set log-time 1 Use this feature to timestamp entries in the log file. set timestamp-format "[%H:%M:%S]" Set the following to the timestamp for the logfile entries. Popular times might be "[%H:%M]" (hour, min), or "[%H:%M:%S]" (hour, min, sec). Read 'man strftime' for more formatting options. Keep it below 32 chars. set keep-all-logs 0 If you want to keep your logfiles forever, turn this setting on. All logfiles will get the suffix ".[day, 2 digits][month, 3 letters][year, 4 digits]". Note that your quota/hard-disk might be filled by this, so check your logfiles often and download them. set switch-logfiles-at 300 You can specify when Eggdrop should switch logfiles and start fresh. use military time for this setting. 300 is the default, and describes 03:00 (AM). set quiet-save 0 "Writing user file..." and "Writing channel file..." messages won't be logged anymore if this option is enabled. If you set it to 2, the "Backing up user file..." and "Backing up channel file..." messages will also not be logged. In addition to this, you can disable the "Switching logfiles..." and the new date message at midnight, by setting this to 3. set logfile-suffix ".%d%b%Y" If keep-all-logs is 1, this setting will define the suffix of the logfiles. The default will result in a suffix like "04May2000". "%Y%m%d" will produce the often used yyyymmdd format. Read the strftime manpages for more options. NOTE: On systems which don't support strftime, the default format will always be used. CONSOLE SETTINGS set console "mkcoblxs" This is the default console mode. It uses the same event flags as the log files do. The console channel is automatically set to your "primary" channel, which is set in the modules section of the config file. Masters can change their console channel and modes with the '.console' command. FILE AND DIRECTORY SETTINGS set userfile "LamestBot.user" Specify here the filename your userfile should be saved as. set pidfile "pid.LamestBot" Specify here the filename Eggdrop will save its pid to. If no pidfile is specified, pid.(botnet-nick) will be used. set help-path "help/" Specify here where Eggdrop should look for help files. Don't modify this setting unless you know what you're doing! set text-path "text/" Specify here where Eggdrop should look for text files. This is used for certain Tcl and DCC commands. set motd "text/motd" The MOTD (Message Of The day) is displayed when people dcc chat or telnet to the bot. Look at doc/TEXT-SUBSTITUTIONS for options. set telnet-banner "text/banner" This banner will be displayed on telnet connections. Look at doc/text-substitutions.doc for options. set userfile-perm 0600 This specifies what permissions the user, channel, and notes files should be set to. The octal values are the same as for the chmod system command. To remind you: | u g o u g o u g o |0600 rw------- 0400 r-------- 0200 -w------- u - user |0660 rw-rw---- 0440 r--r----- 0220 -w--w---- g - group |0666 rw-rw-rw- 0444 r--r--r-- 0222 -w--w--w- o - others Note that the default 0600 is the most secure one and should only be changed if you need your files for shell scripting or other external applications. set mod-path "modules/" 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 :). BOTNET/DCC/TELNET SETTINGS Settings in this section should be unimportant for you until you deal with botnets (multiple Eggdrops connected together to maximize efficiency). You should read doc/BOTNET before modifying these settings. set botnet-nick "LlamaBot" (disabled by default) If you want to use a different nickname on the botnet than you use on IRC (i.e. if you're on an un-trusted botnet), un-comment this line and set it to the nick you would like to use. listen This opens a telnet port by which you and other bots can interact with the Eggdrop by telneting in. There are more options for the listen command in doc/tcl-commands.doc. Note that if you are running more than one bot on the same machine, you will want to space the telnet ports at LEAST 5 apart, although 10 is even better. Valid ports are typically anything between 1025 and 65535 assuming the port is not already in use. If you would like the bot to listen for users and bots in separate ports, use the following format: listen 3333 bots listen 4444 users If you wish to use only one port, use this format: listen 3333 all You can setup a SSL port by prepending a plus sign to it: listen +5555 all You need to un-comment this line and change the port number in order to open the listen port. You should not keep this set to 3333. set remote-boots 2 This setting defines whether or not people can boot users on the Eggdrop from other bots in your botnet. Valid settings are: --- ---------------------------- 0 allow no outside boots 1 allow boots from sharebots 2 allow any boots --- ---------------------------- set share-unlinks 1 This setting prohibits Eggdrop to unlink from share bots if an remote bots tells so. set protect-telnet 0 This setting will drop telnet connections not matching a known host. set dcc-sanitycheck 0 This setting will make the bot ignore DCC chat requests which appear to have bogus information on the grounds that the user may have been trying to make the bot connect to somewhere that will get it into trouble, or that the user has a broken client, in which case the connect wouldn't work anyway. set ident-timeout 5 This setting defines the time in seconds the bot should wait for ident reply before the lookup fails. The default ident on timeout is 'telnet'. set require-p 0 Define here whether or not a +o user still needs the +p flag to dcc the bot. set open-telnets 0 If you want people allow to telnet in and type 'NEW' to become a new user, set this to 1. This is similar to the 'hello' msg command. The protect-telnet setting must be set to 0 to use this. set stealth-telnets 0 If you don't want Eggdrop to identify itself as an Eggdrop on a telnet connection, set this setting to 1. Eggdrop will display 'Nickname' instead. set use-telnet-banner 0 If you want Eggdrop to display a banner when telneting in, set this setting to 1. The telnet banner is set by 'set telnet-banner'. set connect-timeout 15 This setting defines a time in seconds that the bot should wait before a dcc chat, telnet, or relay connection times out. set dcc-flood-thr 3 Specify here the number of lines to accept from a user on the partyline within 1 second before they are considered to be flooding and therefore get booted. set telnet-flood 5:60 Define here how many telnet connection attempts in how many seconds from the same host constitute a flood. The correct format is Attempts:Seconds. set paranoid-telnet-flood 1 If you want telnet-flood to apply even to +f users, set this setting to 1. set resolve-timeout 15 Set here the amount of seconds before giving up on hostname/address lookup (you might want to increase this if you are on a slow network). ADVANCED SETTINGS set firewall "!sun-barr.ebay:3666" Set this to your socks host if your Eggdrop sits behind a firewall. If you use a Sun "telnet passthru" firewall, prefix the host with a "!". set nat-ip "127.0.0.1" If you have a NAT firewall (you box has an IP in one of the following ranges: 192.168.0.0-192.168.255.255, 172.16.0.0-172.31.255.255, 10.0.0.0-10.255.255.255 and your firewall transparently changes your address to a unique address for your box) or you have IP masquerading between you and the rest of the world, and /dcc chat, /ctcp chat or userfile sharing aren't working, enter your outside IP here. This IP is used for transfers only, and has nothing to do with the vhost4/6 or listen settings. You may still need to set them. set reserved-portrange 2010:2020 If you want all dcc file transfers to use a particular portrange either because you're behind a firewall, or for other security reasons, set it here. set ignore-time 15 Set the time in minutes that temporary ignores should last. set hourly-updates 00 Define here what Eggdrop considers 'hourly'. All calls to it, including such things as note notifying or userfile saving, are affected by this. Example: set hourly-updates 15 The bot will save its userfile 15 minutes past every hour. set owner "MrLame, MrsLame" Un-comment this line and set the list of owners of the bot. You NEED to change this setting. set notify-newusers "$owner" Who should a note be sent to when new users are learned? set default-flags "hp" Enter the flags that all new users should get by default. See '.help whois' on the partyline for a list of flags and their descriptions. set whois-fields "url birthday" Enter all user-defined fields that should be displayed in a '.whois'. This will only be shown if the user has one of these extra fields. You might prefer to comment this out and use the userinfo1.0.tcl script which provides commands for changing all of these. #unbind dcc n tcl *dcc:tcl #unbind dcc n set *dcc:set Uncomment these two lines if you wish to disable the .tcl and .set commands. If you select your owners wisely, you should be okay leaving these enabled. set must-be-owner 1 If you enable this setting, only permanent owners (owner setting) will be able to use .tcl and .set. Moreover, if you want to only let permanent owners use .dump, then set this to 2. unbind dcc n simul *dcc:simul Comment out this line to add the 'simul' partyline command (owners can manipulate other people on the party line). Please select owners wisely and use this command ethically! set max-dcc 50 Set here the maximum number of dcc connections you will allow. You can increase this later, but never decrease it. set allow-dk-cmds 1 Enable this setting if you want +d & +k users to use commands bound as -|-. set dupwait-timeout 5 If your Eggdrop rejects bots that actually have already disconnected from another hub, but the disconnect information has not yet spread over the botnet due to lag, use this setting. The bot will wait dupwait-timeout seconds before it checks again and then finally reject the bot. set cidr-support 0 Enables cidr support for b/e/I modes if set to 1. This means the bot will understand and match modes in cidr notation, and will be able to put and enforce such bans or unban itself, if banned with a cidr mask. Do NOT set this, if your network/server does not support cidr! SSL SETTINGS Settings in this section take effect when eggdrop is compiled with TLS support. set ssl-privatekey "eggdrop.key" File containing your private key, needed for the SSL certificate (see below). You can create one issuing the following command: openssl genrsa -out eggdrop.key 4096 It will create a 4096 bit RSA key, strong enough for eggdrop. This is required for SSL hubs/listen ports, secure file transfer and /ctcp botnick schat For your convenience, you can type 'make sslcert' after 'make install' and you'll get a key and a certificate in your DEST directory. set ssl-certificate "eggdrop.crt" Specify the filename where your SSL certificate is located. If you don't set this, eggdrop will not be able to act as a server in SSL connections, as with most ciphers a certificate and a private key are required on the server side. Must be in PEM format. If you don't have one, you can create it using the following command: openssl req -new -key eggdrop.key -x509 -out eggdrop.crt -days 365 This is required for SSL hubs/listen ports, secure file transfer and /ctcp botnick schat For your convenience, you can type 'make sslcert' after 'make install' and you'll get a key and a certificate in your DEST directory. set ssl-verify-depth 9 Sets the maximum depth for the certificate chain verification that shall be allowed for ssl. When certificate verification is enabled, any chain exceeding this depth will fail verification. set ssl-capath "/etc/ssl/" set ssl-cafile ""   Specify the location at which CA certificates for verification purposes are located. These certificates are trusted. If you don't set this, certificate verification will not work. set ssl-ciphers "" Specify the list of ciphers (in order of preference) allowed for use with ssl. The cipher list is one or more cipher strings separated by colons, commas or spaces. Unavailable ciphers are silently ignored unless no usable cipher could be found. For the list of possible cipher strings and their meanings, please refer to the ciphers(1) manual. Note: if you set this, the value replaces any ciphers OpenSSL might use by default. To include the default ciphers, you can put DEFAULT as a cipher string in the list. For example: set ssl-ciphers "DEFAULT ADH" ... will make eggdrop allow the default OpenSSL selection plus anonymous DH ciphers. set ssl-ciphers "ALL" ... will make eggdrop allow all ciphers supported by OpenSSL, in a reasonable order. set ssl-cert-auth 0 Enable certificate authorization. Set to 1 to allow users and bots to identify automatically by their certificate fingerprints. Setting it to 2 to will force fingerprint logins. With a value of 2, users without a fingerprint set or with a certificate UID not matching their handle won't be allowed to login on SSL enabled telnet ports. Fingerprints must be set in advance with the .fprint and .chfinger commands. NOTE: this setting has no effect on plain-text ports. You can control SSL certificate verification using the following variables. All of them are flag-based. You can set them by adding together the numbers for all exceptions you want to enable. By default certificate verification is disabled and all certificates are assumed to be valid. The options are the following: ---- --------------------------------------------- 0 disable verification 1 enable certificate verification 2 allow self-signed certificates 4 don't check peer common or alt names 8 allow expired certificates 16 allow certificates which are not valid yet 32 allow revoked certificates ---- --------------------------------------------- set ssl-verify-dcc 0 Control certificate verification for DCC chats (only /dcc chat botnick) set ssl-verify-bots 0 Control certificate verification for linking to hubs set ssl-verify-clients 0 Control certificate verification for SSL listening ports. This includes leaf bots connecting, users telneting in and /ctcp bot chat. MODULES After the core settings, you should start loading modules. Modules are loaded by the command "loadmodule ". Eggdrop looks for modules in the directory you specified by the module-path setting in the files and directories section. Please note that for different configurations, different modules are needed. Four examples: Channel Security Bot: This bot needs the channels, blowfish, console, dns, irc, and (if you like) ctcp modules loaded. More is not needed and makes the bot slower. Public IRC Bot: A public bot should have all modules available loaded since they provide all functions for everyday use. Secure Filesys Bot: This bot needs all normal IRC operating modules, but not the notes, seen, ctcp or share modules. Limbo Bot: A limbo bot (serves as a botnet hub outside IRC) just needs the channels, console, dns, and maybe notes or share modules loaded. Of course, blowfish needs to be loaded here, too. SCRIPTS The scripts section should be placed at the end of the config file. All modules should be loaded and their variables should be set at this point. source scripts/script.tcl This line loads script.tcl from the scripts directory inside your Eggdrop's directory. All scripts should be put there, although you can place them where you like as long as you can supply a fully qualified path to them. Some commonly loaded scripts are alltools.tcl and action.fix.tcl. The appropriate source lines are: source scripts/alltools.tcl source scripts/action.fix.tcl Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.assoc000066400000000000000000000004521524730042700215420ustar00rootroot00000000000000Last revised: January 1, 2002 Assoc Module This module provides assoc support, i.e. naming channels on the botnet. This module requires: none Put this line into your Eggdrop configuration file to load the assoc module: loadmodule assoc Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.blowfish000066400000000000000000000010231524730042700222420ustar00rootroot00000000000000Last revised: January 28, 2003 Blowfish Module Eggdrop can encrypt your userfile, so users can have secure passwords. Please note that when you change your encryption method later (i.e. using other modules like a md5 module), you can't use your current userfile anymore. Eggdrop will not start without an encryption module. This module requires: none Put this line into your Eggdrop configuration file to load the blowfish module: loadmodule blowfish Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.channels000066400000000000000000000407551524730042700222370ustar00rootroot00000000000000Last revised: October 25, 2010 Channels Module This module provides channel related support for the bot. Without it, you won't be able to make the bot join a channel or save channel specific userfile information. This module requires: none Put this line into your Eggdrop configuration file to load the channels module: loadmodule channels There are also some variables you can set in your config file: set chanfile "LamestBot.chan" Enter here the filename where dynamic channel settings are stored. set force-expire 0 Set this setting to 1 if you want your bot to expire bans/exempts/invites set by other opped bots on the channel. set share-greet 0 Set this setting to 1 if you want your bot to share user greets with other bots on the channel if sharing user data. set use-info 1 Set this setting to 1 if you want to allow users to store an info line. set allow-ps 0 Set this setting to 1 if you want to allow both +p and +s channel modes to be enforced at the same time by the chanmode channel setting. Make sure your server supports +ps mixing or you may get endless mode floods. channel add #channel { SETTINGS } Add each static channel you want your bot to sit in using this command. There are many different possible settings you can insert into this command, which are explained below. chanmode +/- This setting makes the bot enforce channel modes. It will always add the + and remove the - modes. idle-kick 0 This setting will make the bot check every minute for idle users. Set this to 0 to disable idle check. stopnethack-mode 0 This setting will make the bot de-op anyone who enters the channel with serverops. There are seven different modes for this settings: +---+--------------------------------------------------------+ | 0 | turn off | +---+--------------------------------------------------------+ | 1 | isoptest (allow serverop if registered op) | +---+--------------------------------------------------------+ | 2 | wasoptest (allow serverop if user had op before split) | +---+--------------------------------------------------------+ | 3 | allow serverop if isop or wasop | +---+--------------------------------------------------------+ | 4 | allow serverop if isop and wasop. | +---+--------------------------------------------------------+ | 5 | If the channel is -bitch, see stopnethack-mode 3 | | | | | | --- | | | -----------------------------------------------------+ | | | | | | If the channel is +bitch, see stopnethack-mode 1 | | - | | | | | | | | | | | +---+--------------------------------------------------------+ | 6 | If the channel is -bitch, see stopnethack-mode 2 | | | | | | --- | | | -----------------------------------------------------+ | | | | | | If the channel is +bitch, see stopnethack-mode 4 | | - | | | | | | | | | | | +---+--------------------------------------------------------+ revenge-mode 0 This settings defines how the bot should punish bad users when revenging. There are four possible settings: --- ------------------------------------------------------------------- 0 Deop the user. 1 Deop the user and give them the +d flag for the channel. 2 Deop the user, give them the +d flag for the channel, and kick them. 3 Deop the user, give them the +d flag for the channel, kick, and ban them. --- ------------------------------------------------------------------- ban-type 3 This setting defines what type of bans should eggdrop place for +k users or when revenge-mode is 3. Available types are: --- ------------------------ 0 *!user@host 1 *!*user@host 2 *!*@host 3 *!*user@*.host 4 !*@.host 5 n ick!user@host 6 n ick!*user@host 7 n ick!*@host 8 n ick!*user@*.host 9 n ick!*@*.host --- ------------------------ You can also specify types from 10 to 19 which correspond to types 0 to 9, but instead of using a * wildcard to replace portions of the host, only numbers in hostnames are replaced with the '?' wildcard. Same is valid for types 20-29, but instead of '?', the '' wildcard will be used. Types 30-39 set the host to ''. ban-time 120 Set here how long temporary bans will last (in minutes). If you set this setting to 0, the bot will never remove them. exempt-time 60 Set here how long temporary exempts will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the exempts every X minutes, but will not remove the exempt if a ban is set on the channel that matches that exempt. Once the ban is removed, then the exempt will be removed the next time the bot checks. Please note that this is an IRCnet feature. invite-time 60 Set here how long temporary invites will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the invites every X minutes, but will not remove the invite if a channel is set to +i. Once the channel is -i then the invite will be removed the next time the bot checks. Please note that this is an IRCnet feature. aop-delay (minimum:maximum) This is used for autoop, autohalfop, autovoice. If an op or voice joins a channel while another op or voice is pending, the bot will attempt to put both modes on one line. -------------- ----------------------------------------- aop-delay 0 No delay is used. aop-delay X An X second delay is used. aop-delay X:Y A random delay between X and Y is used. -------------- ----------------------------------------- need-op { putserv "PRIVMSG #lamest :op me cos i'm lame!" } This setting will make the bot run the script enclosed in braces if it does not have ops. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-invite { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs an invite to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-key { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs the key to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-unban { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs to be unbanned on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-limit { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs the limit to be raised on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. flood-chan 15:60 Set here how many channel messages in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables text flood protection for the channel, where X is an integer >= 0. flood-deop 3:10 Set here how many deops in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables deop flood protection for the channel, where X is an integer >= 0. flood-kick 3:10 Set here how many kicks in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables kick flood protection for the channel, where X is an integer >= 0. flood-join 5:60 Set here how many joins in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables join flood protection for the channel, where X is an integer >= 0. flood-ctcp 3:60 Set here how many channel ctcps in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables ctcp flood protection for the channel, where X is an integer >= 0. flood-nick 5:60 Set here how many nick changes in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables nick flood protection for the channel, where X is an integer >= 0. channel set +/- There are many different options for channels which you can define. They can be enabled or disabled by a plus or minus in front of them. A complete list of all available channel settings: enforcebans When a ban is set, kick people who are on the channel and match the ban? dynamicbans Only activate bans on the channel when necessary? This keeps the channel's ban list from getting excessively long. The bot still remembers every ban, but it only activates a ban on the channel when it sees someone join who matches that ban. userbans Allow bans to be made by users directly? If turned off, the bot will require all bans to be made through the bot's console. dynamicexempts Only activate exempts on the channel when necessary? This keeps the channel's exempt list from getting excessively long. The bot still remembers every exempt, but it only activates a exempt on the channel when it sees a ban set that matches the exempt. The exempt remains active on the channel for as long as the ban is still active. userexempts Allow exempts to be made by users directly? If turned off, the bot will require all exempts to be made through the bot's console. dynamicinvites Only activate invites on the channel when necessary? This keeps the channel's invite list from getting excessively long. The bot still remembers every invite, but the invites are only activated when the channel is set to invite only and a user joins after requesting an invite. Once set, the invite remains until the channel goes to -i. userinvites Allow invites to be made by users directly? If turned off, the bot will require all invites to be made through the bot's console. autoop Op users with the +o flag as soon as they join the channel? This is insecure and not recommended. autohalfop Halfop users with the +l flag as soon as they join the channel? This is insecure and not recommended. bitch Only let users with the +o flag have op on the channel? greet Say a user's info line when they join the channel? protectops Re-op a user with the +o flag if they get deopped? protecthalfops Re-halfop a user with the +l flag if they get dehalfopped? protectfriends Re-op a user with the +f flag if they get deopped? statuslog Log the channel status line every 5 minutes? This shows the bot's status on the channel (op, voice, etc.), the channel's modes, and the total number of members, ops, voices, regular users, and +b, +e, and +I modes on the channel. A sample status line follows: [01:40] @#lamest (+istn) : [m/1 o/1 v/4 n/7 b/1 e/5 I/7] revenge Remember people who deop/kick/ban the bot, valid ops, or friends and punish them? Users with the +f flag are exempt from revenge. revengebot This is similar to to the 'revenge' option, but it only triggers if a bot gets deopped, kicked or banned. autovoice Voice users with the +v flag when they join the channel? secret Prevent this channel from being listed on the botnet? shared Share channel-related user info for this channel? cycle Cycle the channel when it has no ops? dontkickops Do you want the bot not to be able to kick users who have the +o flag, letting them kick-flood for instance to protect the channel against clone attacks? inactive This prevents the bot from joining the channel (or makes it leave the channel if it is already there). It can be useful to make the bot leave a channel without losing its settings, channel-specific user flags, channel bans, and without affecting sharing. seen Respond to seen requests in the channel? The seen module must be loaded for this to work. nodesynch Allow non-ops to perform channel modes? This can stop the bot from fighting with services such as ChanServ, or from kicking IRCops when setting channel modes without having ops. static Allow only permanent owners to remove the channel? The following settings are used as default values when you .+chan #chan or .tcl channel add #chan. Look in the section above for explanation of every option. set default-flood-chan 15:60 set default-flood-deop 3:10 set default-flood-kick 3:10 set default-flood-join 5:60 set default-flood-ctcp 3:60 set default-flood-nick 5:60 set default-aop-delay 5:30 set default-idle-kick 0 set default-chanmode "nt" set default-stopnethack-mode 0 set default-revenge-mode 0 set default-ban-type 3 set default-ban-time 120 set default-exempt-time 60 set default-invite-time 60 set default-chanset { -autoop -autovoice -bitch +cycle +dontkickops +dynamicbans +dynamicexempts +dynamicinvites -enforcebans +greet -inactive -nodesynch -protectfriends +protectops -revenge -revengebot -secret -seen +shared -statuslog +userbans +userexempts +userinvites -protecthalfops -autohalfop -static } Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.compress000066400000000000000000000014031524730042700222620ustar00rootroot00000000000000Last revised: May 27, 2004 Compress Module This module provides support for file compression. It can be used to compress files via Tcl or to transfer the userfile compressed during the share process, saving bandwidth. This module requires: share Put this line into your Eggdrop configuration file to load the compress module: loadmodule compress There are also some variables you can set in your config file: set share-compressed 1 Allow compressed sending of user files? The user files are compressed with the compression level defined in 'compress-level'. set compress-level 9 This is the default compression level used. These levels are the same as those used by GNU gzip. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.console000066400000000000000000000014451524730042700220770ustar00rootroot00000000000000Last revised: January 1, 2002 Console Module This module provides storage of console settings when you exit the bot or type .store on the partyline. This module requires: none Put this line into your Eggdrop configuration file to load the console module: loadmodule console There are also some variables you can set in your config file: set console-autosave 1 Save users console settings automatically? Otherwise, they have to use the .store command. set force-channel 0 If a user doesn't have any console settings saved, which channel do you want them automatically put on? set info-party 0 Enable this setting if a user's global info line should be displayed when they join a botnet channel. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.ctcp000066400000000000000000000021731524730042700213650ustar00rootroot00000000000000Last revised: February 12, 2002 CTCP Module This module provides the normal ctcp replies that you would expect. Without it loaded, CTCP CHAT will not work. This module requires: server Put this line into your Eggdrop configuration file to load the ctcp module: loadmodule ctcp There are also some variables you can set in your config file: set ctcp-mode 0 Set here how the ctcp module should answer ctcps. There are 3 possible operating modes: --- -------------------------------------------------------------- 0 Normal behavior is used. 1 The bot ignores all ctcps, except for CHAT and PING requests by users with the +o flag. 2 Normal behavior is used, however the bot will not answer more than X ctcps in Y seconds (defined by 'set flood-ctcp'). --- -------------------------------------------------------------- There are also several variables to help make your bot less noticeable. They are: ctcp-version, ctcp-finger, and ctcp-userinfo. You can use set to set them to values you'd like. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.dns000066400000000000000000000027771524730042700212320ustar00rootroot00000000000000Last revised: September 26, 2010 DNS Module This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections. This module requires: none Put this line into your Eggdrop configuration file to load the dns module: loadmodule dns There are also some variables you can set in your config file: set dns-servers "8.8.8.8 8.8.4.4" In case your bot has trouble finding dns servers or you want to use specific ones, you can set them here. The value is a list of dns servers. The relative order doesn't matter. You can also specify a non-standard port. The default is to use the system specified dns servers. You don't need to modify this normally. set dns-cache 86400 Specify how long should the DNS module cache replies at maximum. The value must be in seconds. Note that it will respect the TTL of the reply and this is just an upper boundary. set dns-negcache 600 Specify how long should the DNS module cache negative replies (NXDOMAIN, DNS Lookup failed). The value must be in seconds. set dns-maxsends 4 How many times should the DNS module resend the query for a given domain if it receives no reply? set dns-retrydelay 3 Specify how long should the DNS module wait for a reply before resending the query. The value must be in seconds. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.filesys000066400000000000000000000141461524730042700221150ustar00rootroot00000000000000Last revised: Dec 30, 2017 Filesys Module This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. This module requires: transfer CONFIG FILE SETUP Put this line into your Eggdrop configuration file to load the filesys module: loadmodule filesys There are also some variables you can set in your config file: set files-path "/home/mydir/eggdrop/filesys" Set here the 'root' directory for the file system. set incoming-path "/home/mydir/eggdrop/filesys/incoming" If you want to allow uploads, set this to the directory uploads should be put into. Set this to "" if you don't want people to upload files to your bot. set upload-to-pwd 0 If you don't want to have a central incoming directory, but instead want uploads to go to the current directory that a user is in, set this setting to 1. set filedb-path "" Eggdrop creates a '.filedb' file in each subdirectory of your file area to keep track of its own file system information. If you can't do that (for example, if the dcc path isn't owned by you, or you just don't want it to do that) specify a path here where you'd like all of the database files to be stored instead. set max-file-users 20 Set here the maximum number of people that can be in the file area at once. Setting this to 0 makes it effectively infinite. set max-filesize 1024 Set here the maximum allowable file size that will be received (in KB). Setting this to 0 makes it effectively infinite. PARTYLINE USAGE .files Moves you into the file transfer sub-system, if it has been enabled on this bot. From there you can browse through the files online and use dcc file transfers to download and upload. .cancel [file] ... Tells the bot to stop sending a file that is pending (either queued, waiting, or in the process of being transferred). .cd Changes your current directory if possible. this works exactly like the unix command. .cp Copies a file or group of files from one place to another. .desc Changes the description for a file. if you are a master or file janitor, you can change the description for any file. otherwise you can only change the descriptions for files you have uploaded. The description is restricted to 3 lines of 60 characters each, and is broken up between words. you can force a line break by putting a '|' in the comment. .filestats [clear] Reports on the users upload & download statistics. Optional argument 'clear' clears a users upload and download statistics. .stats Clears a users upload & download statistics. .get [nickname] Sends you the file(s) requested, over IRC. you should get a DCC SEND notice on IRC, and have your client accept it. if your nickname on IRC is different than the one you use on the bot, you should specify a nickname too. you can also use that to send files to other people. if a file you want is actually on another bot, it may take a little bit longer so be patient. if that bot isn't on the botnet right now, it will say the file isn't available. There is a limit to the number of transfers you can have going simultaneously, so if you go over this limit, the remainder of your file requests will be queued. As the first files finish transferring, the queued files will be sent. .hide [files] ... Marks a file as hidden, so that normal users can't see it. Only a master or file janitor using %b'lsa'%b can see hidden files. .ln Creates a link to a file on another bot. The filepath has to be complete, like '/gifs/uglyman.gif'. If the bot is not connected to the botnet, nobody will be able to download the file until that bot connects again. The local filename can be anything you want. Example: ln Snowbot:/gifs/uglyman.gif ugly.gif .ls [filemask] Displays the files in the current directory. Subdirectories are shown with "" next to them, and other files will display their size (typically in kilobytes), who uploaded them (and when), and how many times each file has been downloaded. If a description of the file exists, it is displayed below the filename. You can restrict the file listing by specifying a mask, just like in unix. .mkdir [flags [channel]] Creates a subdirectory from this one, with the given name. If flags are specified, then those flags are required to enter or even see the directory. You can even specify a channel that the flags are matched against. You can use the %b'mkdir'%b command again to alter or remove those flags. .mv Moves a file or group of files from one place to another (it can also be used to rename files). .pending Gives you a listing of every file you've requested which is still waiting, queued, or in the process of transferring. It shows you the nickname on IRC that the file is being sent to, and, if the transfer is in progress, tells you how far along the transfer is. .pwd Tells you what your current directory is. .quit Exits the file system. rm [files] ... Erase a file for good. .rmdir Removes an existing directory, if there are no files in it. .share [files] ... Marks a file as shared. This means that other bots can get the file remotely for users on their file systems. By default, files are marked as unshared. .optimize Cleans up the current directory's database. If you have a large directory with many files you may want to use this command if you experience slow-downs/delays over time. Normally, the db should clean up itself though. .unhide Makes a file be not hidden any more. .unshare [file] ... Removes the shared tag from a file. .filesys module This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. The following commands are provided by the filesys module: For filesystem users: files Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.ident000066400000000000000000000064021524730042700215360ustar00rootroot00000000000000Last revised: June 27, 2019 Ident Module This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon. Traditionally, an IRC server queries port 113 on an irc client's host to determine the ident for the client. The client's host normally replies with the username of the account. If the host machine is running oident as its ident service, Eggdrop can now interact with oident to instead reply with a custom ident specified by Eggdrop (oidentd is an external service not specific to Eggdrop nor maintained by Eggheads developers, thus no support for it can be given). Alternatively, this module can also allow Eggdrop to act as its own ident daemon, answering queries from the IRC server directly. While this option may be of use to a very specific group of people, it requires additional external configuration via root access. Because most modern OSs do not allow user-level programs to bind to port 113, required by this option, a user must either: - Allow Eggdrop to bind to ports <1024 via the command: sudo setcap "cap_net_bind_service=+ep" /path/to/eggdrop Warning: this will allow Eggdrop to any port below 1024, potentially allowing a user to impersonate a standard system service with Eggdrop - Use iptables to re-route ident queries destined for port 113 to a >1024 port that Eggdrop can bind to, for example by running: sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport \ 113 -j REDIRECT --to-port ' Additionally, using Eggdrop as a built-in ident daemon can cause issues when running multiple bots from the same account. The ident server is only activated and bound to the configured ident port while Eggdrop is connecting to an IRC server, but only one Eggdrop can bind to port 113 at a time. If multiple bots were to start at the same time, such as after a netsplit or host reboot, only one bot can bind to port 113 at a time, creating ident conflicts. Thus, this option is best suited for single-bot environments. This module requires: - oident to be running on your shell host, configured to allow user spoofing, for the oidentd method. Put this line into your Eggdrop configuration file to load the ident module: loadmodule ident There are also some variables you can set in your config file: set ident-method 0 This sets which ident method you wish to use: 0 = oidentd / Your bot will overwrite $HOME/.oidentd.conf right before opening the socket to the IRC server with the global reply. 1 = Builtin / Your bot will automatically turn its builtin identd on and off when needed so it shouldn't conflict with other identds that do the same. Ident port 113 is normally restricted to privileged processes on UNIX systems. set ident-port 113 This sets the port which port Eggdrop will attempt to bind to for option 1 of ident-method. Most OSs do not allow non-privileged programs such as Eggdrop to bind to ports <1024; please read the beginning of this document for potential ways to implement this setting. Copyright (C) 2019 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.irc000066400000000000000000000133241524730042700212110ustar00rootroot00000000000000Last revised: August 21, 2004 IRC Module This module controls the bots interaction on IRC. It allows the bot to join channels, etc. You have to load this if you want your bot to come on irc. This module requires: server, channels Put this line into your Eggdrop configuration file to load the irc module: loadmodule irc There are also some variables you can set in your config file: set bounce-bans 1 Set this to 1 if you want to bounce all server bans. set bounce-exempts 0 Set this to 1 if you want to bounce all server exemptions (+e modes). This is disabled if use-exempts is disabled. set bounce-invites 0 Set this to 1 if you want to bounce all server invitations (+I modes). This is disabled if use-invites is disabled. set bounce-modes 0 Set this to 1 if you want to bounce all server modes. set max-modes 30 There is a global limit for +b/+e/+I modes. This limit should be set to the same value as max-bans for networks that do not support +e/+I. set max-bans 30 Set here the maximum number of bans you want the bot to set on a channel. Eggdrop will not place any more bans if this limit is reached. Undernet currently allows 45 bans, IRCnet allows 30, EFnet allows 100, and DALnet allows 100. set max-exempts 20 Set here the maximum number of exempts you want Eggdrop to set on a channel. Eggdrop will not place any more exempts if this limit is reached. set max-invites 20 Set here the maximum number of invites you want Eggdrop to set on a channel. Eggdrop will not place any more invites if this limit is reached. set use-exempts 0 set use-invites 0 These settings should be left commented unless the default values are being overridden. By default, exempts and invites are on for EFnet and IRCnet, but off for all other large networks. This behavior can be modified with the following 2 flags. If your network doesn't support +e/+I modes then you will be unable to use these features. set learn-users 0 If you want people to be able to add themselves to the bot's userlist with the default userflags (defined above in the config file) via the 'hello' msg command, set this to 1. set wait-split 600 Set here the time (in seconds) to wait for someone to return from a netsplit (i.e. wasop will expire afterwards). Set this to 1500 on IRCnet since its nick delay stops after 30 minutes. set wait-info 180 Set here the time (in seconds) that someone must have been off-channel before re-displaying their info line. set mode-buf-length 200 Set this to the maximum number of bytes to send in the arguments of modes sent to the server. Most servers default this to 200. unbind msg - hello *msg:hello bind msg - myword *msg:hello Many IRCops find bots by seeing if they reply to 'hello' in a msg. You can change this to another word by un-commenting these two lines and changing "myword" to the word wish to use instead of'hello'. It must be a single word. unbind msg - ident *msg:ident unbind msg - addhost *msg:addhost Many takeover attempts occur due to lame users blindly /msg ident'ing to the bot and attempting to guess passwords. We now unbind this command by default to discourage them. You can enable this command by un-commenting these two lines. set opchars "@" #set opchars "@&~" Some IRC servers are using some non-standard op-like channel prefixes/modes. Define them here so the bot can recognize them. Just "@" should be fine for most networks. Un-comment the second line for some UnrealIRCds. set no-chanrec-info 0 If you are so lame you want the bot to display peoples info lines, even when you are too lazy to add their chanrecs to a channel, set this to 1. NOTE This means every user with an info line will have their info line displayed on EVERY channel they join (provided they have been gone longer than wait-info). These were the core irc module settings. There are more settings for 'net-type' IRCnet and Efnet. net-type has to be set in the server module config section. Use the following settings only if you set 'net-type' to IRCnet! set prevent-mixing 1 At the moment, the current IRCnet IRCd version (2.10) doesn't support the mixing of b, o and v modes with e and I modes. This might be changed in the future, so use 1 at the moment for this setting. Use the following settings only if you set 'net-type' to Efnet! set kick-method 1 If your network supports more users per kick command then 1, you can change this behavior here. Set this to the number of users to kick at once, or set this to 0 for all at once. set modes-per-line 3 Some networks allow you to stack lots of channel modes into one line. They're all guaranteed to support at least 3, so that's the default. If you know your network supports more, you may want to adjust this. This setting is limited to 6, although if you want to use a higher value, you can modify this by changing the value of MODES_PER_LINE_MAX in src/chan.h and recompiling the bot. set include-lk 1 Some networks don't include the +l limit and +k or -k key modes in the modes-per-line (see above) limitation. Set include-lk to 0 for these networks. set use-354 0 Set this to 1 if your network uses IRCu2.10.01 specific /who requests. Eggdrop can, therefore, ask only for exactly what's needed. set rfc-compliant 1 If your network doesn't use rfc 1459 compliant string matching routines, set this to 0. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.notes000066400000000000000000000023161524730042700215630ustar00rootroot00000000000000Last revised: January 1, 2002 Notes Module This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval. This module requires: none Put this line into your Eggdrop configuration file to load the notes module: loadmodule notes There are also some variables you can set in your config file: set notefile "LamestBot.notes" Set here the filename where private notes between users are stored. set max-notes 50 Set here the maximum number of notes to allow to be stored for each user (to prevent flooding). set note-life 60 Set here how long (in days) to store notes before expiring them. set allow-fwd 0 Set this to 1 if you want to allow users to specify a forwarding address for forwarding notes to another account on another bot. set notify-users 1 Set this to 1 if you want the bot to let people know hourly if they have any notes. set notify-onjoin 1 Set this to 1 if you want the bot to let people know on join if they have any notes. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.seen000066400000000000000000000010601524730042700213600ustar00rootroot00000000000000Last revised: Mar 14, 2023 Seen Module This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot's userlist. If you are looking for a better and more advanced seen module, try the gseen module, originally by G'Quann and forked/maintained by mortmann. You can find it at https://github.com/michaelortmann/gseen.mod. This module requires: none Put this line into your Eggdrop configuration file to load the seen module: loadmodule seen Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.server000066400000000000000000000213111524730042700217350ustar00rootroot00000000000000Last revised: October 25, 2010 Server Module This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define. This module requires: none Put this line into your Eggdrop configuration file to load the server module: loadmodule server There are also some variables you can set in your config file: set net-type Efnet What is your network? Possible allowed values are Efnet, IRCnet, Undernet, DALnet, Libera, freenode, Quakenet, Rizon, Other. If the network you use is not listed, using "Other" is a good sane choice and can be customized with settings both here and in the IRC module sections of the config file. set nick "LamestBot" Set the nick the bot uses on IRC, and on the botnet unless you specify a separate botnet-nick, here. set altnick "L?m?stB?t" Set the alternative nick which the bot uses on IRC if the nick specified by 'set nick' is unavailable. All '?' characters will be replaced by random numbers. set keep-nick 1 This setting makes the bot try to get his original nickname back if its primary nickname is already in use. set realname "/msg LamestBot hello" Set here what to display in the real-name field for the bot. bind evnt - init-server evnt:init_server proc evnt:init_server {type} { global botnick putquick "MODE $botnick +i-ws" } ... is Tcl scripting that is run immediately after connecting to a server. set init-server { putquick "MODE $botnick +i-ws" } ... is Tcl scripting that is run immediately after connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). set connect-server { putlog "Connecting to server." } ... is Tcl scripting that is run immediately before connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). set disconnect-server { putlog "Disconnected from server." } ... is a Tcl script that is run immediately after disconnecting from a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). set servers { you.need.to.change.this:6667 another.example.com:7000:password [2001:db8:618:5c0:263::]:6669:password ssl.example.net:+6697 } ... is the bot's server list. The bot will start at the first server listed, and cycle through them whenever it gets disconnected. You need to change these servers to YOUR network's servers. The format is: server[:port[:password]] Prefix the port with a plus sign to attempt a SSL connection: server:+port[:password] Both the port and password fields are optional; however, if you want to set a password or use SSL you must also set a port. If a port isn't specified it will default to your default-port setting. set default-port 6667 Set the default port which should be used if none is specified with '.jump' or in 'set servers'. set msg-rate 2 Number of seconds to wait between transmitting queued lines to the server. Lower this value at your own risk. ircd is known to start flood control at 512 bytes/2 seconds. set ssl-verify-servers 0 Control certificate verification for servers. You can set this by adding together the numbers for all exceptions you want to enable. By default certificate verification is disabled and all certificates are assumed to be valid. The numbers are the following: The numbers are the following: ---- --------------------------------------------- 0 disable verification 1 enable certificate verification 2 allow self-signed certificates 4 don't check peer common or alt names 8 allow expired certificates 16 allow certificates which are not valid yet 32 allow revoked certificates ---- --------------------------------------------- set server-cycle-wait 60 This setting defines how long Eggdrop should wait before moving from one server to another on disconnect. If you set 0 here, Eggdrop will not wait at all and will connect instantly. Setting this too low could result in your bot being K:Lined. set server-timeout 60 Set here how long Eggdrop should wait for a response when connecting to a server before giving up and moving on to next server. set check-stoned 1 Set this to 1 if Eggdrop should check for stoned servers? (where the server connection has died, but Eggdrop hasn't been notified yet). set serverror-quit 1 If you want your bot to exit the server if it receives an ERROR message, set this to 1. set max-queue-msg 300 Set here the maximum number of lines to queue to the server. If you're going to dump large chunks of text to people over IRC, you will probably want to raise this. 300 is fine for most people though. set quiet-reject 1 This setting makes the bot squelch the error message when rejecting a DCC CHAT, SEND or message command. Normally, Eggdrop notifies the user that the command has been rejected because they don't have access. Note that sometimes IRC server operators detect bots that way. set flood-msg 5:60 Set here how many msgs in how many seconds from one host constitutes a flood. If you set this to 0:0, msg flood protection will be disabled. set flood-ctcp 3:60 Set here how many ctcps in how many seconds from one host are recognized as a flood. Not specifying or using number = 0 will not check against ctcp floods. set answer-ctcp 3 Set how many ctcps should be answered at once here. set lowercase-ctcp 0 If you want your bot to answer lower case ctcp requests (non rfc-compliant), set this setting to 1. mIRC will do this, most other clients will not. set trigger-on-ignore 0 If you want Eggdrop to trigger binds for ignored users, set this to 1. set exclusive-binds 0 This setting configures PUBM and MSGM binds to be exclusive of PUB and MSG binds. This means if a MSGM bind with the mask "help" exists and is triggered, any MSG bindings with "help" in their mask will not be triggered. Don't enable this unless you know what you are doing! set double-mode 0 Allow identical messages in the mode queue? set double-server 0 Allow identical messages in the server queue? set double-help 0 Allow identical messages in the help queue? set use-penalties 1 (default on net-type IRCnet) This enables Eggdrop's penalty calculation. Every command Eggdrop sends to the IRC server raises its penalty points. If Eggdrop reaches a server limit, it gets disconnected with "excess flood" message. Eggdrop is able to count internal those penalty points, too and take measures against excess flooding. Note: it's highly advised to turn this on! set optimize-kicks 1 This optimizes the kick queue. It also traces nick changes and parts in the channel and changes the kick queue accordingly. There are three different options for this setting: --- ------------------------------------------------------------------ 0 Turn it off. 1 Optimize the kick queue by summarizing kicks. 2 Trace nick changes and parts on the channel and change the queue accordingly. For example, bot will not try to kick users who have already parted the channel. --- ------------------------------------------------------------------ ATTENTION: Setting 2 is very CPU intensive. There are additional settings for 'net-type' Efnet. net-type 5 specific features: Attention: Use this settings only if you set 'net-type' to Efnet! set check-mode-r 1 This settings defines how umode +r is understood by Eggdrop. Some networks use +r to indicate a restricted connection. If this is your case, and you want your bot to leave restricted servers and jump to the next server on its list, then set it to 1. Please note, this setting is automatically set to 0 for net-type of Efnet, Undernet, and DALnet, and set to 1 for net-type IRCNet. set nick-len 9 This setting allows you to specify the maximum nick-length supported by your network. The default setting is 9. The maximum supported length by Eggdrop is 32. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.share000066400000000000000000000034651524730042700215430ustar00rootroot00000000000000Last revised: November 27, 2003 Share Module This module provides userfile sharing support between two directly linked bots. This module requires: transfer, channels Put this line into your Eggdrop configuration file to load the share module: loadmodule share 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. set resync-time 900 This setting specifies how long to hold another bots resync data before flushing it. This is a setting for allow-resync. set private-global 0 When sharing user lists, DON'T ACCEPT global flag changes from other bots? NOTE: The bot will still send changes made on the bot, it just won't accept any global flag changes from other bots. This overrides the private-globals setting (below). set private-globals "mnot" When sharing user lists, if private-global isn't set, which global flag changes from other bots should be ignored? set private-user 0 When sharing user lists, don't accept ANY userfile changes from other bots? Paranoid people should use this feature on their hub bot. This will force all userlist changes to be made via the hub. set override-bots 0 This setting makes the bot discard its own bot records in favor of the ones sent by the hub. NOTE: No passwords or botflags are shared, only ports and address are added to sharing procedure. This only works with hubs that are v1.5.1 or higher. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.transfer000066400000000000000000000026701524730042700222620ustar00rootroot00000000000000Last revised: January 1, 2002 Transfer Module The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing. This module requires: none Put this line into your Eggdrop configuration file to load the transfer module: loadmodule transfer There are also some variables you can set in your config file: set max-dloads 3 Set here the maximum number of simultaneous downloads to allow for each user. set dcc-block 0 Set here the block size for dcc transfers. ircII uses 512 bytes, but admits that may be too small. 1024 is standard these days. Set this to 0 to use turbo-dcc (recommended). set copy-to-tmp 1 Enable this setting if you want to copy files to a temporary location before sending or receiving them. This might be useful for file stability, but if your directories are NFS mounted, it's a pain. Setting this to 1 is not advised for big files or if you're low on disk space. set xfer-timeout 30 Set here the time (in seconds) to wait before an inactive transfer times out. set sharefail-unlink 1 By default, Eggdrop will abort the linking process if userfile sharing is enabled but the userfile transfer fails. Set this to 0 to keep the bots linked if the userfile transfer fails and retry every minute (both bots must be v1.9.0 or higher). Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.uptime000066400000000000000000000015171524730042700217400ustar00rootroot00000000000000Last revised: Mar 14, 2023 Uptime Module This module reports uptime statistics to the uptime contest web site at https://www.eggheads.org/uptime/. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn't listed, try again later. Information sent to the server includes the bot's uptime, botnet-nick, server, version, and IP address. This information is stored in a temporary logfile for debugging purposes only. The only publicly available information will be the bot's botnet-nick, version and uptime. If you do not wish for this information to be sent, comment out the 'loadmodule uptime' line in your bot's config file. This module requires: server Put this line into your Eggdrop configuration file to load the uptime module: loadmodule uptime Copyright (C) 2001 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/settings/mod.woobie000066400000000000000000000005451524730042700217210ustar00rootroot00000000000000Last revised: December 31, 2001 Woobie Module This is for demonstrative purposes only. If you are looking for starting point in writing modules, woobie is the right thing. This module requires: none Put this line into your Eggdrop configuration file to load the woobie module: loadmodule woobie Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/000077500000000000000000000000001524730042700207615ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/Makefile000066400000000000000000000152201524730042700224210ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = .. # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: @echo "Do not use this, remember what happened last time?" html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Eggdrop.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Eggdrop.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Eggdrop" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Eggdrop" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." eggheads-eggdrop-9c00fe1/doc/sphinx_source/_static/000077500000000000000000000000001524730042700224075ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/_static/eggdrop.css000066400000000000000000000016511524730042700245530ustar00rootroot00000000000000@import url("agogo.css"); div.header .headertitle { margin-bottom: 0px; font-family: "Palatino Linotype", serif; } div.header .headertitle a { color: #2F2F2F; } div.document { width: 85em; } div.header, div.content, div.footer { width: 97vw; /* unset to override agogo.css settings */ } div.content { display: flex; } div.sidebar .caption { font-weight: bold; color: #3465a4; } div.body .toctree-wrapper { display: none; font-size: 1px; } div.sidebar li.toctree-l1 a { background-color: initial; } div.footer, div.footer a { color: #034f84; } table { border-collapse: collapse; margin-bottom: 10px; margin-top: 10px; } th, td { padding-right: 4px; padding-left: 4px; border: 2px solid #87bdd8; } div.document li.toctree-l1 { margin-bottom: .5em; } div.document li.toctree-l1 a { color:black; } div.document ul { margin: 1.5em; list-style-type: circle; } div.cap eggheads-eggdrop-9c00fe1/doc/sphinx_source/_static/eggman.png.gif000066400000000000000000000105231524730042700251200ustar00rootroot00000000000000GIF89aR|U111{Μ !1rrrkckΥ9Zk޵{{kKR^J995' !}ވ}}ֵ1BRü)))9BƥsB{߄{cRB){ΥבֿZRZt ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  !U,R|U5' ST8 /Ļ ""ͬTՠU'ݔ:7 ."TnXAޮ hǰatfj@%"l\I"Bj|bɕ8X b?6 sc<7EZR2ʕJ dUO!c׳„FE͞*gB :7 ;p'DrJX΂+޸ NXB$'wMՀr 5LMm:pI"Z4-MS!Sdzf =  +P  \YϪn྿{@vKK?e;<Nd 0t c4PxXv=4C?B|5!,ԐpA3ȶgl%ZXyPPÁTD Ȁh @( Pny%|%&$=@֣^p90_!(PGUM`fY)*-@{y0nƉ($%S9?TBQ%]bMqE|Ed1o. !C*gBi'yLY(љϸEp {@g(FfaA-cabb&2]^WU$bG)ܣ* ,Rx ]S$hRMbJ 4e8A@#%tbDvQ@ZgBDL# X, `)@ &@ >RB 00H>Fb 0lo@ #Ȯm1%HBsJ*t@Pq2e# tL@.qZ` 0zRsu*[%lh l B=o \!Me ݍC)pXS;za)+. b p( DKH܀`" oǛ 2LjH&G̢}ϲ?\x*| mF]Yrѯ]G <^Ox#YB3>C3+5:$\|N:zo.$8,nP 28ڐ @0$!_QB?>'wv['oh"N\̀ނBN< |y뺃 D?U0`KŁӵ/h `6mh@ W Ȅ)27w\\Bh,P%'fq+`1Gyfl}vGs[P4X63@}#n%uQp`E%23m >n88\޶)$`B&! wCl&m``bg`+%  bJ NV1`dh&`u[_ͨ1Ӑ Sf7lRpZ';;eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/000077500000000000000000000000001524730042700224055ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/000077500000000000000000000000001524730042700235015ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/layout.html000066400000000000000000000063611524730042700257120ustar00rootroot00000000000000{# agogo/layout.html ~~~~~~~~~~~~~~~~~ Sphinx layout template for the agogo theme, originally written by Andi Albrecht. :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} {%- extends "basic/layout.html" %} {% block header %} {% endblock %} {%- macro agogo_sidebar() %} {%- block sidebartoc %}

{{ _('Table of Contents') }}

{{ toctree() }} {%- endblock %} {%- block sidebarsearch %}

{{ _('Search') }}

{%- endblock %} {% endmacro %} {% block content %}
{%- if not theme_rightsidebar|tobool %} {%- endif %}
{%- block document %} {{ super() }} {%- endblock %}
{%- if theme_rightsidebar|tobool %} {%- endif %}
{% endblock %} {% block footer %} {% endblock %} {% block relbar1 %}{% endblock %} {% block relbar2 %}{% endblock %} eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/static/000077500000000000000000000000001524730042700247705ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/static/agogo.css_t000066400000000000000000000205261524730042700271260ustar00rootroot00000000000000/* * agogo.css_t * ~~~~~~~~~~~ * * Sphinx stylesheet -- agogo theme. * * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ * { margin: 0px; padding: 0px; } body { font-family: {{ theme_bodyfont }}; line-height: 1.4em; color: black; background-color: {{ theme_bgcolor }}; } /* Page layout */ div.header, div.content, div.footer { width: {{ theme_pagewidth }}; margin-left: auto; margin-right: auto; } div.header-wrapper { background: {{ theme_headerbg }}; border-bottom: 3px solid #2e3436; } /* Default body styles */ a { color: {{ theme_linkcolor }}; } div.bodywrapper a, div.footer a { text-decoration: underline; } .clearer { clear: both; } .left { float: left; } .right { float: right; } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } h1, h2, h3, h4 { font-family: {{ theme_headerfont }}; font-weight: normal; color: {{ theme_headercolor2 }}; margin-bottom: .8em; } h1 { color: {{ theme_headercolor1 }}; } h2 { padding-bottom: .5em; border-bottom: 1px solid {{ theme_headercolor2 }}; } a.headerlink { visibility: hidden; color: #dddddd; padding-left: .3em; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink, caption:hover > a.headerlink, p.caption:hover > a.headerlink, div.code-block-caption:hover > a.headerlink { visibility: visible; } img { border: 0; } div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 2px 7px 1px 7px; border-left: 0.2em solid black; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } dt:target, .highlighted { background-color: #fbe54e; } /* Header */ div.header { padding-top: 10px; padding-bottom: 10px; } div.header .headertitle { font-family: {{ theme_headerfont }}; font-weight: normal; font-size: 180%; letter-spacing: .08em; margin-bottom: .8em; } div.header .headertitle a { color: white; } div.header div.rel { margin-top: 1em; } div.header div.rel a { color: {{ theme_headerlinkcolor }}; letter-spacing: .1em; text-transform: uppercase; } p.logo { float: right; } img.logo { border: 0; } /* Content */ div.content-wrapper { background-color: white; padding-top: 20px; padding-bottom: 20px; } div.document { width: {{ theme_documentwidth }}; float: left; } div.body { {%- if theme_rightsidebar|tobool %} padding-right: 2em; {%- else %} padding-left: 2em; {% endif %} text-align: {{ theme_textalign }}; } div.document h1 { line-height: 120%; } div.document ul { margin: 1.5em; list-style-type: square; } div.document dd { margin-left: 1.2em; margin-top: .4em; margin-bottom: 1em; } div.document .section { margin-top: 1.7em; } div.document .section:first-child { margin-top: 0px; } div.document div.highlight { padding: 3px; background-color: #eeeeec; border-top: 2px solid #dddddd; border-bottom: 2px solid #dddddd; margin-top: .8em; margin-bottom: .8em; } div.document div.literal-block-wrapper { margin-top: .8em; margin-bottom: .8em; } div.document div.literal-block-wrapper div.highlight { margin: 0; } div.document div.code-block-caption span.caption-number { padding: 0.1em 0.3em; font-style: italic; } div.document div.code-block-caption span.caption-text { } div.document h2 { margin-top: .7em; } div.document p { margin-bottom: .5em; } div.document li.toctree-l1 { margin-bottom: 1em; } div.document .descname { font-weight: bold; } div.document .sig-paren { font-size: larger; } div.document .docutils.literal { background-color: #eeeeec; padding: 1px; } div.document .docutils.xref.literal { background-color: transparent; padding: 0px; } div.document blockquote { margin: 1em; } div.document ol { margin: 1.5em; } /* Sidebar */ div.sidebar { width: {{ theme_sidebarwidth|todim }}; {%- if theme_rightsidebar|tobool %} float: right; {%- else %} float: left; {%- endif %} font-size: .9em; } div.sidebar a, div.header a { text-decoration: none; } div.sidebar a:hover, div.header a:hover { text-decoration: underline; } div.sidebar h3 { color: #2e3436; text-transform: uppercase; font-size: 130%; letter-spacing: .1em; } div.sidebar ul { list-style-type: none; } div.sidebar li.toctree-l1 a { display: block; padding: 1px; border: 1px solid #dddddd; background-color: #eeeeec; margin-bottom: .4em; padding-left: 3px; color: #2e3436; } div.sidebar li.toctree-l2 a { background-color: transparent; border: none; margin-left: 1em; border-bottom: 1px solid #dddddd; } div.sidebar li.toctree-l3 a { background-color: transparent; border: none; margin-left: 2em; border-bottom: 1px solid #dddddd; } div.sidebar li.toctree-l2:last-child a { border-bottom: none; } div.sidebar li.toctree-l1.current a { border-right: 5px solid {{ theme_headerlinkcolor }}; } div.sidebar li.toctree-l1.current li.toctree-l2 a { border-right: none; } div.sidebar input[type="text"] { width: 170px; } div.sidebar input[type="submit"] { width: 30px; } /* Footer */ div.footer-wrapper { background: {{ theme_footerbg }}; border-top: 4px solid #babdb6; padding-top: 10px; padding-bottom: 10px; min-height: 80px; } div.footer, div.footer a { color: #888a85; } div.footer .right { text-align: right; } div.footer .left { text-transform: uppercase; } /* Styles copied from basic theme */ img.align-left, .figure.align-left, object.align-left { clear: left; float: left; margin-right: 1em; } img.align-right, .figure.align-right, object.align-right { clear: right; float: right; margin-left: 1em; } img.align-center, .figure.align-center, object.align-center { display: block; margin-left: auto; margin-right: auto; } .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } table caption span.caption-number { font-style: italic; } table caption span.caption-text { } div.figure p.caption span.caption-number { font-style: italic; } div.figure p.caption span.caption-text { } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable td { text-align: left; vertical-align: top; } table.indextable ul { margin-top: 0; margin-bottom: 0; list-style-type: none; } table.indextable > tbody > tr > td > ul { padding-left: 0em; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } /* -- domain module index --------------------------------------------------- */ table.modindextable td { padding: 2px; border-collapse: collapse; } /* -- viewcode extension ---------------------------------------------------- */ .viewcode-link { float: right; } .viewcode-back { float: right; font-family:: {{ theme_bodyfont }}; } div.viewcode-block:target { margin: -1px -3px; padding: 0 3px; background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; } div.code-block-caption { background-color: #ddd; color: #333; padding: 2px 5px; font-size: small; } /* -- math display ---------------------------------------------------------- */ div.body div.math p { text-align: center; } span.eqno { float: right; } eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/static/bgfooter.png000066400000000000000000000006621524730042700273110ustar00rootroot00000000000000PNG  IHDRP2sRGBbKGDϴ Bb pHYs  tIME4\tEXtCommentCreated with GIMPW IDAT8m;%13jk1a >{1mC(_>Z7  zfH[v|BT:m882GK##>k׎9!ҾK8s(igISjl;a͇e]iw  "HVyb_ \i #l*喈j:hv0qePzD=ٸ@Bd&dt3ܰ|ǞSϳ6 亓pwF/O\҈k Hy3IENDB`eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/static/bgtop.png000066400000000000000000000006561524730042700266200ustar00rootroot00000000000000PNG  IHDRP2sRGBbKGDUWS!ÿ pHYs  tIME-tEXtCommentCreated with GIMPW IDAT8]ˎ@!C_d2mgAQy#|qI w X/`ᶮqdϏϪfH=C&H9M Cv"QV1T^ΜF#lvTت&j0PGyV!.6`HSsVfCxAƎ/]MyTږ9vne`cSLFUs%J'h v|m5ku|*l>*6D夻zdDX?IENDB`eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/agogo/theme.conf000066400000000000000000000007261524730042700254570ustar00rootroot00000000000000[theme] inherit = basic stylesheet = agogo.css pygments_style = tango [options] bodyfont = "Verdana", Arial, sans-serif headerfont = "Georgia", "Times New Roman", serif pagewidth = 90em documentwidth = 65em rightsidebar = false sidebarwidth = 18em bgcolor = #eeeeec headerbg = url(bgtop.png) top left repeat-x footerbg = url(bgfooter.png) top left repeat-x linkcolor = #ce5c00 headercolor1 = #204a87 headercolor2 = #3465a4 headerlinkcolor = #fcaf3e textalign = justify eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/eggdrop/000077500000000000000000000000001524730042700240345ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/eggdrop/layout.html000066400000000000000000000000431524730042700262340ustar00rootroot00000000000000{%- extends "agogo/layout.html" %} eggheads-eggdrop-9c00fe1/doc/sphinx_source/_themes/eggdrop/theme.conf000066400000000000000000000000611524730042700260020ustar00rootroot00000000000000[theme] inherit = agogo stylesheet = eggdrop.css eggheads-eggdrop-9c00fe1/doc/sphinx_source/about/000077500000000000000000000000001524730042700220735ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/about/about.rst000066400000000000000000000052771524730042700237520ustar00rootroot00000000000000About Eggdrop Last revised: July 27, 2010 ============= About Eggdrop ============= Eggdrop was created around December 1993 to help stop the incessant wars on #gayteen. It spawned from another bot that was in the process of being written at the time called "Unrest". The purpose of Unrest was to answer help requests from other bots. The first public release of Eggdrop was version 0.6, and since then, it has grown into what you have before you. Eggdrop is the World's most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms. An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc. One of the features that makes Eggdrop stand out from other bots is module and Tcl scripting support. With scripts and modules, you can make the bot perform almost any task you want. They can do anything from preventing floods to greeting users and banning advertisers from channels. You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently, and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban lists, exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet. Eggdrop is always being improved and adjusted because there are bugs to be fixed and features to be added (if the users demand them, and they make actually sense). In fact, it existed for several years as v0.7 - v0.9 before finally going 1.0. This version of Eggdrop is part of the 1.9 tree. A valiant effort has been made to chase down and destroy bugs. To use Eggdrop, you need: * Some sort of Unix account * A pretty good knowledge of IRC and Unix, including how to compile programs, how to read, and what DCC chat is, at absolute minimum. * About 5-7 MB of disk space. The Eggdrop tarball is about 5.4 MB unpacked. * Tcl -- Eggdrop cannot compile without Tcl installed on your shell. Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/about/legal.rst000066400000000000000000000031201524730042700237050ustar00rootroot00000000000000Boring legal stuff ================== The Eggdrop bot is Copyright (C) by Robey Pointer. As of January 1997, Eggdrop is distributed according to the GNU General Public License. There should be a copy of this license in the COPYING file. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As of Eggdrop 1.3.28, all changes made by the Eggheads Development Team to the Eggdrop source code and any related files are Copyright (C) by Eggheads Development Team. The source code will still be distributed according to the GNU General Public License as Robey Pointer did in the past. Releases previous to 1.0m were made using a different licensing scheme. You may, at your option, use the GNU General Public License on those versions (instead of the license packaged with them) with Robey's blessing. For any versions bearing a copyright date of 1997 or later, you have no choice - you must use the GNU General Public License. The files match.c, net.c and blowfish.c are exempt from the above restrictions. match.c is original code by Chris Fuller and has been placed by him into the public domain. net.c is by Robey has placed it in the public domain. blowfish.c is by various sources and is in the public domain as well. All 3 files contain useful functions that could easily be ported to other applications. Tcl is by John Ousterhout and is in no way affiliated with Eggdrop. It likely has its own set of copyrights and what-nots. There is no warranty, implied or whatever. You use this software at your own risk, no matter what purpose you put it to. eggheads-eggdrop-9c00fe1/doc/sphinx_source/conf.py000066400000000000000000000207411524730042700222640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Eggdrop documentation build configuration file, created by # sphinx-quickstart on Tue Jun 28 18:48:35 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx_substitution_extensions'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Eggdrop' copyright = u'2026, Eggheads' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.10.2' # The full version, including alpha/beta/rc tags. release = '1.10.2' # Just X.Y, for use in doc links (Geo) maj_version = ".".join(version.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'eggdrop' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = {'headerfont':'"Open Sans", sans serif', 'headerlinkcolor':'87bdd8', 'headerbg':'#86AAD3', 'linkcolor':'#336699'} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = '_static/eggman.png.gif' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = False # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_domain_indices = False # If false, no index is generated. html_use_index = False # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Eggdropdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'Eggdrop.tex', u'Eggdrop Documentation', u'Eggheads', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'eggdrop', u'Eggdrop Documentation', [u'Eggheads'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Eggdrop', u'Eggdrop Documentation', u'Eggheads', 'Eggdrop', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False rst_prolog = """ .. role:: raw-html(raw) :format: html .. |dlink| replace:: :raw-html:`the current version of Eggdrop` .. |majversion| replace:: %s .. |fullversion| replace:: %s """ % (maj_version, version, maj_version, version) eggheads-eggdrop-9c00fe1/doc/sphinx_source/docutils.conf000066400000000000000000000000631524730042700234550ustar00rootroot00000000000000[restructuredtext parser] syntax_highlight = short eggheads-eggdrop-9c00fe1/doc/sphinx_source/eggdrop.css000066400000000000000000000017171524730042700231300ustar00rootroot00000000000000@import url("agogo.css"); div.header .headertitle { margin-bottom: 0px; font-family: "Palatino Linotype", serif; } div.header .headertitle a { color: #2F2F2F; } div.document { width: 85em; } /* MMX's edit */ div.header, div.content, div.footer { width: unset; /* unset to override agogo.css settings */ } div.content { display: flex; } /* END of MMX's edit */ div.sidebar .caption { font-weight: bold; color: #3465a4; } div.body .toctree-wrapper { display: none; font-size: 1px; } div.sidebar li.toctree-l1 a { background-color: initial; } div.footer, div.footer a { color: #034f84; } table { border-collapse: collapse; margin-bottom: 10px; margin-top: 10px; } th, td { padding-right: 4px; padding-left: 4px; border: 2px solid #87bdd8; } div.document li.toctree-l1 { margin-bottom: .5em; } div.document li.toctree-l1 a { color:black; } div.document ul { margin: 1.5em; list-style-type: circle; } div.cap eggheads-eggdrop-9c00fe1/doc/sphinx_source/index.rst000066400000000000000000000056511524730042700226310ustar00rootroot00000000000000Eggdrop, an open source IRC bot =============================== Eggdrop is a free, open source software program built to assist in managing an IRC channel. It is the world's oldest actively-maintained IRC bot and was designed to be easily used and expanded on via it's ability to run Tcl scripts. Eggdrop can join IRC channels and perorm automated tasks such as protecting the channel from abuse, assisting users obtain their op/voice status, provide information and greetings, host games, etc. Some things you can do with Eggdrop ----------------------------------- Eggdrop has a large number of features, such as: * `Channel Management `_ * Running Tcl Scripts * `Integration of the most current IRCv3 capabilities `_ * `The ability to link multiple Eggdrops together and share userfiles `_ * `TLS Support `_ * `IPv6 Support `_ * `Twitch Support `_ * ... and much much more! How to get Eggdrop ------------------ The Eggdrop project source code is hosted at https://github.com/eggheads/eggdrop. You can clone it via git, or alternatively a copy of the current stable snapshot is located at https://geteggdrop.com. Additional information can be found on the official Eggdrop webpage at https://www.eggheads.org. For more information, see `Installing Eggdrop `_ How to install Eggdrop ---------------------- Installation Pre-requisites ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Eggdrop requires Tcl (and its development header files) to be present on the system it is compiled on. It is also strongly encouraged to install openssl (and its development header files) to enable TLS-protected network communication. Installation ^^^^^^^^^^^^ A guide to quickly installing Eggdrop can be found here. Where to find more help ----------------------- The Eggheads development team can be found lurking on #eggdrop on the Libera network (irc.libera.chat). .. toctree:: :caption: Installing Eggdrop :maxdepth: 2 install/readme install/install install/upgrading .. toctree:: :caption: Using Eggdrop :maxdepth: 2 using/features using/core using/partyline using/autoscripts using/users using/bans using/botnet using/ipv6 using/tls using/ircv3 using/accounts using/pbkdf2info using/python using/twitchinfo using/tricks using/text-sub using/tcl-commands using/twitch-tcl-commands using/patch .. toctree:: :caption: Tutorials :maxdepth: 2 tutorials/setup tutorials/firststeps tutorials/tlssetup tutorials/userfilesharing tutorials/firstscript tutorials/pythonscript tutorials/module .. toctree:: :caption: Eggdrop Modules :maxdepth: 2 modules/index modules/included modules/writing modules/internals.rst .. toctree:: :caption: About Eggdrop :maxdepth: 2 about/about about/legal eggheads-eggdrop-9c00fe1/doc/sphinx_source/install/000077500000000000000000000000001524730042700224275ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/install/install.rst000066400000000000000000000120701524730042700246270ustar00rootroot00000000000000.. _installing_eggdrop: ======================================= Installing Eggdrop ======================================= This is the quick install guide; if you have had little or no experience with UNIX or Eggdrop, READ THE README FILE NOW! This file is best for experienced users. For more information on compiling Eggdrop, see the Compile Guide in doc/COMPILE-GUIDE (and of course, the README FILE). Quick Startup ------------- Eggdrop uses the GNU autoconfigure scripts to make things easier. 1. Type './configure' from the Eggdrop directory. The configure script will determine how your system is set up and figure out how to correctly compile Eggdrop. It will also try to find Tcl, which is required to compile. 2. Type either 'make config' or 'make iconfig' to determine which modules will be compiled. 'make config' compiles the default modules (everything but woobie.mod). If you want to choose which modules to compile, use 'make iconfig'. 3. Type 'make' from the Eggdrop directory, or to force a statically linked module bot, type 'make static'. Otherwise, the Makefile will compile whatever type of bot the configure script determined your system will support. Dynamic is always the better way to go if possible. There are also the 'debug' and 'sdebug' (static-debug) options, which will give more detailed output on a (highly unlikely :) crash. This will help the development team track down the crash and fix the bug. Debug and sdebug will take a little longer to compile and will enlarge the binary a bit, but it's worth it if you want to support Eggdrop development. 4. Eggdrop must be installed in a directory somewhere. This is accomplished by entering the UNIX command:: make install This will install the Eggdrop in your home directory in a directory called 'eggdrop' (i.e. /home/user/eggdrop). If you want to install to a different directory, use:: make install DEST= For example:: make install DEST=/home/user/otherdir Note that you must use full path for every file to be correctly installed. 5. Since version 1.8, Eggdrop can use SSL to protect botnet links. If you intend on protecting botnet traffic between Eggdrops, you must generate SSL certificates by running:: make sslcert Or, if you installed your eggdrop to a different directory in step 4, you will want to run: make sslcert DEST= For those using scripts to install Eggdrop, you can non-interactively generate a key and certificate by running: make sslsilent Read docs/TLS for more info on this process. [The following steps are performed in the directory you just installed Eggdrop into from the previous step] 6. Edit your config file completely. 7. Start the bot with the "-m" option to create a user file, i.e. :: ./eggdrop -m LamestBot.conf 8. When starting the bot in the future, drop the "-m". If you have edited your config file correctly, you can type:: chmod u+x For example:: chmod u+x LamestBot.conf From then on, you will be able to use your config file as a shell script. You can just type "./LamestBot.conf" from your shell prompt to start up your bot. For this to work, the top line of your script MUST contain the correct path to the Eggdrop executable. 9. It's advisable to run your bot via crontab, so that it will automatically restart if the machine goes down or (heaven forbid) the bot should crash. Eggdrop includes a helper script to auto-generate either a systemd or crontab entry. To add a systemd job, run:: ./scripts/autobotchk [yourconfig.conf] -systemd or to add a crontab job, run:: ./scripts/autobotchk [yourconfig.conf] 10. Smile. You have an Eggdrop! Cygwin Requirements (Windows) ---------------------------------------- Eggdrop requires the following packages to be added from the Cygwin installation tool prior to compiling: :: Interpreters: tcl, tcl-devel Net: openssl, libssl-devel Devel: autoconf, gcc-core, git, make Libs: zlib-devel Modules ------- Modules are small pieces of code that can either be compiled into the binary or can be compiled separately into a file. This allows for a much smaller binary. If there are any modules that you have made or downloaded, you can add them to the bot by placing them in the /src/mod directory with a mod extension. They will be automatically compiled during make for you. They must have a valid Makefile and, of course, be compatible with the rest of the Eggdrop source. If you wish to add a module at a later time, follow the same steps in paragraph 2. After you have moved the appropriate files, you will only need to type 'make modules' to compile only the modules portion of the bot. This is the end. If you read to this point, hopefully you have also read the README file. If not, then READ IT!&@#%@! Have fun with Eggdrop! Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/install/readme.rst000066400000000000000000000236241524730042700244250ustar00rootroot00000000000000README ====== Please, at least SKIM this document before asking questions. In fact, READ IT if you've never successfully set up an Eggdrop bot before. Notice ------ Make SURE that you select your +n (owner) users wisely. They have 100% access to your bot and account. ONLY GIVE THIS POWER TO SOMEONE YOU TRUST COMPLETELY! What is Eggdrop? ---------------- Eggdrop is the world's most popular Internet Relay Chat (IRC) bot; it is freely distributable under the GNU General Public License (GPL). Eggdrop is a feature-rich program designed to be easily used and expanded upon by both novice and advanced IRC users on a variety of hardware and software platforms. An IRC bot is a program that sits on an IRC channel and performs automated tasks while looking just like a normal user on the channel. Some of these functions include protecting the channel from abuse, allowing privileged users to gain op or voice status, logging channel events, providing information, hosting games, etc. One of the features that makes Eggdrop stand out from other bots is module and Tcl and Python scripting support. With scripts and modules you can make the bot perform almost any task you want. They can do anything: from preventing floods to greeting users and banning advertisers from channels. You can also link multiple Eggdrop bots together to form a botnet. This can allow bots to op each other securely, control floods efficiently and even link channels across multiple IRC networks. It also allows the Eggdrops share user lists, ban/exempt/invite lists, and ignore lists with other bots if userfile sharing is enabled. This allows users to have the same access on every bot on your botnet. It also allows the bots to distribute tasks such as opping and banning users. See doc/BOTNET for information on setting up a botnet. Eggdrop is always being improved and adjusted because there are bugs to be fixed and features to be added (if the users demand them and they make actually sense). In fact, it existed for several years as v0.7 - v0.9 before finally going 1.0. This version of Eggdrop is part of the 1.9 tree. A valiant effort has been made to chase down and destroy bugs. This README file contains information about how to get Eggdrop, command line options for Eggdrop, what you may need to do when upgrading from older versions, a list of frequently asked questions, how to set up a crontab, some boring legal stuff, some basics about git usage and some channels where you might get help with Eggdrop. How to Get Eggdrop ------------------ There are two official methods to download Eggdrop source code. Alternately, Eggdrop also comes as a docker image. FTP ^^^ The latest Eggdrop stable source code is always located at ``_. You can also download the current stable, previous stable, and development snapshot at ``_ Git Development Snapshot ^^^^^^^^^^^^^^^^^^^^^^^^ Eggdrop developers use git to manage the Eggdrop codebase for development. If you are interested in trying out the VERY LATEST updates to Eggdrop, you can use git to obtain most recent code from the Eggheads repository. BE WARNED, the development branch of Eggdrop is not to be considered stable and may (haha) contain significant bugs still being worked on. To obtain Eggdrop via the git repository (hosted by GitHub), you can either clone the repository via git or download a development snapshot. To clone the repository, simply type:: git clone https://github.com/eggheads/eggdrop.git Otherwise, you can download the development snapshot as a tar archive from: ``_ Docker ^^^^^^ You can pull the official Eggdrop Docker image via:: docker pull eggdrop:latest Additional Eggdrop Docker documentation can be found at ``_ System Pre-Requisites --------------------- Before you can compile Eggdrop, Tcl must be installed on your system. Many systems have Tcl installed on them by default (you can check by trying the command "tclsh"; if you are given a '%' for a prompt, it is, and you can type 'exit' to exit the Tcl shell. However, Eggdrop also requires the Tcl development header files to be installed. They can often be installed via an OS package manager, usually called something similar to 'tcl-dev' for the package name. You can also download Tcl source from ``_. Eggdrop also requires openssl (and its development headers) in order to enable SSL/TLS protection of network data. The header files are often called something similar to 'libssl-dev'. While not advised, this requirement can be removed by compiling using ``./configure --disable-tls``, but you will not be able to connect to TLS-protected IRC servers nor utilize secure botnet communication. Minimum Requirements -------------------- Some components of Eggdrop relies on a variety of third-party libraries, documented here. +-------------------------------+-------------------+-------------------+ | Functionality | Package | Minimum Version | +===============================+===================+===================+ | Tcl interpreter (required) | Tcl Dev Library | 8.5.0 | +-------------------------------+-------------------+-------------------+ | Secure communication | OpenSSL | 0.9.8 | +-------------------------------+-------------------+-------------------+ | Python module | Python | 3.8.0 | +-------------------------------+-------------------+-------------------+ | Compression module | zlib | Any | +-------------------------------+-------------------+-------------------+ Quick Startup ------------- Please see the `Install `_ file after you finish reading this file. Upgrading --------- The upgrade process for Eggdrop is very simple, simply download the new source code and repeat the compile process. You will want to read the NEWS for any new configuration file settings you want to add. Please see `Upgrading `_ for full details. Command Line ------------ Eggdrop has some command line options - not many, because most things should be defined through the config file. However, sometimes you may want to start up the bot in a different mode and the command line options let you do that. Basically, the command line for Eggdrop is:: ./eggdrop [options] [config-file] The options available are: -t: Don't background, use terminal. Your console will drop into an interactive partyline session, similar to a DCC chat with the bot. This is useful for troubleshooting connection issues with the bot. -c: Don't background, show channel info. Every 10 seconds your screen will clear and you will see the current channel status, sort of like "top". -m: Create userfile. If you don't have a userfile, this will make Eggdrop create one and give owner status to the first person that introduces himself or herself to it. You'll need to do this when you first set up your bot. -h: Show help, then quit. -v: Show version info, then quit. Most people never use any of the options except -m and you usually only need to use that once. Auto-starting Eggdrop --------------------- Systems go down from time to time, taking your Eggdrop along with it. You may not be not around to restart it manually, so you can instead use features of the operating system to automatically restart Eggdrop should it quit for any reason. Eggdrop comes with an autobotchk shell script that can create either a systemd or crontab entry. The systemd option will monitor your Eggdrop and a) start it when the machine boots and b) restart the Eggdrop if it crashes for any reason. The (older) crontab option will check (by default) every 10 minutes to see if your Eggdrop is still running, and attempt to restart it if it is not. To auto-generate a systemd job, from the Eggdrop install directory, simply run:: ./scripts/autobotchk -systemd To auto-geneerate a script to check Eggdrop's status and run it via a crontab entry, simply run:: ./scripts/autobotchk This will crontab your bot using the default setup. If you want a list of autobotchk options, type './autobotchk'. A crontab example with options would be:: ./scripts/autobotchk -noemail -5 This would setup crontab to run the botchk every 5 minutes and not send you an email saying that it restarted your bot. Documentation ------------- We're trying to keep the documentation up to date. If you feel that anything is missing here or that anything should be added, etc, please create an issue, or better yet a pull request, at ``_ Thank you! Obtaining Help -------------- You can obtain help with Eggdrop in the following IRC channels: * Libera Chat - #eggdrop (official channel), #eggheads (development discussion) * DALnet - #eggdrop * EFnet - #egghelp * IRCnet - #eggdrop * QuakeNet - #eggdrop.support * Undernet - #eggdrop If you plan to ask questions in any of the above channels, you should be familiar with and follow IRC etiquette: * Don't type using CAPITAL letters, colors or bold. * Don't use "!" and "?" excessively. * Don't /msg people without their permission. * Don't repeat or paste more than 4 lines of text to the channel. * 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 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/install/upgrading.rst000066400000000000000000000066701524730042700251520ustar00rootroot00000000000000Upgrading Eggdrop ================= It is easy to upgrade Eggdrop to a new version! To have a full picture of the changes made since your last upgrade, we recommend reading the NEWS file. Upgrades from the 1.8 and 1.9 lines of Eggdrop should take place with little to no issues. While the config file, user files, and channel files can all be reused, it is recommended to review the NEWS files for each release to identify new configuration settings that have been added. For support, feel free to visit us on Libera #eggdrop. How to Upgrade -------------- To upgrade to a newer version of Eggdrop, first backup your .config file, .user file, and .chan file. While they are saved and should not be overwritten by the upgrade process, it never hurts to make anothere copy :) Next, view the NEWS file to see what changes have been made, paying particular attention to the config file section to see what new/different commands have been added. Update your config file appropriately. Then, simply follow the same steps you followed to install Eggdrop previously. Download the source, unzip/untar it, and run the './configure', 'make config', 'make', and 'make install' commands. Restart your Eggdrop and you will be up and running with the latest version of Eggdrop. Must-read changes for Eggdrop v1.10 ----------------------------------- These are NOT all the changes or new settings; rather just the "killer" changes that may directly affect Eggdrop's previous performance without modification. Config file changes ^^^^^^^^^^^^^^^^^^^ To migrate from a 1.8 to a |majversion| Eggdrop, some changes are suggested to be made in your configuration file: * Eggdrop has deprecated the blowfish module for password hashing in favor of the PBKDF2 module. This is a BIG change which, if done carelessly, has the potential to render stored passwords useless. Please see doc/PBKDF2 for information on how to properly migrate your userfiles and passwords to the new module. * Eggdrop 1.9 switched from the "set servers {}" syntax to the "server add" command. For example, if your configuration file previously had:: set servers { my.server.com:6667 } you should now instead use:: server add my.server.com 6667 Please read the config file for additional examples * Eggdrop no longer requires the '-n' flag to start Eggdrop in terminal mode. Modules ^^^^^^^ While most 3rd party modules that worked on older Eggdrop versions should still work with Eggdrop |majversion|, many of them contain a version check that only allows them to run on 1.6.x bots. We have removed the version check from some of the more popular modules provide them at ``_ Scripts ^^^^^^^ All 3rd party Tcl scripts that worked with Eggdrop versions as early as v1.6 should still fully work with Eggdrop |majversion|. Botnet ^^^^^^ In Eggdrop v1.8, Eggdrop bots would automatically attempt to upgrade any botnet link to an SSL/TLS connection. Since v1.9, the user is required to explicitly request an SSL/TLS connection by prefixing the port with a '+'. If you wish your botnet to take advantage of encryption, use the .chaddr command to update your ports to start with a '+'. Tcl Commands ^^^^^^^^^^^^ A lot of backwards-compatible additions and changes have been made to Tcl commands. Please look at doc/tcl-commands.doc to see them. Documentation ^^^^^^^^^^^^^ Documentation has been updated to reflect new and removed commands and variables. eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/000077500000000000000000000000001524730042700224315ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/included.rst000066400000000000000000000112641524730042700247560ustar00rootroot00000000000000Modules included with Eggdrop ============================= .. toctree:: mod/assoc mod/blowfish mod/channels mod/compress mod/console mod/ctcp mod/dns mod/filesys mod/ident mod/irc mod/notes mod/pbkdf2 mod/python mod/seen mod/server mod/share mod/transfer mod/twitch mod/woobie mod/uptime :ref:`assoc` This module provides assoc support, i.e. naming channels on the botnet. :ref:`blowfish` This module has been deprecated in favor of the pbkdf2 module for hashing purposes, such as passwords in the userfile. However, it is still required for encrypting/decrypting strings. :ref:`channels` This module provides channel related support for the bot. Without it, you won't be able to make the bot join a channel or save channel specific userfile information. :ref:`compress` This module provides support for file compression. This allows the bot to transfer compressed user files and, therefore, save a significant amount of bandwidth. :ref:`console` This module provides storage of console settings when you exit the bot or type .store on the partyline. :ref:`ctcp` This module provides the normal ctcp replies that you'd expect. Without it loaded, CTCP CHAT will not work. :ref:`dns` This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections. :ref:`filesys` This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. :ref:`ident` This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon. :ref:`irc` This module provides basic IRC support for your bot. You have to load this if you want your bot to come on IRC. :ref:`notes` This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval. :ref:`pbkdf2` This modules updates Eggdrop to use PBKDF2 for hashing purposes, such as for userfile passwords. It was specifically designed to work with the blowfish module to make the transition from blowfish to pbkdf2 password hashing as easy as possible. If you are transitioning a userfile from 1.8 or earlier, you should load this AND the blowfish module. By doing so, Eggdrop will seamlessly update the old blowfish hashes to the new PBKDF2 hashes once a user logs in for the first time, and allow you to (eventually) remove the blowfish module altogether. For new bots, you should load this module by itself and not use the blowfish module. The blowfish module is still required if you use Tcl to encrypt/decrypt strings in Tcl (ie, some scripts). Eggdrop will not start without a password-hashing module loaded. :ref:`python` This module adds a python interpreter to Eggdrop, enabling you to run python scripts on your Eggdrop! :ref:`seen` This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot's userlist. If you are looking for a better and more advanced seen module, try the gseen module, originally written by G'Quann and forked/updated for modern Eggdrop by mortmann. You can find it at ``_ :ref:`server` This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define. :ref:`share` This module provides userfile sharing support between two directly linked bots. :ref:`transfer` The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing. :ref:`twitch` The Twitch module modifies Eggdrop to interact with the Twitch service. Twitch uses a modified implementation of IRC so not all functionality will be present. Please read doc/TWITCH for specifics on how to best use the Tiwtch module. :ref:`uptime` This module reports uptime statistics to the uptime contest web site at ``_. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn't listed, try again later. See doc/settings/mod.uptime for more information, including details on what information is sent to the uptime server. :ref:`woobie` This is for demonstrative purposes only. If you are looking for starting point in writing modules, woobie is the right thing. eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/index.rst000066400000000000000000000043101524730042700242700ustar00rootroot00000000000000Eggdrop Module Information Last revised: Jul 25, 2016 Eggdrop Module Information ========================== What are modules? ----------------- Modules are independent portions of code that are loaded separately from the main core code of Eggdrop. This allows users to only implement the features they desire without adding the extra overhead or "bloat" of those they don't, or even write their own module to add an enhancement not currently implemented by the Eggdrop development team. For example, the transfer module provides the ability to transfer files to and from the Eggdrop, and the ident module provides the ability to run an ident server to answer ident requests. How to install a module ----------------------- Please note that these are only basic instructions for compiling and installing a module. Please read any and all directions included with the module you wish to install. 1. Download and un-tar the Eggdrop source code. 2. Place the new module in its own directory (in the format of (modulename).mod) in src/mod. 3. Run ./configure (from eggdrop-|version|/). 4. Type 'make config' or 'make iconfig'. 5. Type 'make'. 6. Copy the compiled module file (modulename.so) into your bot's modules folder. 7. Add 'loadmodule modulename' to your eggdrop.conf file (do not add the .so suffix). 8. Rehash or restart your bot. To view your currently loaded modules, type '.module'. Can I compile Eggdrop without dynamic modules? (Static compile) --------------------------------------------------------------- Yes, you can. If the configure script detects that your system CAN'T run modules, it will setup 'make' to link the modules in statically for you. You can choose this option yourself by using 'make static'. You can also try to compile dynamic modules on a static-only system by using 'make eggdrop'. Do I still need to 'loadmodule' modules? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ YES, when you compile statically, all the modules are linked into the main executable. HOWEVER, they are not enabled until you use loadmodule to enable them, hence you get nearly the same functionality with static modules as with dynamic modules. Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/internals.rst000066400000000000000000000435641524730042700251760ustar00rootroot00000000000000Eggdrop Bind Internals ====================== This document is intended for C developers who want to understand how Eggdrop’s Tcl binds or C binds work. For documentation purposes the “dcc” bind type is used as an example. It already exists and is suitable to illustrate the details of bind handling in Eggdrop. Note: All code snippets are altered for brevity and simplicity, see original source code for the full and current versions. General Workflow To Create a New Bind ------------------------------------- To create a new type of bind, there are generally three steps. First, you must add the new bind type to the `Bind Table`_. Once you have registered the bind type with the bind table, you must create a `C Function`_ that will be called to perform the functionality you wish to occur when the bind is triggered. Finally, once the C code supporting the new bind has been finished, the new Tcl binding is ready to be used in a Tcl script. .. _Bind Table: Adding a New Bind Type to the Bind Table ---------------------------------------- The bind is added to the bind table is by calling, either at module initialization or startup .. code-block:: C /* Global symbol, available to other C files with * extern p_tcl_bind_list H_dcc; */ p_tcl_bind_list H_dcc; /* Creating the bind table: * @param[in] const char *name Limited in length, see tclhash.h * @param[in] int flags HT_STACKABLE or 0 * @param[in] IntFunc Function pointer to C handler * @return p_tcl_bind_list aka (tcl_bind_list_t *) */ H_dcc = add_bind_table("dcc", 0, builtin_dcc); What the :code:`C handler` does is explained later, because a lot happens before it is actually called. :code:`IntFunc` is a generic function pointer that returns an :code:`int` with arbitrary arguments. :code:`H_dcc` can be exported from core and imported into modules as any other variable or function. That should be explained in a separate document. .. _HT_STACKABLE: Stackable Binds: HT_STACKABLE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :code:`HT_STACKABLE` means that multiple binds can exist for the same mask. An example of what happens when NOT using this flag shown in the code block below. .. code-block:: Tcl bind dcc - test proc1; # not stackable bind dcc - test proc2; # overwrites the first one, only proc2 will be called To enable this feature, you must set the second argument to ``add_bind_table()`` with ``HT_STACKABLE``. Using ``HT_STACKABLE`` does not automatically call all the binds that match, see the bind flags listed in `Triggering any Bind`_ section for details on the partner flags ``BIND_STACKABLE`` and ``BIND_WANTRET`` used in ``check_tcl_bind()``. .. _C Function: Adding Bind Functionality ------------------------- A C function must created that will be called when the bind is triggered. Importantly, the function is designed to accept specific arguments passed by Tcl. .. code-block:: C int check_tcl_dcc(const char *cmd, int idx, const char *args) { struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; int x; char s[11]; get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan); egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock); Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0); Tcl_SetVar(interp, "_dcc2", (char *) s, 0); Tcl_SetVar(interp, "_dcc3", (char *) args, 0); x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3", MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS); /* snip ..., return code handling */ return 0; } The global Tcl variables :code:`$_dcc1 $_dcc2 $_dcc3` are used as temporary string variables and passed as arguments to the registered Tcl proc. This shows which arguments the callbacks in Tcl get: * the nickname of the DCC chat user (handle of the user) * the IDX (socket id) of the partyline so :code:`[putdcc]` can respond back * another string argument that depends on the caller The call to :code:`check_tcl_dcc` can be found in the DCC parsing in `src/dcc.c`. Using the Bind in Tcl --------------------- After the bind table is created with :code:`add_bind_table`, Tcl procs can already be registered to this bind by calling .. code-block:: Tcl bind dcc -|- test myproc proc myproc {args} { putlog "myproc was called, argument list: '[join $args ',']'" return 0 } Of course it is not clear so far: * If flags :code:`-|-` matter for this bind at all and what they are checked against * If channel flags have a meaning or global/bot only * What :code:`test` is matched against to see if the bind should trigger * Which arguments :code:`myproc` receives, the example just accepts all arguments .. _triggering_any_bind: Triggering any Bind ------------------- `check_tcl_bind` is used by all binds and does the following .. code-block:: C /* Generic function to call one/all matching binds * @param[in] tcl_bind_list_t *tl Bind table (e.g. H_dcc) * @param[in] const char *match String to match the bind-masks against * @param[in] struct flag_record *atr Flags of the user calling the bind * @param[in] const char *param Arguments to add to the bind callback proc (e.g. " $_dcc1 $_dcc2 $_dcc3") * @param[in] int match_type Matchtype and various flags * @returns int Match result code */ /* Source code changed, only illustrative */ int check_tcl_bind(tcl_bind_list_t *tl, const char *match, struct flag_record *atr, const char *param, int match_type) { int x = BIND_NOMATCH; for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) { /* Check if bind mask matches */ if (!check_bind_match(match, tm->mask, match_type)) continue; for (tc = tm->first; tc; tc = tc->next) { /* Check if the provided flags suffice for this command. */ if (check_bind_flags(&tc->flags, atr, match_type)) { tc->hits++; /* not much more than Tcl_Eval(interp, " "); and grab the result */ x = trigger_bind(tc->func_name, param, tm->mask); } } } return x; } Bind Flags ^^^^^^^^^^ The last argument to :code:`check_tcl_bind` in `check_tcl_dcc` sets additional configurations for the bind. These are the allowed defined values: +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | **Value** | **Description** | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | MATCH_PARTIAL | Check the triggering value against the beginning of the bind mask, ie DIR triggers a mask for DIRECTORY (case insensitive) | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | MATCH_EXACT | Check the triggering value exactly against the bind mask value (case insensitive) | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | MATCH_CASE | Check the triggering value exactly against the bind mask value (case sensitive) | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | MATCH_MASK | Check if the bind mask is matched against the triggering value as a wildcarded value | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | MATCH_MODE | Special mode for `bind mode` similar to `MATCH_MASK`. This uses case-insensitive matching before the first space in the mask, | | | (the channel), and then case sensitive after the first space (the modes) | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | MATCH_CRON | Check the triggering value against a bind mask formatted as a cron entry, ie "30 7 6 7 5 " triggers a mask for "30 7 * * * " | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | BIND_USE_ATTR | Check the flags of the user match the flags required to trigger the bind | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | BIND_STACKABLE | Allow one mask to be re-used to call multiple Tcl proc. Must be used with HT_STACKABLE_ | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | BIND_WANTRET | With stacked binds, if the called Tcl proc called returns a '1', halt processing any further binds triggered by the action | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ | BIND_STACKRET | Used with BIND_WANTRET; allow stacked binds to continue despite receiving a '1' | +-------------------+-------------------------------------------------------------------------------------------------------------------------------+ Bind Return Values ^^^^^^^^^^^^^^^^^^ The value returned by the bind is often matched against a desired value to return a '1' (often used with BIND_WANTRET and BIND_STACKRET) to the calling function. +----------------+--------------------------------------------------------------------------------------------------------------+ | **Value** | **Description** | +----------------+--------------------------------------------------------------------------------------------------------------+ | BIND_NOMATCH | The bind was not triggered due to not meeting the criteria set for the bind | +----------------+--------------------------------------------------------------------------------------------------------------+ | BIND_AMBIGUOUS | The triggering action matched multiple non-stackable binds | +----------------+--------------------------------------------------------------------------------------------------------------+ | BIND_MATCHED | The bind criteria was met, but the Tcl proc it tried to call could not be found | +----------------+--------------------------------------------------------------------------------------------------------------+ | BIND_EXECUTED | The bind criteria was met and the Tcl proc was called | +----------------+--------------------------------------------------------------------------------------------------------------+ | BIND_EXEC_LOG | The bind criteria was met, the Tcl proc was called, and Eggdrop logged the bind being called | +----------------+--------------------------------------------------------------------------------------------------------------+ | BIND_QUIT | Sentinel value to signal that quit was triggered by the target leaving the partyline or filesys area. | | | (Virtual bind to CMD_LEAVE) | +----------------+--------------------------------------------------------------------------------------------------------------+ Note: For a bind type to be stackable it needs to be registered with :code:`HT_STACKABLE` AND :code:`check_tcl_bind` must be called with :code:`BIND_STACKABLE`. C Binding --------- To create a C function that is called by the bind, Eggdrop provides the :code:`add_builtins` function. .. code-block:: C /* Add a list of C function callbacks to a bind * @param[in] tcl_bind_list_t * the bind type (e.g. H_dcc) * @param[in] cmd_t * a NULL-terminated table of binds: * cmd_t *mycmds = { * {char *name, char *flags, IntFunc function, char *tcl_name}, * ..., * {NULL, NULL, NULL, NULL} * }; */ void add_builtins(tcl_bind_list_t *tl, cmd_t *cc) { char p[1024]; cd_tcl_cmd tclcmd; tclcmd.name = p; tclcmd.callback = tl->func; for (i = 0; cc[i].name; i++) { /* Create Tcl command with automatic or given names *:, e.g. * - H_raw {"324", "", got324, "irc:324"} => *raw:irc:324 * - H_dcc {"boot", "t", cmd_boot, NULL} => *dcc:boot */ egg_snprintf(p, sizeof p, "*%s:%s", tl->name, cc[i].funcname ? cc[i].funcname : cc[i].name); /* arbitrary void * can be included, we include C function pointer */ tclcmd.cdata = (void *) cc[i].func; add_cd_tcl_cmd(tclcmd); bind_bind_entry(tl, cc[i].flags, cc[i].name, p); } } It automatically creates Tcl commands (e.g. :code:`*dcc:cmd_boot`) that will call the `C handler` from `add_bind_table` in the first section `Bind Table`_ and it gets a context (void \*) argument with the C function it is supposed to call (e.g. `cmd_boot()`). Now we can actually look at the C function handler for dcc as an example and what it has to implement. C Handler --------- The example handler for DCC looks as follows .. code-block:: C /* Typical Tcl_Command arguments, just like e.g. tcl_putdcc is a Tcl/C command for [putdcc] */ static int builtin_dcc (ClientData cd, Tcl_Interp *irp, int argc, char *argv[]) { int idx; /* F: The C function we want to call, if the bind is okay, e.g. cmd_boot() */ Function F = (Function) cd; /* Task of C function: verify argument count and syntax as any Tcl command */ BADARGS(4, 4, " hand idx param"); /* C Macro only used in C handlers for bind types, sanity checks the Tcl proc name * for *: and that we are in the right C handler */ CHECKVALIDITY(builtin_dcc); idx = findidx(atoi(argv[2])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } /* Call the desired C function, e.g. cmd_boot() with their arguments */ F(dcc[idx].user, idx, argv[3]); Tcl_ResetResult(irp); Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } This is finally the part where we see the arguments a C function gets for a DCC bind as opposed to a Tcl proc. :code:`F(dcc[idx].user, idx, argv[3])`: * User information as struct userrec * * IDX as int * The 3rd string argument from the Tcl call to \*dcc:cmd_boot, which was :code:`$_dcc3` which was :code:`args` to :code:`check_tcl_dcc` which was everything after the dcc command So this is how we register C callbacks for binds with the correct arguments .. code-block:: C /* We know the return value is ignored because the return value of F * in builtin_dcc is ignored, so it can be void, but for other binds * it could be something else and used in the C handler for the bind. */ void cmd_boot(struct userrec *u, int idx, char *par) { /* snip */ } cmd_t *mycmds = { {"boot", "t", (IntFunc) cmd_boot, NULL /* automatic name: *dcc:boot */}, {NULL, NULL, NULL, NULL} }; add_builtins(H_dcc, mycmds); Summary ------- In summary, this is how the dcc bind is called: * :code:`check_tcl_dcc()` creates Tcl variables :code:`$_dcc1 $_dcc2 $_dcc3` and lets :code:`check_tcl_bind` call the binds * Tcl binds are done at this point * C binds mean the Tcl command associated with the bind is :code:`*dcc:boot` which calls :code:`builtin_dcc` which gets :code:`cmd_boot` as ClientData cd argument * :code:`gbuildin_dcc` performs some sanity checking to avoid crashes and then calls :code:`cmd_boot()` aka :code:`F()` with the arguments it wants C callbacks to have Example edited and annotated gdb backtrace in :code::`cmd_boot` after doing :code:`.boot test` on the partyline as user :code:`thommey` with typical owner flags. :: #0 cmd_boot (u=0x55e8bd8a49b0, idx=4, par=0x55e8be6a0010 "test") at cmds.c:614 *u = {next = 0x55e8bd8aec90, handle = "thommey", flags = 8977024, flags_udef = 0, chanrec = 0x55e8bd8aeae0, entries = 0x55e8bd8a4a10} #1 builtin_dcc (cd=0x55e8bbf002d0 , irp=0x55e8bd59b1c0, argc=4, argv=0x55e8bd7e3e00) at tclhash.c:678 idx = 4 argv = {0x55e8be642fa0 "*dcc:boot", 0x55e8be9f6bd0 "thommey", 0x55e8be7d9020 "4", 0x55e8be6a0010 "test", 0x0} F = 0x55e8bbf002d0 #5 Tcl_Eval (interp=0x55e8bd59b1c0, script = "*dcc:boot $_dcc1 $_dcc2 $_dcc3") from /usr/lib/x86_64-linux-gnu/libtcl8.6.so Tcl: return $_dcc1 = "thommey" Tcl: return $_dcc2 = "4" Tcl: return $_dcc3 = "test" Tcl: return $lastbind = "boot" (set automatically by trigger_bind) #8 trigger_bind (proc=proc@entry=0x55e8bd5efda0 "*dcc:boot", param=param@entry=0x55e8bbf4112b " $_dcc1 $_dcc2 $_dcc3", mask=mask@entry=0x55e8bd5efd40 "boot") at tclhash.c:742 #9 check_tcl_bind (tl=0x55e8bd5eecb0 , match=match@entry=0x7ffcf3f9dac1 "boot", atr=atr@entry=0x7ffcf3f9d100, param=param@entry=0x55e8bbf4112b " $_dcc1 $_dcc2 $_dcc3", match_type=match_type@entry=80) at tclhash.c:942 proc = 0x55e8bd5efda0 "*dcc:boot" mask = 0x55e8bd5efd40 "boot" brkt = 0x7ffcf3f9dac6 "test" #10 check_tcl_dcc (cmd=cmd@entry=0x7ffcf3f9dac1 "boot", idx=idx@entry=4, args=0x7ffcf3f9dac6 "test") at tclhash.c:974 fr = {match = 5, global = 8977024, udef_global = 0, bot = 0, chan = 0, udef_chan = 0} #11 dcc_chat (idx=idx@entry=4, buf=, i=) at dcc.c:1068 v = 0x7ffcf3f9dac1 "boot" eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/000077500000000000000000000000001524730042700232105ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/assoc.rst000066400000000000000000000005211524730042700250500ustar00rootroot00000000000000Last revised: January 1, 2002 .. _assoc: ============ Assoc Module ============ This module provides assoc support, i.e. naming channels on the botnet. This module requires: none Put this line into your Eggdrop configuration file to load the assoc module:: loadmodule assoc Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/blowfish.rst000066400000000000000000000011011524730042700255500ustar00rootroot00000000000000Last revised: January 28, 2003 .. _blowfish: =============== Blowfish Module =============== Eggdrop can encrypt your userfile, so users can have secure passwords. Please note that when you change your encryption method later (i.e. using other modules like a md5 module), you can't use your current userfile anymore. Eggdrop will not start without an encryption module. This module requires: none Put this line into your Eggdrop configuration file to load the blowfish module:: loadmodule blowfish Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/channels.rst000066400000000000000000000416501524730042700255430ustar00rootroot00000000000000Last revised: October 25, 2010 .. _channels: =============== Channels Module =============== This module provides channel related support for the bot. Without it, you won't be able to make the bot join a channel or save channel specific userfile information. This module requires: none Put this line into your Eggdrop configuration file to load the channels module:: loadmodule channels There are also some variables you can set in your config file: set chanfile "LamestBot.chan" Enter here the filename where dynamic channel settings are stored. set force-expire 0 Set this setting to 1 if you want your bot to expire bans/exempts/invites set by other opped bots on the channel. set share-greet 0 Set this setting to 1 if you want your bot to share user greets with other bots on the channel if sharing user data. set use-info 1 Set this setting to 1 if you want to allow users to store an info line. set allow-ps 0 Set this setting to 1 if you want to allow both +p and +s channel modes to be enforced at the same time by the chanmode channel setting. Make sure your server supports +ps mixing or you may get endless mode floods. channel add #channel { SETTINGS } This command adds a static channel to your bot (it cannot be removed via the partyline- we recommend using the .+chan command on the partyline instead). This command will add the channel preconfigured with the specified settings. A full list of settings is defined in `Channel Settings`_. Channel Settings ---------------- There are two types of channel settings: value-based settings (where you configure a setting with a number or string), and enable/disable-based settings (where you turn a setting on or off). These settings can be configured via Tcl using the 'channel set' command: channel set This command modifies a specific channel setting for a channel. There are many different options for channels which you can define. Some settings are enabled or disabled by a plus or minus in front of them, and others directly take text or integer values. Value-based Channel Settings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ chanmode +/- This setting makes the bot enforce channel modes. It will always enforce the + and remove the - modes. idle-kick 0 This setting will make the bot check every minute for idle users. Set this to 0 to disable idle check. stopnethack-mode 0 This setting will make the bot de-op anyone who enters the channel with serverops. There are seven different modes for this settings: +---+--------------------------------------------------------+ | 0 | turn off | +---+--------------------------------------------------------+ | 1 | isoptest (allow serverop if registered op) | +---+--------------------------------------------------------+ | 2 | wasoptest (allow serverop if user had op before split) | +---+--------------------------------------------------------+ | 3 | allow serverop if isop or wasop | +---+--------------------------------------------------------+ | 4 | allow serverop if isop and wasop. | +---+--------------------------------------------------------+ | 5 | If the channel is -bitch, see stopnethack-mode 3 | | +--------------------------------------------------------+ | | If the channel is +bitch, see stopnethack-mode 1 | +---+--------------------------------------------------------+ | 6 | If the channel is -bitch, see stopnethack-mode 2 | | +--------------------------------------------------------+ | | If the channel is +bitch, see stopnethack-mode 4 | +---+--------------------------------------------------------+ revenge-mode 0 This settings defines how the bot should punish bad users when revenging. There are four possible settings: +---+--------------------------------------------------------------------------+ | 0 | Deop the user. | +---+--------------------------------------------------------------------------+ | 1 | Deop the user and give them the +d flag for the channel. | +---+--------------------------------------------------------------------------+ | 2 | Deop the user, give them the +d flag for the channel, and kick them. | +---+--------------------------------------------------------------------------+ | 3 | Deop the user, give them the +d flag for the channel, kick, and ban them.| +---+--------------------------------------------------------------------------+ ban-type 3 This setting defines what type of bans should eggdrop place for +k users or when revenge-mode is 3. Available types are: +---+------------------------+ | 0 \*!user\@host | +---+------------------------+ | 1 \*!\*user\@host | +---+------------------------+ | 2 \*!\*\@host | +---+------------------------+ | 3 \*!\*user\@\*.host | +---+------------------------+ | 4 \*!\*\@*.host | +---+------------------------+ | 5 nick!user\@host | +---+------------------------+ | 6 nick!\*user\@host | +---+------------------------+ | 7 nick!\*\@host | +---+------------------------+ | 8 nick!\*user\@*.host | +---+------------------------+ | 9 nick!\*\@*.host | +---+------------------------+ You can also specify types from 10 to 19 which correspond to types 0 to 9, but instead of using a * wildcard to replace portions of the host, only numbers in hostnames are replaced with the '?' wildcard. Same is valid for types 20-29, but instead of '?', the '*' wildcard will be used. Types 30-39 set the host to '*'. ban-time 120 Set here how long temporary bans will last (in minutes). If you set this setting to 0, the bot will never remove them. exempt-time 60 Set here how long temporary exempts will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the exempts every X minutes, but will not remove the exempt if a ban is set on the channel that matches that exempt. Once the ban is removed, then the exempt will be removed the next time the bot checks. Please note that this is an IRCnet feature. invite-time 60 Set here how long temporary invites will last (in minutes). If you set this setting to 0, the bot will never remove them. The bot will check the invites every X minutes, but will not remove the invite if a channel is set to +i. Once the channel is -i then the invite will be removed the next time the bot checks. Please note that this is an IRCnet feature. aop-delay (minimum:maximum) This is used for autoop, autohalfop, autovoice. If an op or voice joins a channel while another op or voice is pending, the bot will attempt to put both modes on one line. +--------------+-----------------------------------------+ | aop-delay 0 | No delay is used. | +--------------+-----------------------------------------+ | aop-delay X | An X second delay is used. | +--------------+-----------------------------------------+ | aop-delay X:Y| A random delay between X and Y is used. | +--------------+-----------------------------------------+ need-op { putserv "PRIVMSG #lamest :op me cos i'm lame!" } This setting will make the bot run the script enclosed in braces if it does not have ops. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-invite { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs an invite to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-key { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs the key to the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-unban { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs to be unbanned on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. need-limit { putserv "PRIVMSG #lamest :let me in!" } This setting will make the bot run the script enclosed in braces if it needs the limit to be raised on the channel. This must be shorter than 120 characters. If you use scripts like getops.tcl or botnetop.tcl, you don't need to set this setting. flood-chan 15:60 Set here how many channel messages in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables text flood protection for the channel, where X is an integer >= 0. flood-deop 3:10 Set here how many deops in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables deop flood protection for the channel, where X is an integer >= 0. flood-kick 3:10 Set here how many kicks in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables kick flood protection for the channel, where X is an integer >= 0. flood-join 5:60 Set here how many joins in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables join flood protection for the channel, where X is an integer >= 0. flood-ctcp 3:60 Set here how many channel ctcps in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables ctcp flood protection for the channel, where X is an integer >= 0. flood-nick 5:60 Set here how many nick changes in how many seconds from one host constitutes a flood. Setting this to 0, 0:X or X:0 disables nick flood protection for the channel, where X is an integer >= 0. Enable/Disable Channel Settings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These settings should be preceded by a + or - to enable or disable the setting, respctively. enforcebans When a ban is set, kick people who are on the channel and match the ban dynamicbans Only activate bans on the channel when necessary? This keeps the channel's ban list from getting excessively long. The bot still remembers every ban, but it only activates a ban on the channel when it sees someone join who matches that ban. userbans Allow bans to be made by users directly? If turned off, the bot will require all bans to be made through the bot's console. dynamicexempts Only activate exempts on the channel when necessary? This keeps the channel's exempt list from getting excessively long. The bot still remembers every exempt, but it only activates a exempt on the channel when it sees a ban set that matches the exempt. The exempt remains active on the channel for as long as the ban is still active. userexempts Allow exempts to be made by users directly? If turned off, the bot will require all exempts to be made through the bot's console. dynamicinvites Only activate invites on the channel when necessary? This keeps the channel's invite list from getting excessively long. The bot still remembers every invite, but the invites are only activated when the channel is set to invite only and a user joins after requesting an invite. Once set, the invite remains until the channel goes to -i. userinvites Allow invites to be made by users directly? If turned off, the bot will require all invites to be made through the bot's console. autoop Op users with the +o flag as soon as they join the channel This is insecure and not recommended. autohalfop Halfop users with the +l flag as soon as they join the channel? This is insecure and not recommended. bitch Only let users with the +o flag have op on the channel greet Say a user's info line when they join the channel protectops Re-op a user with the +o flag if they get deopped? protecthalfops Re-halfop a user with the +l flag if they get dehalfopped protectfriends Re-op a user with the +f flag if they get deopped statuslog Log the channel status line every 5 minutes? This shows the bot's status on the channel (op, voice, etc.), the channel's modes, and the total number of members, ops, voices, regular users, and +b, +e, and +I modes on the channel. A sample status line follows: [01:40] @#lamest (+istn) : [m/1 o/1 v/4 n/7 b/1 e/5 I/7] revenge Remember people who deop/kick/ban the bot, valid ops, or friends and punish them? Users with the +f flag are exempt from revenge. revengebot This is similar to to the 'revenge' option, but it only triggers if a bot gets deopped, kicked or banned. autovoice Voice users with the +v flag when they join the channel? secret Prevent this channel from being listed on the botnet? shared Share channel-related user info for this channel? cycle Cycle the channel when it has no ops? dontkickops Do you want the bot not to be able to kick users who have the +o flag, letting them kick-flood for instance to protect the channel against clone attacks? inactive This prevents the bot from joining the channel (or makes it leave the channel if it is already there). It can be useful to make the bot leave a channel without losing its settings, channel-specific user flags, channel bans, and without affecting sharing. seen Respond to seen requests in the channel? The seen module must be loaded for this to work. nodesynch Allow non-ops to perform channel modes? This can stop the bot from fighting with services such as ChanServ, or from kicking IRCops when setting channel modes without having ops. static Allow only permanent owners to remove the channel Default Channel Values ---------------------- The following settings are used as default values when you .+chan #chan or .tcl channel add #chan. You can modify these default values by adding the below values to your config fil. Look in the `Channel Settings`_ for an explanation of each option. set default-flood-chan 15:60 set default-flood-deop 3:10 set default-flood-kick 3:10 set default-flood-join 5:60 set default-flood-ctcp 3:60 set default-flood-nick 5:60 set default-aop-delay 5:30 set default-idle-kick 0 set default-chanmode "nt" set default-stopnethack-mode 0 set default-revenge-mode 0 set default-ban-type 3 set default-ban-time 120 set default-exempt-time 60 set default-invite-time 60 set default-chanset { | -autoop | -autovoice | -bitch | +cycle | +dontkickops | +dynamicbans | +dynamicexempts | +dynamicinvites | -enforcebans | +greet | -inactive | -nodesynch | -protectfriends | +protectops | -revenge | -revengebot | -secret | -seen | +shared | -statuslog | +userbans | +userexempts | +userinvites | -protecthalfops | -autohalfop | -static } Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/compress.rst000066400000000000000000000014501524730042700255750ustar00rootroot00000000000000Last revised: May 27, 2004 .. _compress: =============== Compress Module =============== This module provides support for file compression. It can be used to compress files via Tcl or to transfer the userfile compressed during the share process, saving bandwidth. This module requires: share Put this line into your Eggdrop configuration file to load the compress module:: loadmodule compress There are also some variables you can set in your config file: set share-compressed 1 Allow compressed sending of user files? The user files are compressed with the compression level defined in 'compress-level'. set compress-level 9 This is the default compression level used. These levels are the same as those used by GNU gzip. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/console.rst000066400000000000000000000015041524730042700254040ustar00rootroot00000000000000Last revised: January 1, 2002 .. _console: ============== Console Module ============== This module provides storage of console settings when you exit the bot or type .store on the partyline. This module requires: none Put this line into your Eggdrop configuration file to load the console module:: loadmodule console There are also some variables you can set in your config file: set console-autosave 1 Save users console settings automatically? Otherwise, they have to use the .store command. set force-channel 0 If a user doesn't have any console settings saved, which channel do you want them automatically put on? set info-party 0 Enable this setting if a user's global info line should be displayed when they join a botnet channel. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/ctcp.rst000066400000000000000000000025561524730042700247030ustar00rootroot00000000000000Last revised: February 12, 2002 .. _ctcp: =========== CTCP Module =========== This module provides the normal ctcp replies that you would expect. Without it loaded, CTCP CHAT will not work. This module requires: server Put this line into your Eggdrop configuration file to load the ctcp module:: loadmodule ctcp There are also some variables you can set in your config file: set ctcp-mode 0 Set here how the ctcp module should answer ctcps. There are 3 possible operating modes: +---+--------------------------------------------------------------+ | 0 | Normal behavior is used. | +---+--------------------------------------------------------------+ | 1 | The bot ignores all ctcps, except for CHAT and PING requests | | | by users with the +o flag. | +---+--------------------------------------------------------------+ | 2 | Normal behavior is used, however the bot will not answer more| | | than X ctcps in Y seconds (defined by 'set flood-ctcp'). | +---+--------------------------------------------------------------+ There are also several variables to help make your bot less noticeable. They are: ctcp-version, ctcp-finger, and ctcp-userinfo. You can use set to set them to values you'd like. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/dns.rst000066400000000000000000000030061524730042700245250ustar00rootroot00000000000000Last revised: September 26, 2010 .. _dns: ========== DNS Module ========== This module provides asynchronous dns support. This will avoid long periods where the bot just hangs there, waiting for a hostname to resolve, which will often let it timeout on all other connections. This module requires: none Put this line into your Eggdrop configuration file to load the dns module:: loadmodule dns There are also some variables you can set in your config file: set dns-servers "8.8.8.8 8.8.4.4" In case your bot has trouble finding dns servers or you want to use specific ones, you can set them here. The value is a list of dns servers. The relative order doesn't matter. You can also specify a non-standard port. The default is to use the system specified dns servers. You don't need to modify this normally. set dns-cache 86400 Specify how long should the DNS module cache replies at maximum. The value must be in seconds. Note that it will respect the TTL of the reply and this is just an upper boundary. set dns-negcache 600 Specify how long should the DNS module cache negative replies (NXDOMAIN, DNS Lookup failed). The value must be in seconds. set dns-maxsends 4 How many times should the DNS module resend the query for a given domain if it receives no reply? set dns-retrydelay 3 Specify how long should the DNS module wait for a reply before resending the query. The value must be in seconds. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/filesys.rst000066400000000000000000000160341524730042700254240ustar00rootroot00000000000000Last revised: Dec 30, 2017 .. _filesys: ============== Filesys Module ============== This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. This module requires: transfer ----------------- Config file setup ----------------- Put this line into your Eggdrop configuration file to load the filesys module:: loadmodule filesys There are also some variables you can set in your config file: set files-path "/home/mydir/eggdrop/filesys" Set here the 'root' directory for the file system. set incoming-path "/home/mydir/eggdrop/filesys/incoming" If you want to allow uploads, set this to the directory uploads should be put into. Set this to "" if you don't want people to upload files to your bot. set upload-to-pwd 0 If you don't want to have a central incoming directory, but instead want uploads to go to the current directory that a user is in, set this setting to 1. set filedb-path "" Eggdrop creates a '.filedb' file in each subdirectory of your file area to keep track of its own file system information. If you can't do that (for example, if the dcc path isn't owned by you, or you just don't want it to do that) specify a path here where you'd like all of the database files to be stored instead. set max-file-users 20 Set here the maximum number of people that can be in the file area at once. Setting this to 0 makes it effectively infinite. set max-filesize 1024 Set here the maximum allowable file size that will be received (in KB). Setting this to 0 makes it effectively infinite. --------------- Partyline usage --------------- ^^^^^^ .files ^^^^^^ Moves you into the file transfer sub-system, if it has been enabled on this bot. From there you can browse through the files online and use dcc file transfers to download and upload. ^^^^^^^^^^^^^^^^^^^^^^^^^ .cancel [file] ... ^^^^^^^^^^^^^^^^^^^^^^^^^ Tells the bot to stop sending a file that is pending (either queued, waiting, or in the process of being transferred). ^^^^^^^^^^^^^^^ .cd ^^^^^^^^^^^^^^^ Changes your current directory if possible. this works exactly like the unix command. ^^^^^^^^^^^^^^^^^^ .cp ^^^^^^^^^^^^^^^^^^ Copies a file or group of files from one place to another. ^^^^^^^^^^^^^^^^^^^^^^^^^^ .desc ^^^^^^^^^^^^^^^^^^^^^^^^^^ Changes the description for a file. if you are a master or file janitor, you can change the description for any file. otherwise you can only change the descriptions for files you have uploaded. The description is restricted to 3 lines of 60 characters each, and is broken up between words. you can force a line break by putting a '|' in the comment. ^^^^^^^^^^^^^^^^^^^^^^^^^ .filestats [clear] ^^^^^^^^^^^^^^^^^^^^^^^^^ Reports on the users upload & download statistics. Optional argument 'clear' clears a users upload and download statistics. ^^^^^^ .stats ^^^^^^ Clears a users upload & download statistics. ^^^^^^^^^^^^^^^^^^^^^^^^^^ .get [nickname] ^^^^^^^^^^^^^^^^^^^^^^^^^^ Sends you the file(s) requested, over IRC. you should get a DCC SEND notice on IRC, and have your client accept it. if your nickname on IRC is different than the one you use on the bot, you should specify a nickname too. you can also use that to send files to other people. if a file you want is actually on another bot, it may take a little bit longer so be patient. if that bot isn't on the botnet right now, it will say the file isn't available. There is a limit to the number of transfers you can have going simultaneously, so if you go over this limit, the remainder of your file requests will be queued. As the first files finish transferring, the queued files will be sent. ^^^^^^^^^^^^^^^^^^^^^^^^ .hide [files] ... ^^^^^^^^^^^^^^^^^^^^^^^^ Marks a file as hidden, so that normal users can't see it. Only a master or file janitor using %b'lsa'%b can see hidden files. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .ln ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Creates a link to a file on another bot. The filepath has to be complete, like '/gifs/uglyman.gif'. If the bot is not connected to the botnet, nobody will be able to download the file until that bot connects again. The local filename can be anything you want. Example: ln Snowbot:/gifs/uglyman.gif ugly.gif ^^^^^^^^^^^^^^ .ls [filemask] ^^^^^^^^^^^^^^ Displays the files in the current directory. Subdirectories are shown with "" next to them, and other files will display their size (typically in kilobytes), who uploaded them (and when), and how many times each file has been downloaded. If a description of the file exists, it is displayed below the filename. You can restrict the file listing by specifying a mask, just like in unix. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .mkdir [flags [channel]] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Creates a subdirectory from this one, with the given name. If flags are specified, then those flags are required to enter or even see the directory. You can even specify a channel that the flags are matched against. You can use the %b'mkdir'%b command again to alter or remove those flags. ^^^^^^^^^^^^^^^^^^^ .mv ^^^^^^^^^^^^^^^^^^^ Moves a file or group of files from one place to another (it can also be used to rename files). ^^^^^^^^ .pending ^^^^^^^^ Gives you a listing of every file you've requested which is still waiting, queued, or in the process of transferring. It shows you the nickname on IRC that the file is being sent to, and, if the transfer is in progress, tells you how far along the transfer is. ^^^^ .pwd ^^^^ Tells you what your current directory is. ^^^^^ .quit ^^^^^ Exits the file system. ^^^^^^^^^^^^^^^^^^^^^ rm [files] ... ^^^^^^^^^^^^^^^^^^^^^ Erase a file for good. ^^^^^^^^^^^^ .rmdir ^^^^^^^^^^^^ Removes an existing directory, if there are no files in it. ^^^^^^^^^^^^^^^^^^^^^^^^^ .share [files] ... ^^^^^^^^^^^^^^^^^^^^^^^^^ Marks a file as shared. This means that other bots can get the file remotely for users on their file systems. By default, files are marked as unshared. ^^^^^^^^^ .optimize ^^^^^^^^^ Cleans up the current directory's database. If you have a large directory with many files you may want to use this command if you experience slow-downs/delays over time. Normally, the db should clean up itself though. ^^^^^^^ .unhide ^^^^^^^ Makes a file be not hidden any more. ^^^^^^^^^^^^^^^^^^^^^^^^^^ .unshare [file] ... ^^^^^^^^^^^^^^^^^^^^^^^^^^ Removes the shared tag from a file. ^^^^^^^^^^^^^^^ .filesys module ^^^^^^^^^^^^^^^ This module provides an area within the bot where users can store and manage files. With this module, the bot is usable as a file server. The following commands are provided by the filesys module: For filesystem users: files Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/ident.rst000066400000000000000000000062561524730042700250560ustar00rootroot00000000000000Last revised: June 27, 2019 .. _ident: ============ Ident Module ============ This module adds Eggdrop support for the externally-provided oident service, or alternatively the ability for Eggdrop to act as its own ident daemon. Traditionally, an IRC server queries port 113 on an irc client's host to determine the ident for the client. The client's host normally replies with the username of the account. If the host machine is running oident as its ident service, Eggdrop can now interact with oident to instead reply with a custom ident specified by Eggdrop (oidentd is an external service not specific to Eggdrop nor maintained by Eggheads developers, thus no support for it can be given). Alternatively, this module can also allow Eggdrop to act as its own ident daemon, answering queries from the IRC server directly. While this option may be of use to a very specific group of people, it requires additional external configuration via root access. Because most modern OSs do not allow user-level programs to bind to port 113, required by this option, a user must either: - Allow Eggdrop to bind to ports <1024 via the command:: sudo setcap "cap_net_bind_service=+ep" /path/to/eggdrop Warning: this will allow Eggdrop to *any* port below 1024, potentially allowing a user to impersonate a standard system service with Eggdrop - Use iptables to re-route ident queries destined for port 113 to a >1024 port that Eggdrop can bind to, for example by running:: sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport \ 113 -j REDIRECT --to-port ' Additionally, using Eggdrop as a built-in ident daemon can cause issues when running multiple bots from the same account. The ident server is only activated and bound to the configured ident port while Eggdrop is connecting to an IRC server, but only one Eggdrop can bind to port 113 at a time. If multiple bots were to start at the same time, such as after a netsplit or host reboot, only one bot can bind to port 113 at a time, creating ident conflicts. Thus, this option is best suited for single-bot environments. This module requires: - oident to be running on your shell host, configured to allow user spoofing, for the oidentd method. Put this line into your Eggdrop configuration file to load the ident module:: loadmodule ident There are also some variables you can set in your config file: set ident-method 0 This sets which ident method you wish to use: 0 = oidentd / Your bot will overwrite $HOME/.oidentd.conf right before opening the socket to the IRC server with the global reply. 1 = Builtin / Your bot will automatically turn its builtin identd on and off when needed so it shouldn't conflict with other identds that do the same. Ident port 113 is normally restricted to privileged processes on UNIX systems. set ident-port 113 This sets the port which port Eggdrop will attempt to bind to for option 1 of ident-method. Most OSs do not allow non-privileged programs such as Eggdrop to bind to ports <1024; please read the beginning of this document for potential ways to implement this setting. Copyright (C) 2019 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/irc.rst000066400000000000000000000131751524730042700245260ustar00rootroot00000000000000Last revised: August 21, 2004 .. _irc: ========== IRC Module ========== This module controls the bots interaction on IRC. It allows the bot to join channels, etc. You have to load this if you want your bot to come on irc. This module requires: server, channels Put this line into your Eggdrop configuration file to load the irc module:: loadmodule irc There are also some variables you can set in your config file: set bounce-bans 1 Set this to 1 if you want to bounce all server bans. set bounce-exempts 0 Set this to 1 if you want to bounce all server exemptions (+e modes). This is disabled if use-exempts is disabled. set bounce-invites 0 Set this to 1 if you want to bounce all server invitations (+I modes). This is disabled if use-invites is disabled. set bounce-modes 0 Set this to 1 if you want to bounce all server modes. set max-modes 30 There is a global limit for +b/+e/+I modes. This limit should be set to the same value as max-bans for networks that do not support +e/+I. set max-bans 30 Set here the maximum number of bans you want the bot to set on a channel. Eggdrop will not place any more bans if this limit is reached. Undernet currently allows 45 bans, IRCnet allows 30, EFnet allows 100, and DALnet allows 100. set max-exempts 20 Set here the maximum number of exempts you want Eggdrop to set on a channel. Eggdrop will not place any more exempts if this limit is reached. set max-invites 20 Set here the maximum number of invites you want Eggdrop to set on a channel. Eggdrop will not place any more invites if this limit is reached. | set use-exempts 0 | set use-invites 0 These settings should be left commented unless the default values are being overridden. By default, exempts and invites are on for EFnet and IRCnet, but off for all other large networks. This behavior can be modified with the following 2 flags. If your network doesn't support +e/+I modes then you will be unable to use these features. set learn-users 0 If you want people to be able to add themselves to the bot's userlist with the default userflags (defined above in the config file) via the 'hello' msg command, set this to 1. set wait-split 600 Set here the time (in seconds) to wait for someone to return from a netsplit (i.e. wasop will expire afterwards). Set this to 1500 on IRCnet since its nick delay stops after 30 minutes. set wait-info 180 Set here the time (in seconds) that someone must have been off-channel before re-displaying their info line. set mode-buf-length 200 Set this to the maximum number of bytes to send in the arguments of modes sent to the server. Most servers default this to 200. | unbind msg - hello \*msg:hello | bind msg - myword \*msg:hello Many IRCops find bots by seeing if they reply to 'hello' in a msg. You can change this to another word by un-commenting these two lines and changing "myword" to the word wish to use instead of'hello'. It must be a single word. | unbind msg - ident \*msg:ident | unbind msg - addhost \*msg:addhost Many takeover attempts occur due to lame users blindly /msg ident'ing to the bot and attempting to guess passwords. We now unbind this command by default to discourage them. You can enable this command by un-commenting these two lines. | set opchars "@" | #set opchars "@&~" Some IRC servers are using some non-standard op-like channel prefixes/modes. Define them here so the bot can recognize them. Just "@" should be fine for most networks. Un-comment the second line for some UnrealIRCds. set no-chanrec-info 0 If you are so lame you want the bot to display peoples info lines, even when you are too lazy to add their chanrecs to a channel, set this to 1. *NOTE* This means *every* user with an info line will have their info line displayed on EVERY channel they join (provided they have been gone longer than wait-info). These were the core irc module settings. There are more settings for 'net-type' IRCnet and Efnet. net-type has to be set in the server module config section. Use the following settings only if you set 'net-type' to IRCnet! set prevent-mixing 1 At the moment, the current IRCnet IRCd version (2.10) doesn't support the mixing of b, o and v modes with e and I modes. This might be changed in the future, so use 1 at the moment for this setting. Use the following settings only if you set 'net-type' to Efnet! set kick-method 1 If your network supports more users per kick command then 1, you can change this behavior here. Set this to the number of users to kick at once, or set this to 0 for all at once. set modes-per-line 3 Some networks allow you to stack lots of channel modes into one line. They're all guaranteed to support at least 3, so that's the default. If you know your network supports more, you may want to adjust this. This setting is limited to 6, although if you want to use a higher value, you can modify this by changing the value of MODES_PER_LINE_MAX in src/chan.h and recompiling the bot. set include-lk 1 Some networks don't include the +l limit and +k or -k key modes in the modes-per-line (see above) limitation. Set include-lk to 0 for these networks. set use-354 0 Set this to 1 if your network uses IRCu2.10.01 specific /who requests. Eggdrop can, therefore, ask only for exactly what's needed. set rfc-compliant 1 If your network doesn't use rfc 1459 compliant string matching routines, set this to 0. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/notes.rst000066400000000000000000000023241524730042700250730ustar00rootroot00000000000000Last revised: January 1, 2002 .. _notes: ============ Notes Module ============ This module provides support for storing of notes for users from each other. Note sending between currently online users is supported in the core, this is only for storing the notes for later retrieval. This module requires: none Put this line into your Eggdrop configuration file to load the notes module:: loadmodule notes There are also some variables you can set in your config file: set notefile "LamestBot.notes" Set here the filename where private notes between users are stored. set max-notes 50 Set here the maximum number of notes to allow to be stored for each user (to prevent flooding). set note-life 60 Set here how long (in days) to store notes before expiring them. set allow-fwd 0 Set this to 1 if you want to allow users to specify a forwarding address for forwarding notes to another account on another bot. set notify-users 1 Set this to 1 if you want the bot to let people know hourly if they have any notes. set notify-onjoin 1 Set this to 1 if you want the bot to let people know on join if they have any notes. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/pbkdf2.rst000066400000000000000000000030331524730042700251110ustar00rootroot00000000000000Last revised: October 28, 2020 .. _pbkdf2: =============== PBKDF2 Module =============== Eggdrop encrypts its userfile, so users can have secure passwords. Eggdrop will not start without an encryption module. As of Eggdrop 1.9.0, a new encryption module was added, the PBKDF2 module. The PBKDF2 module was designed to work with the previous encryption module (blowfish) to make the transition easier. If this is a new bot, you can safely load just the PBKDF2 module. If you are transitioning a userfile from 1.8 or earlier, you should load this AND the blowfish module. By doing so, Eggdrop will seamlessly update the old blowfish hashes to the new PBKDF2 hashes once a user logs in for the first time, and allow you to (eventually) remove the blowfish module altogether. Outside of the specific case noted above, please note that if you change your encryption method later (i.e. using other modules like a rijndael module), you can't use your current userfile anymore. Put this line into your Eggdrop configuration file to load the server module:: loadmodule pbkdf2 There are also some variables you can set in your config file: set pbkdf2-method "SHA256" Cryptographic hash function used. Use ``openssl list -digest-algorithms`` to view all available options set pbkdf2-rounds 1600 Number of rounds. The higher the number, the longer hashing takes; but also generally the higher the protection against brute force attacks. This module requires: none Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/python.rst000066400000000000000000000032741524730042700252710ustar00rootroot00000000000000Last revised: November 03, 2023 .. _python: ============= 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 -------------- 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 ------------------ Partyline Commands ------------------ ^^^^^^^^^^^^^^^^^^^ python ^^^^^^^^^^^^^^^^^^^ You can run a python command from the partyline with the .python command, such as:: .python 1 + 1 Python: 2 .python from eggdrop.tcl import putmsg; putmsg('#chan', 'Hello world!') Python: None ^^^^^^^^^^^^^ .binds python ^^^^^^^^^^^^^ The python module extends the core ``.binds`` partyline command by adding a ``python`` mask. This command will list all binds for python scripts. ------------ Tcl Commands ------------ ^^^^^^^^^^^^^^^^^^^^^^^ 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. eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/seen.rst000066400000000000000000000011701524730042700246730ustar00rootroot00000000000000Last revised: Mar 14, 2023 .. _seen: =========== Seen Module =========== This module provides very basic seen commands via msg, on channel or via dcc. This module works only for users in the bot's userlist. If you are looking for a better and more advanced seen module, try the gseen module originally written by G'Quann and forked/updated to work with modern Eggdrop by mortmann. You can find it at ``_. This module requires: none Put this line into your Eggdrop configuration file to load the seen module:: loadmodule seen Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/server.rst000066400000000000000000000222531524730042700252540ustar00rootroot00000000000000Last revised: October 25, 2010 .. _server: ============= Server Module ============= This module provides the core server support. You have to load this if you want your bot to come on IRC. Not loading this is equivalent to the old NO_IRC define. This module requires: none Put this line into your Eggdrop configuration file to load the server module:: loadmodule server There are also some variables you can set in your config file: set net-type Efnet What is your network? Possible allowed values are Efnet, IRCnet, Undernet, DALnet, Libera, freenode, Quakenet, Rizon, Other. If the network you use is not listed, using "Other" is a good sane choice and can be customized with settings both here and in the IRC module sections of the config file. set nick "LamestBot" Set the nick the bot uses on IRC, and on the botnet unless you specify a separate botnet-nick, here. set altnick "L?m?stB?t" Set the alternative nick which the bot uses on IRC if the nick specified by 'set nick' is unavailable. All '?' characters will be replaced by random numbers. set keep-nick 1 This setting makes the bot try to get his original nickname back if its primary nickname is already in use. set realname "/msg LamestBot hello" Set here what to display in the real-name field for the bot. :: bind evnt - init-server evnt:init_server proc evnt:init_server {type} { global botnick putquick "MODE $botnick +i-ws" } ... is Tcl scripting that is run immediately after connecting to a server. :: set init-server { putquick "MODE $botnick +i-ws" } ... is Tcl scripting that is run immediately after connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). :: set connect-server { putlog "Connecting to server." } ... is Tcl scripting that is run immediately before connecting to a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). :: set disconnect-server { putlog "Disconnected from server." } ... is a Tcl script that is run immediately after disconnecting from a server. It is limited to 120 characters, and is depriciated due to the EVNT bind (see doc/tcl-commands.doc). :: set servers { you.need.to.change.this:6667 another.example.com:7000:password [2001:db8:618:5c0:263::]:6669:password ssl.example.net:+6697 } ... is the bot's server list. The bot will start at the first server listed, and cycle through them whenever it gets disconnected. You need to change these servers to YOUR network's servers. The format is: server[:port[:password]] Prefix the port with a plus sign to attempt a SSL connection: server:+port[:password] Both the port and password fields are optional; however, if you want to set a password or use SSL you must also set a port. If a port isn't specified it will default to your default-port setting. set default-port 6667 Set the default port which should be used if none is specified with '.jump' or in 'set servers'. set msg-rate 2 Number of seconds to wait between transmitting queued lines to the server. Lower this value at your own risk. ircd is known to start flood control at 512 bytes/2 seconds. set ssl-verify-servers 0 Control certificate verification for servers. You can set this by adding together the numbers for all exceptions you want to enable. By default certificate verification is disabled and all certificates are assumed to be valid. The numbers are the following: The numbers are the following: +---+---------------------------------------------+ | 0 | disable verification | +---+---------------------------------------------+ | 1 | enable certificate verification | +---+---------------------------------------------+ | 2 | allow self-signed certificates | +---+---------------------------------------------+ | 4 | don't check peer common or alt names | +---+---------------------------------------------+ | 8 | allow expired certificates | +---+---------------------------------------------+ | 16| allow certificates which are not valid yet | +---+---------------------------------------------+ | 32| allow revoked certificates | +---+---------------------------------------------+ set server-cycle-wait 60 This setting defines how long Eggdrop should wait before moving from one server to another on disconnect. If you set 0 here, Eggdrop will not wait at all and will connect instantly. Setting this too low could result in your bot being K:Lined. set server-timeout 60 Set here how long Eggdrop should wait for a response when connecting to a server before giving up and moving on to next server. set check-stoned 1 Set this to 1 if Eggdrop should check for stoned servers? (where the server connection has died, but Eggdrop hasn't been notified yet). set serverror-quit 1 If you want your bot to exit the server if it receives an ERROR message, set this to 1. set max-queue-msg 300 Set here the maximum number of lines to queue to the server. If you're going to dump large chunks of text to people over IRC, you will probably want to raise this. 300 is fine for most people though. set quiet-reject 1 This setting makes the bot squelch the error message when rejecting a DCC CHAT, SEND or message command. Normally, Eggdrop notifies the user that the command has been rejected because they don't have access. Note that sometimes IRC server operators detect bots that way. set flood-msg 5:60 Set here how many msgs in how many seconds from one host constitutes a flood. If you set this to 0:0, msg flood protection will be disabled. set flood-ctcp 3:60 Set here how many ctcps in how many seconds from one host are recognized as a flood. Not specifying or using number = 0 will not check against ctcp floods. set answer-ctcp 3 Set how many ctcps should be answered at once here. set lowercase-ctcp 0 If you want your bot to answer lower case ctcp requests (non rfc- compliant), set this setting to 1. mIRC will do this, most other clients will not. set trigger-on-ignore 0 If you want Eggdrop to trigger binds for ignored users, set this to 1. set exclusive-binds 0 This setting configures PUBM and MSGM binds to be exclusive of PUB and MSG binds. This means if a MSGM bind with the mask "*help*" exists and is triggered, any MSG bindings with "help" in their mask will not be triggered. Don't enable this unless you know what you are doing! set double-mode 0 Allow identical messages in the mode queue? set double-server 0 Allow identical messages in the server queue? set double-help 0 Allow identical messages in the help queue? set use-penalties 1 (default on net-type IRCnet) This enables Eggdrop's penalty calculation. Every command Eggdrop sends to the IRC server raises its penalty points. If Eggdrop reaches a server limit, it gets disconnected with "excess flood" message. Eggdrop is able to count internal those penalty points, too and take measures against excess flooding. Note: it's highly advised to turn this on! set optimize-kicks 1 This optimizes the kick queue. It also traces nick changes and parts in the channel and changes the kick queue accordingly. There are three different options for this setting: +---+------------------------------------------------------------------+ | 0 | Turn it off. | +---+------------------------------------------------------------------+ | 1 | Optimize the kick queue by summarizing kicks. | +---+------------------------------------------------------------------+ | 2 | Trace nick changes and parts on the channel and change the queue | | | accordingly. For example, bot will not try to kick users who have| | | already parted the channel. | +---+------------------------------------------------------------------+ ATTENTION: Setting 2 is very CPU intensive. There are additional settings for 'net-type' Efnet. *net-type 5 specific features:* Attention: Use this settings *only* if you set 'net-type' to Efnet! set check-mode-r 1 This settings defines how umode +r is understood by Eggdrop. Some networks use +r to indicate a restricted connection. If this is your case, and you want your bot to leave restricted servers and jump to the next server on its list, then set it to 1. Please note, this setting is automatically set to 0 for net-type of Efnet, Undernet, and DALnet, and set to 1 for net-type IRCNet. set nick-len 9 This setting allows you to specify the maximum nick-length supported by your network. The default setting is 9. The maximum supported length by Eggdrop is 32. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/share.rst000066400000000000000000000034601524730042700250470ustar00rootroot00000000000000Last revised: November 27, 2003 .. _share: ============ Share Module ============ This module provides userfile sharing support between two directly linked bots. This module requires: transfer, channels Put this line into your Eggdrop configuration file to load the share module:: loadmodule share 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. set resync-time 900 This setting specifies how long to hold another bots resync data before flushing it. This is a setting for allow-resync. set private-global 0 When sharing user lists, DON'T ACCEPT global flag changes from other bots? NOTE: The bot will still send changes made on the bot, it just won't accept any global flag changes from other bots. This overrides the private-globals setting (below). set private-globals "mnot" When sharing user lists, if private-global isn't set, which global flag changes from other bots should be ignored? set private-user 0 When sharing user lists, don't accept ANY userfile changes from other bots? Paranoid people should use this feature on their hub bot. This will force all userlist changes to be made via the hub. set override-bots 0 This setting makes the bot discard its own bot records in favor of the ones sent by the hub. NOTE: No passwords or botflags are shared, only ports and address are added to sharing procedure. This only works with hubs that are v1.5.1 or higher. Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/transfer.rst000066400000000000000000000022041524730042700255640ustar00rootroot00000000000000Last revised: November 29, 2023 .. _transfer: =============== Transfer Module =============== The transfer module provides DCC SEND/GET support and userfile transfer support for userfile sharing. This module requires: none Put this line into your Eggdrop configuration file to load the transfer module:: loadmodule transfer There are also some variables you can set in your config file: set max-dloads 3 Set here the maximum number of simultaneous downloads to allow for each user. set dcc-block 0 Set here the block size for dcc transfers. ircII uses 512 bytes, but admits that may be too small. 1024 is standard these days. Set this to 0 to use turbo-dcc (recommended). set xfer-timeout 30 Set here the time (in seconds) to wait before an inactive transfer times out. set sharefail-unlink 1 By default, Eggdrop will abort the linking process if userfile sharing is enabled but the userfile transfer fails. Set this to 0 to keep the bots linked if the userfile transfer fails and retry every minute (both bots must be v1.9.0 or higher). Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/twitch.rst000066400000000000000000000044551524730042700252540ustar00rootroot00000000000000Last revised: April 20, 2020 .. _twitch: ============= Twitch Module ============= This module *attempts* to provide connectivity with the Twitch gaming platform. While Twitch provides an IRC gateway to connect with it's streaming service, it does not claim to (and certainly does not) follow the IRC RFC in any meaningful way. The intent of this module is not to provide the full spectrum of management functions typically associated with Eggdrop; instead it focuses around the following key functions: * Logging of general and Twitch-specific events (raids, blocks, bit donations) * Tracking userstate and roomstate values * Adding Tcl event binds for a number of Twitch events This module requires: irc.mod Put this line into your Eggdrop configuration file to load the twitch module:: loadmodule twitch and set `net-type "Twitch"` in your config file. ----------- Limitations ----------- There are a few things you should know about how Twitch provides service through the IRC gateway that affects how well Eggdrop can function: * Twitch does not broadcast JOINs or PARTs for channels over 1,000 users. This renders tracking users on a channel unreliable. * Twitch does not broadcast MODE changes for moderator status. This (mostly) renders tracking the status of users infeasible. (See Tcl below section for workaround) * Twitch stores bans on its servers (and does not accept MODE +b), making the Eggdrop ban list (and exempts/invites) mostly useless * Twitch does not allow clients to issue MODE +o/-o commands, preventing Eggdrop from op'ing users through the traditional method ... but the good news is, we have extended much of the Twitch functionality to Tcl! ------- Tcl API ------- That last section was a little bit of a downer, but don't worry, we added a TON of functionality to the Tcl API. This module adds binds for the following Twitch events: * CLEARCHAT * CLEARMSG * HOSTTARGET * WHISPER * ROOMSTATE * USERSTATE * USERNOTICE ------------------ Partyline commands ------------------ This module adds the following commands to the partyline: * userstate - Lists current userstate on a channel * roomsstate - Lists current roomstate for a channel * twcmd - Issues a traditional Twitch web interface command to the Twitch server (/ban, /block, /host, etc) Copyright (C) 2020 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/uptime.rst000066400000000000000000000015731524730042700252530ustar00rootroot00000000000000Last revised: Mar 14, 2023 .. _uptime: ------------- Uptime Module ------------- This module reports uptime statistics to the uptime contest web site at ``_. Go look and see what your uptime is! It takes about 9 hours to show up, so if your bot isn't listed, try again later. Information sent to the server includes the bot's uptime, botnet-nick, server, version, and IP address. This information is stored in a temporary logfile for debugging purposes only. The only publicly available information will be the bot's botnet-nick, version and uptime. If you do not wish for this information to be sent, comment out the 'loadmodule uptime' line in your bot's config file. This module requires: server Put this line into your Eggdrop configuration file to load the uptime module:: loadmodule uptime Copyright (C) 2001 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/mod/woobie.rst000066400000000000000000000005771524730042700252370ustar00rootroot00000000000000Last revised: December 31, 2001 .. _woobie: Woobie Module ------------- This is for demonstrative purposes only. If you are looking for starting point in writing modules, woobie is the right thing. This module requires: none Put this line into your Eggdrop configuration file to load the woobie module:: loadmodule woobie Copyright (C) 2000 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/modules/writing.rst000066400000000000000000000235061524730042700246540ustar00rootroot00000000000000.. _writing_module: How to Write an Eggdrop Module ============================== Note: This is for a simple module of 1 source file. 1. Create a src/mod/MODULE.mod directory in your Eggdrop directory (where MODULE is the module name) and cd to it. 2. Copy the file 'Makefile' from src/mod/woobie.mod and replace all occurrences of 'woobie' with your module name. This should ensure that your module gets compiled. 3. Next, you want to create a file called MODULE.c (MODULE is the module name again). 4. You MUST include the following in your source code:: #define MODULE_NAME "module-name" This should be defined to the same name you will be using when you load your module. :: #define MAKING_MODULENAME MODULENAME is the name of your module (MODULE_NAME), but in all caps. :: #include "../module.h" This provides access to Eggdrop's global function table. Examine src/mod/module.h closely to find a list of functions available. :: #include any other standard c header files you might need. Note that stdio.h, string.h, stdlib.h, and sys/types.h are already included. :: Function *global; This variable provides access to all the Eggdrop functions; without it, you can't call any Eggdrop functions (the module won't even load). Module requirements ------------------- In most modules, all functions/variables (except global and MODULE_start) should be static. This will drastically reduce the size of modules on decent systems. Throughout this step, MODULE refers to the module name. Note that "MODULE_NAME" should literally be "MODULE_NAME". MODULE_start ^^^^^^^^^^^^ :: char *MODULE_start(Function *func_table) This function is called when the module is first loaded. There are several things that need to be done in this function :: global = func_table; This allows you to make calls to the global function table. :: module_register(MODULE_NAME, MODULE_table, MAJOR, MINOR); This records details about the module for other modules and Eggdrop itself to access. MAJOR and MINOR are ints, where MAJOR is the module's major version number and MINOR is a minor version number. MODULE_table is a function table (see below). :: module_depend(MODULE_NAME, "another-module", MAJOR, MINOR); This lets Eggdrop know that your module NEEDS "another-module" of major version 'MAJOR' and at least minor version 'MINOR' to run, and hence should try to load it if it's not already loaded. This will return 1 on success, or 0 if it can't be done (at which stage you should return an error). Any other initialization stuff you desire should also be included in this function. See below for various things you can do. You also will need to return a value. Returning NULL implies the module loaded successfully. Returning a non-NULL STRING is an error message. The module (and any other dependent modules) will stop loading and an error will be returned. MODULE_table ^^^^^^^^^^^^ :: static Function *MODULE_table = { MODULE_start, MODULE_close, MODULE_expmem, MODULE_report, any_other_functions, you_want_to_export }; This is a table of functions which any other module can access. The first 4 functions are FIXED. You MUST have them; they provide important module information. MODULE_close () ^^^^^^^^^^^^^^^ :: static char *MODULE_close () This is called when the module is unloaded. Apart from tidying any relevant data (I suggest you be thorough, we don't want any trailing garbage from modules), you MUST do the following: :: module_undepend(MODULE_NAME); This lets Eggdrop know your module no longer depends on any other modules. Return a value. NULL implies success; any non-NULL STRING implies that the module cannot be unloaded for some reason, and hence the bot should not unload it (see the blowfish module for an example). MODULE_expmem ^^^^^^^^^^^^^ :: static int MODULE_expmem () This should tally all memory you allocate/deallocate within the module (using nmalloc, nfree, etc) in bytes. It's used by memory debugging to track memory faults, and it is used by .status to total up memory usage. MODULE_report ^^^^^^^^^^^^^ :: static void MODULE_report (int idx) This should provide a relatively short report of the module's status (for the module and status commands). These functions are available to modules. MANY more available functions can be found in src/mod/module.h. Additional functions ^^^^^^^^^^^^^^^^^^^^ nmalloc """"""" :: void *nmalloc(int j); This allocates j bytes of memory. nfree """"" :: void nfree(void *a); This frees an nmalloc'd block of memory. Context """"""" :: Context; Actually a macro -- records the current position in execution (for debugging). Using Context is no longer recommended, because it uses too many resources and a core file provides much more information. dprintf """"""" :: void dprintf(int idx, char *format, ...) This acts like a normal printf() function, but it outputs to log/socket/idx. idx is a normal dcc idx, or if < 0 is a sock number. Other destinations:: DP_LOG - send to log file DP_STDOUT - send to stdout DP_MODE - send via mode queue to the server DP_SERVER - send via normal queue to the server DP_HELP - send via help queue to server module_entry """""""""""" :: const module_entry *module_find(char *module_name, int major, int minor); Searches for a loaded module (matching major, >= minor), and returns info about it. :: Members of module_entry: char *name; - module name int major; - real major version int minor; - real minor version Function *funcs; - function table (see above) module_rename """"""""""""" :: void module_rename(char *old_module_name, char *new_module_name) This renames a module from old_module_name to new_module_name. add_hook/del_hook """"""""""""""""" :: void add_hook(int hook_num, Function *funcs) void del_hook(int hook_num, Function *funcs) These are used for adding or removing hooks to/from Eggdrop code that are triggered on various events. Valid hooks are:: HOOK_SECONDLY - called every second HOOK_MINUTELY - called every minute HOOK_5MINUTELY - called every 5 minutes HOOK_HOURLY - called every hour (hourly-updates minutes past) HOOK_DAILY - called when the logfiles are switched HOOK_READ_USERFILE - called when the userfile is read HOOK_USERFILE - called when the userfile is written HOOK_PRE_REHASH - called just before a rehash HOOK_REHASH - called just after a rehash HOOK_IDLE - called whenever the dcc connections have been idle for a whole second HOOK_BACKUP - called when a user/channel file backup is done HOOK_LOADED - called when Eggdrop is first loaded HOOK_DIE - called when Eggdrop is about to die module_load/unload """""""""""""""""" :: char *module_unload (char *module_name); char *module_load (char *module_name); Tries to load or unload the specified module; returns 0 on success, or an error message. add/rem_tcl_commands """""""""""""""""""" :: void add_tcl_commands(tcl_cmds *tab); void rem_tcl_commands(tcl_cmds *tab); Provides a quick way to create and remove a table of Tcl commands. The table is in the form of:: {char *func_name, Function *function_to_call} Use { NULL, NULL } to indicate the end of the list. add/rem_tcl_ints """""""""""""""" :: void add_tcl_ints(tcl_ints *); void rem_tcl_ints(tcl_ints *); Provides a quick way to create and remove a table of links from C int variables to Tcl variables (add_tcl_ints checks to see if the Tcl variable exists and copies it over the C one). The format of table is:: {char *variable_name, int *variable, int readonly} Use {NULL, NULL, 0} to indicate the end of the list. add/rem_tcl_strings """"""""""""""""""" :: void add_tcl_strings(tcl_strings *); void rem_tcl_strings(tcl_strings *); Provides a quick way to create and remove a table of links from C string variables to Tcl variables (add_tcl_ints checks to see if the Tcl variable exists and copies it over the C one). The format of table is:: {char *variable_name, char *string, int length, int flags} Use {NULL, NULL, 0, 0} to indicate the end of the list. Use 0 for length if you want a const string. Use STR_DIR for flags if you want a '/' constantly appended; use STR_PROTECT if you want the variable set in the config file, but not during normal usage. add/rem_builtins """""""""""""""" :: void add_builtins(p_tcl_hash_list table, cmd_t *cc); void rem_builtins(p_tcl_hash_list table, cmd_t *cc); This adds binds to one of Eggdrop's bind tables. The format of the table is:: {char *command, char *flags, Function *function, char *displayname} Use {NULL, NULL, NULL, NULL} to indicate the end of the list. This works EXACTLY like the Tcl 'bind' command. displayname is what Tcl sees this function's proc name as (in .binds all). function is called with exactly the same args as a Tcl binding is with type conversion taken into account (e.g. idx's are ints). Return values are much the same as Tcl bindings. Use int 0/1 for those which require 0/1, or char * for those which require a string (auch as filt). Return nothing if no return value is required. putlog """""" :: void putlog (int logmode, char *channel, char *format, ...) Adds text to a logfile (determined by logmode and channel). This text will also output to any users' consoles if they have the specified console mode enabled. What to do with a module? ------------------------- If you have written a module and feel that you wish to share it with the rest of the Eggdrop community, find us in #eggdrop on Libera. Make sure you have a nice descriptive text (modulename.desc) to describe it, and make sure to mention in your text file which version Eggdrop the module is written for. eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/000077500000000000000000000000001524730042700230075ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/firstscript.rst000066400000000000000000000253411524730042700261220ustar00rootroot00000000000000============================= Writing an Eggdrop Tcl Script ============================= So you want to write an Eggdrop Tcl script, but you don't really know where to begin. This file will give you a very basic idea about what Eggdrop scripting is like, using a *very* simple script that may help you get started with your own scripts. This guide assumes you know a bit about Eggdrops and IRC. You should have already installed Eggdrop. The bot should not be on any important or busy channels (development bots can be annoying if your script has bugs). If you plan on doing a lot of development, enable the .tcl and .set commands, and make sure nobody else has access to your bot. The .tcl and .set commands are helpful in debugging and testing your code. First, read through the script. Very few commands are listed here intentionally, but as you want to develop more advanced scripts, you will definitely want to get familiar with the `core Tcl language commands `_, especially the string- and list-related commands, as well as Eggdrop's own library of custom Tcl commands, located in `tcl-commands.doc `_ If you have the .tcl command enabled, you can load a script by typing '.tcl source script/file.tcl' to load it. Otherwise, add it to your config file like normal (examples to do so are at the bottom of the config file) and '.rehash' or '.restart' your bot. Let's look at a very basic example script that greets users as they join a channel:: # GreetScript.tcl # Version: 1.0 # Author: Geo # # Description: # A simple script that greets users as they join a channel # ### Config Section ### # How would you like the bot to gree users? # 0 - via public message to channel # 1 - via private message set pmsg 0 # What message would you like to send to users when they join? set greetmsg "Hi! Welcome to the channel!" ### DO NOT EDIT BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING! ### bind join - * greet proc greet {nick uhost hand chan} { global pmsg global greetmsg if {$pmsg} { putserv "PRIVMSG $nick :$greetmsg" } else { putserv "PRIVMSG $chan :$greetmsg" } } putlog "greetscript.tcl v1.0 by Geo" Whew! There's a lot going on here. You'll generally see scripts broken into a few key parts- the header, the config section, and the code section. Ok, let's go over this piece by piece. First, the header of the script:: # GreetScript.tcl # Version: 1.0 # Author: Geo or #eggdrop on Libera # # Description: # A simple script that greets users as they join a channel Any line prefixed by a # means it is comment, and thus ignored. You can type whatever you want, and it won't matter. When writing scripts (especially if you want to give them to other people, it is good to use comments in the code to show what you're doing. Here though, we use it to describe what the script is and, most importantly, who wrote it! Let's give credit where credit is due, right? You may want to give users a way to contact you as well. Next, let's look at the configuration section:: ### Config Section ### # How would you like the bot to gree users? # 0 - via public message to channel # 1 - via private message set pmsg 0 # What message would you like to send to users when they join? set greetmsg "Hi! Welcome to the channel!" ### DO NOT EDIT BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING! ### To make scripts easy to use, you'll want to have a section that allows users to easily change the way the script operates, without having to edit the script itself. Here, we have two settings: one that controls which method the Eggdrop uses to greet a user, and a second with the message to greet the user with. Sure, we could hard-code that into the code section below, but in larger scripts that makes things harder to find, and also forces you to potentially have to make the same change multiple times in code. Why not make it simple and do it once, up top? Notice the settings do not have #s in front of them- they are variables that will be used by the script later on. And of course, the standard ominous warning not to change anything below! Now, let's look start to dissect the actual code!:: bind join - * greet This is a bind. This sets up an action that Eggdrop will react to. You can read `all the binds that Eggdrop uses here. `_ Generally, we like to place all binds towards the top of the script so that they are together and easy to find. Now, let's look at documentation of the bind join together. +----------------------------------------------------------------------------------------------------------------------------------------------------------+ | bind JOIN | +============================+=============================================================================================================================+ | bind join | +----------------------------------------------------------------------------------------------------------------------------------------------------------+ | procname | +----------------------------------------------------------------------------------------------------------------------------------------------------------+ | Description: triggered by someone joining the channel. The mask in the bind is matched against "#channel nick!user@host" and can contain wildcards. | +----------------------------------------------------------------------------------------------------------------------------------------------------------+ So here, we can start to match the bind listed in the code to how it is described in the documentation. The first term after the bind command is 'join', showing that it is a join bind, which means the action we define here will take place each time a user joins a channel. The next term is 'mask', and it says it is in the form "#channel nick!user@host". This is where we can start to refine exactly when this bind is triggered. If we want it to work for every person joining every channel Eggdrop is on, then a simple '*' will suffice here- that will match everything. If we wanted this bind to only work in #foo, then the mask would be "#foo \*". If we wanted to greet users on every channel, but only those who are on AOL, the mask would be "* \*!*@*.aol.com". Finally the 'proc' argument is the name of the Tcl proc we want to call, where the code that actually *does stuff* is located. So to sum up this line from the example script: When a user joins on any channel and with any hostmask, run the code located in proc 'greet'. Now that we told the Eggdrop what action to look for, we need to tell it what to do when that action occurs!:: proc greet {nick uhost hand chan} { This is how we declare a Tcl proc. As we said above, this is where the magic happens. To set up the proc (this will look differently for different binds), lets refer back to the bind JOIN documentation. The second line shows ``procname ``. Eggdrop does a lot of stuff in the background when a bind is triggered, and this is telling you how Eggdrop will present that information to you. Here, Eggdrop is telling you it is going to pass the proc you created four variables: One that contains the nickname of the person who triggered the bind (in this case, the user who joined), the user@host of that user, the handle of that user (if the user has one on the bot), and the channel that the bind was triggered on. So let's say someone with the nickname Geo with a hostmask of awesome@aol.com joined #sexystuff and that person is not added to the bot as a user. Eggdrop will pass 4 values to the variables you set up in that proc: The first variable will get the value "Geo", the second "awesome@aol.com", the third "*", and the fourth "#sexystuff". (That third value was a trick, we didn't talk about that- if the user is not added to the bot, handle will get a "*" as a value). Now, let's use those variables!:: global pmsg global greetmsg This is a simple one- because we're using variables declared in the main body of the script (remember way up top?), we have to tell this proc to use that variable, not not create a new local variable for this proc. And finally, let's actually send a message to the user:: if {pmsg} putserv "PRIVMSG $nick :$greetmsg" } else { putserv "PRIVMSG $chan :$greetmsg" } Here, we're going to check if pmsg is true (any value that is not 0) and, if yes, send a private message to the user. If pmsg is not true (it is 0), then we will send the message to the channel. You can see that the first putserv message sends a PRIVMSG message to $nick - this is the nickname of the user that joined, and that Eggdrop stored for us in the first variable of the proc, which we called 'nick'. The second putserv message will send a PRIVMSG message to the $chan - this is the channel the user joined, and that Eggdrop stored for us in the fourth variable of the proc, which we called 'chan'. And finally: get the credit you deserve when the script loads!:: putlog "greetscript.tcl v1.0 by Geo" Like your variables at the top of the script, this line is not inside a Tcl proc and will execute when the script is loaded. You can put this or any other initialization code you want to run. And there you have it- your first script! Take this, modify it and experiment. A few challenges for you: * How can you configure which channel it should run on, without hard-coding it into the bind? (Maybe with a variable...) * How can you configure it to only message a user with the nickname "FancyPants"? (Sounds like something a bind could handle) * How can you delay the message from sending by 5 seconds? (Hint: utimer) * How can you send different messages to different channels? (A new setting may be in order...) * How can you get the bot to not greet itself when it joins the channel? (Eggdrop stores its own nickname in a variable called $botnick) * How can you add the person joining the channel's nickname to the greet message? (You can put variables inside variables...) If you want to try these out, join #eggdrop on Libera and check your answers (and remember, there are dozens of ways to do this, so a) don't be defensive of your choices, and b) take others' answers with a grain of salt!) .. |br| raw:: html
Copyright (C) 2003 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/firststeps.rst000066400000000000000000000241171524730042700257540ustar00rootroot00000000000000Common First Steps ================== Log on to the partyline ----------------------- Now that your bot is online, you'll want to join the partyline to further use the bot. First, read what it tells you when you started it up:: STARTING BOT IN USERFILE CREATION MODE. Telnet to the bot and enter 'NEW' as your nickname. OR go to IRC and type: /msg BotNick hello This will make the bot recognize you as the master. You can either connect to the bot via telnet (unencrypted), connect to the bot using DCC Chat (unencrypted), connect to the bot using SCHAT (encrypted), or use TLS-compatible socket client (encrypted). Telnet ^^^^^^ To telnet, you'll either need a program like Putty (Windows), or you can do it from the command line of your shell using the telnet command:: telnet You can find the IP and port the bot is listening on by a) remembering what you set in the config file ;) or b) reading the display the bot presented when it started up. Look for a line that looks similar to this:: Listening for telnet connections on 2.4.6.9:3183 (all). This tells you that the bot is listening on IP 2.4.6.9, port 3183. If you see 0.0.0.0 listed, that means Eggdrop is listening on all available IPs on that particular host. DCC Chat ^^^^^^^^ To use a DCC chat connection in your IRC client, you can either ``/dcc chat BotNick`` or ``/ctcp BotNick chat``. SCHAT ^^^^^ SCHAT (Secure Chat) is a sub-protocol of Secure Direct Client-to-Client (SDCC) that enables encrypted TLS sessions between two users. KVIrc and AdiIRC are examples of IRC clients that support the SCHAT protocol. If TLS is configured for your Eggdrop, you can connect via ``/ctcp bot schat``. Direct TLS ^^^^^^^^^^ To use a client such as openssl to wrap your connection with TLS, please read the `TLS docs `_ regarding partyline connection. There are also other clients that can connect to the partyline with TLS, like `pefero `_, maintained by Eggdrop contributor mortmann. If one of those methods does not work for you, try another. Once you're on the bot for the first time, type ``.help`` for a short list of available commands, or ``.help all`` for a more thorough list. Common first steps ------------------ To learn more about any of these commands, type .help on the partyline. It will provide you the syntax you need, as well as a short description of how to use the command. Join a Channel ^^^^^^^^^^^^^^ To tell the Eggdrop to join a channel, use:: .+chan #channel Add a User ^^^^^^^^^^ To add a user to the bot, use:: .+user Add a Host to a User ^^^^^^^^^^^^^^^^^^^^ The handle is the name that the bot uses to track a user. No matter what nickname on IRC a user uses, a single handle is used to track the user by their hostmask. To add a hostmask of a user to a handle, use:: .+host where the hostmask is in the format of !@hostname.com . Wildcards can be used; common formats are \*!\*@hostname.com for static hosts, or \*!ident@*.foo.com for dynamic hostnames. Assign Permission Flags ^^^^^^^^^^^^^^^^^^^^^^^ To assign an access level to a user, first read ``.help whois`` for a listing of possible access levels and their corresponding flags. Then, assign the desired flag to the user with:: .chattr <+flag> So to grant a user the voice flag, you would do:: .chattr handle +v It is important to note that, when on the partyline, you want to use the handle of the user, not their current nickname. Configure Channel Settings ^^^^^^^^^^^^^^^^^^^^^^^^^^ Finally, Eggdrop is often used to moderate and control channels. This is done via the ``.chanset`` command. To learn more about the (numerous!) settings that can be used to control a channel, read:: .help chaninfo Common uses involve setting channels modes. This can be done with the chanmode channel setting:: .chanset #channel chanmode +snt which will enforce the s, n, and t flags on a channel. Automatically restarting an Eggdrop ----------------------------------- A common question asked by users is, how can I configure Eggdrop to automatically restart should it die, such as after a reboot? Historically, Eggdrop relied on the host's crontab system to run a script (called botchk) every ten minutes to see if the eggdrop is running. If the eggdrop is not running, the script will restart the bot, with an optional email sent to the user informing them of the action. Newer Linux systems come with systemd, which can provide better real-time monitoring of processes such as Eggdrop. You probably want to use systemd if your system has it. Crontab Method ^^^^^^^^^^^^^^ 1. Enter the directory you installed your Eggdrop to. Most commonly, this is ~/eggdrop (also known as /home//eggdrop). 2. Just humor us- run ``./scripts/autobotchk`` without any arguments and read the options available to you. They're listed there for a reason! 3. If you don't want to customize anything via the options listed in #2, you can install a crontab job to start Eggdrop simply by running:: ./scripts/autobotchk yourEggdropConfigNameHere.conf 4. Review the output of the script, and verify your new crontab entry by typing:: crontab -l By default, it should create an entry that looks similar to:: 0,10,20,30,40,50 * * * * /home/user/bot/scripts/YourEggdrop.botchk 2>&1 This will run the generated botchk script every ten minutes and restart your Eggdrop if it is not running during the check. Also note that if you run autobotchk from the scripts directory, you'll have to manually specify your config file location with the -dir option. To remove a crontab entry, use ``crontab -e`` to open the crontab file in your system's default editor and remove the crontab line. Systemd Method (Newer Linux Systems) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. Enter the directory you installed your Eggdrop to. Most commonly, this is ~/eggdrop (also known as /home//eggdrop). 2. Install the systemd job for Eggdrop simply by running:: ./scripts/autobotchk yourEggdropConfigNameHere.conf -systemd 3. Note the output at the end of the script informing you of the command you can use to start/stop the Eggdrop in thee future. For example, to manually start the Eggdrop, use:: systemctl --user start .service To stop Eggdrop, use:: systemctl --user stop .service To rehash (not reload) Eggdrop, use:: systemctl --user reload .service (Yes, we acknowledge the confusion that the systemd reload command will execute the Eggdrop '.rehash' command, not the '.reload' command. Unfortunately, systemd did not consult us when choosing its commands!) To prevent Eggdrop from automatically running after a system start, use:: systemctl --user disable .service To re-enable Eggdrop automatically starting after a system start, use:: systemctl --user enable .service Authenticating with NickServ ---------------------------- Many IRC features require you to authenticate with NickServ to use them. You can do this from your config file by searching for the line:: # putserv "PRIVMSG NickServ :identify " in your config file. Uncomment it by removing the '#' sign and then replace with your password. Your bot will now authenticate with NickServ each time it joins a server. Setting up SASL authentication ------------------------------ Simple Authentication and Security Layer (SASL) is becoming a prevalant method of authenticating with IRC services such as NickServ prior to your client finalizing a connection to the IRC server, eliminating the need to /msg NickServ to identify yourself. In other words, you can authenticate with NickServ and do things like receive a cloaked hostmask before your client ever appears on the IRC server. Eggdrop supports three methods of SASL authentication, set via the sasl-mechanism setting: * **PLAIN**: To use this method, set sasl-mechanism to 0. This method passes the username and password (set in the sasl-username and sasl-password config file settings) to the IRC server in plaintext. If you only connect to the IRC server using a connection protected by SSL/TLS this is a generally safe method of authentication; however you probably want to avoid this method if you connect to a server on a non-protected port as the exchange itself is not encrypted. * **ECDSA-NIST256P-CHALLENGE**: To use this method, set sasl-mechanism to 1. This method uses a public/private keypair to authenticate, so no username/password is required. Not all servers support this method. If your server does support this, you you must generate a certificate pair using:: openssl ecparam -genkey -name prime256v1 -out eggdrop-ecdsa.pem You will need to determine your public key fingerprint by using:: openssl ec -noout -text -conv_form compressed -in eggdrop-ecdsa.pem | grep '^pub:' -A 3 | tail -n 3 | tr -d ' \n:' | xxd -r -p | base64 Then, authenticate with your NickServ service and register your public certificate with NickServ. You can view your public key On Libera for example, it is done by:: /msg NickServ set pubkey * **EXTERNAL**: To use this method, set sasl-mechanism to 2. This method allows you to use other TLS certificates to connect to the IRC server, if the IRC server supports it. An EXTERNAL authentication method usually requires you to connect to the IRC server using SSL/TLS. There are many ways to generate certificates; one such way is generating your own certificate using:: openssl req -new -x509 -nodes -keyout eggdrop.key -out eggdrop.crt You will need to determine your public key fingerprint by using:: openssl x509 -in eggdrop.crt -outform der | sha1sum -b | cut -d' ' -f1 Then, ensure you have those keys loaded in the ssl-privatekey and ssl-certificate settings in the config file. Finally, to add this certificate to your NickServ account, type:: /msg NickServ cert add * **SCRAM-SHA-256**: To use this method, set sasl-mechanism to 3. * **SCRAM-SHA-512**: To use this method, set sasl-mechanism to 4. eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/module.rst000066400000000000000000000232171524730042700250330ustar00rootroot00000000000000Writing a Basic Eggdrop Module ============================== An Eggdrop module is a piece of C code that can be loaded (or unloaded) onto the core Eggdrop code. A module differs from a Tcl script in that modules must be compiled and then loaded, whereas scripts can be edited and loaded directly. Importantly, a module can be written to create new Eggdrop-specific Tcl commands and binds that a user can then use in a Tcl script. For example, the server module loaded by Eggdrop is what creates the "jump" Tcl command, which causes tells the Eggdrop to jump to the next server in its server list. There are a few required functions a module must perform in order to properly work with Eggdrop Module Header ------------- A module should include license information. This tells other open source users how they are allowed to use the code. Eggdrop uses GPL 2.0 licensing, and our license information looks like this:: /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ Required Code ------------- For this section, you don't necessarily need to understand what it is doing, but this code is required for a module to function. If you want to learn more about this, check out :ref:`writing_module` You'll next want to name your module:: #define MODULE_NAME "woobie" Declare your own function tables (again, you don't need to understand this part; you just need to copy/paste it):: #undef global static Function *global = NULL, *server_funcs = NULL; EXPORT_SCOPE char *woobie_start(); Next are two memory-related functions used by the core Eggdrop .status and .module commands:: static int woobie_expmem() { int size = 0; return size; } static void woobie_report(int idx, int details) { if (details) { int size = woobie_expmem(); dprintf(idx, " Using %d byte%s of memory\n", size, (size != 1) ? "s" : ""); } } This function is called when Eggdrop loads the module:: char *woobie_start(Function *global_funcs) { global = global_funcs; /* Register the module. */ module_register(MODULE_NAME, woobie_table, 2, 1); /* ^--- minor module version * ^------ major module version * ^-------------------- module function table * ^--------------------------------- module name */ if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) { module_undepend(MODULE_NAME); return "This module requires Eggdrop 1.8.0 or later."; } This next function is used to unload the module:: static char *woobie_close() { module_undepend(MODULE_NAME); return NULL; } This creates a function table that is exported to Eggdrop. In other words, these are commands that are made available to the Eggdrop core and other modules. At minimum, the following functions must be exported:: static Function woobie_table[] = { (Function) woobie_start, (Function) woobie_close, (Function) woobie_expmem, (Function) woobie_report, }; At this point, you should have a module that compiles and can be loaded by Eggdrop- but doesn't really do anything yet. We'll change that in the next section! Adding a Partyline Command -------------------------- A partyline command function accepts three arguments- a pointer to the user record of the user that called the command; the idx the user was on when calling the command; and a pointer to the arguments appended to the command. A command should immediately log that it was called to the LOG_CMDS log level, and then run its desired code. This simple example prints "WOOBIE" to the partyline idx of the user that called it:: static int cmd_woobie(struct userrec *u, int idx, char *par) { putlog(LOG_CMDS, "*", "#%s# woobie", dcc[idx].nick); dprintf(idx, "WOOBIE!\n"); return 0; } If you add partyline commands, you need to create a table which links the new command name to the function it should call. This can be done like so:: static cmd_t mywoobie[] = { /* command flags function tcl-name */ {"woobie", "", cmd_woobie, NULL}, {NULL, NULL, NULL, NULL} /* Mark end. */ }; The tcl-name field can be a name for a Tcl command that will also call the partyline command, or it can be left as NULL. Adding a Tcl Command -------------------- Eggdrop uses the Tcl C API library to interact with the Tcl interpreter. Learning this API is outside the scope of this tutorial, but this example Tcl command will echo the provided argument:: static int tcl_echome STDVAR { BADARGS(2, 2, " arg"); if (strcmp(argv[1], "llama") { Tcl_AppendResult(irp, "You said: ", argv[1], NULL); return TCL_OK; } else { Tcl_AppendResult(irp, "illegal word!"); return TCL_ERROR; } } A few notes on this example. BADARGS is a macro that checks the input provided to the Tcl command. The first argument BADARGS accepts is the minimum number of parameters the Tcl command must accept (including the command itself). The second argument is the maximum number of parameters that BADARGS will accept. The third argument is the help text that will be displayed if these boundaries are exceeded. For example, BADARGS(2, 4, " name ?date? ?place?") requires at least one argument to be passed, and a maximum of three arguments. Eggdrop code style is to enclose optional arguments between qusetion marks in the help text. Similar to adding a partyline command, you also have to create a function table for a new Tcl command:: static tcl_cmds mytcl[] = { {"echome", tcl_echome}, {NULL, NULL} /* Required to mark end of table */ }; And now the newly-created Tcl command 'echome' is available for use in a script! Adding a Tcl Bind ----------------- A Tcl bind is a command that is activated when a certain condition is met. With Eggdrop, these are usually linked to receiving messages or other IRC events. To create a bind, you must first register the bind type with Eggdrop when the module is loaded (you added the woobie_start() and woobie_close functions earlier, you still need all that earlier code in here as well):: static p_tcl_bind_list H_woob; ... char *woobie_start(Function *global_funcs) { ... H_woob = add_bind_table("woobie", HT_STACKABLE, woobie_2char); } And then remove the binds when the module is unloaded:: static char *woobie_close() { ... del_bind_table(H_woob); } Here, "woobie" is the name of the bind (similar to the PUB, MSG, JOIN types of binds you already see in tcl-commands.doc). HT_STACKABLE means you can have multiple binds of this type. "woobie_2char" defines how many arguments the bind will take, and we'll talk about that next. Defining bind arguments ^^^^^^^^^^^^^^^^^^^^^^^ The following code example defines a bind that will take two arguments:: static int woobie_2char STDVAR { Function F = (Function) cd; BADARGS(3, 3, " nick chan"); CHECKVALIDITY(woobie_2char); F(argv[1], argv[2]); return TCL_OK; } And this example defines a bind that will take three arguments:: static int woobie_3char STDVAR { Function F = (Function) cd; BADARGS(4, 4, " foo bar moo"); CHECKVALIDITY(woobie_3char); F(argv[1], argv[2], argv[3]); return TCL_OK; } Like before, BADARGS still checks that the number of arguments passed is correct, and outputs help text if it is not. The rest is boilerplate code to pass the arguments when the bind is called. Calling the Bind ^^^^^^^^^^^^^^^^ To call the bind, Eggdrop coding style is to name that function "check_tcl_bindname". So here, whenever we reach a point in code that should trigger the bind, we'll call check_tcl_woobie() and pass the arguments we defined- in this case, two arguments that woobie_2char was created to handle. Here is some sample code:: check_tcl_woobie(chan, nick); static int check_tcl_woobie(char *chan, char *nick, char *userhost) { int x; char mask[1024]; struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; snprintf(mask, sizeof mask, "%s %s!%s", chan, nick, userhost); Tcl_SetVar(interp, "_woob1", nick ? (char *) nick : "", 0); Tcl_SetVar(interp, "_woob2", chan, 0); x = check_tcl_bind(H_woob, mask, &fr, " $_woob1 $_woob2", MATCH_MASK | BIND_STACKABLE); return (x == BIND_EXEC_LOG); } Now that we have encountered a condition that triggers the bind type (in code by calling ``check_tcl_woobie()`` ), we need to check it against the binds the user has loaded in scripts and see if it matches those conditions. This is done with ``check_tcl_bind()``, called with the bind type, the userhost of the user, the flag record of the user if it exists, the bind arguments, and bind options. We can configure how we want to check the triggering action against the bind, and we can further use the return value from ``check_tcl_bind()`` to take additional action by Eggdrop. You can read more about the specific values used in ``check_tcl_bind`` in :ref:`triggering_any_bind` eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/pythonscript.rst000066400000000000000000000121341524730042700263100ustar00rootroot00000000000000================================ Writing an Eggdrop Python Script ================================ So you want to write an Eggdrop python script, but you don't really know where to begin. This file will give you a very basic idea about what Eggdrop python scripting is like, using a *very* simple script that may help you get started with your own scripts. This guide assumes you know a bit about Eggdrops and, because it reimplements those commands, Tcl. You should have already installed Eggdrop. The bot should not be on any important or busy channels (development bots can be annoying if your script has bugs). If you plan on doing a lot of development, enable the .python commands, and make sure nobody else has access to your bot. First, read through the script below. Very few commands are listed here intentionally, but as you want to develop more advanced scripts, you will definitely want to get familiar with the `Eggdrop custom Tcl commands `_, as Python reimplements them. If you have the .python command enabled, you can load a script by typing '.pysource script/file.tcl' to load it. Otherwise, add it to your config file like normal (examples to do so are at the bottom of the config file) and '.rehash' or '.restart' your bot. Let's look at a very basic example script that greets users as they join a channel:: from eggdrop import bind from eggdrop.tcl import putmsg def joinGreetUser(nick, host, handle, channel, **kwargs): putmsg(channel, f"Hello {nick}, welcome to {channel}") def joinGreetOp(nick, host, handle, channel, **kwargs): putmsg(channel, f"{nick} is an operator on this channel!") if 'GREET_BINDS' in globals(): for greetbind in GREET_BINDS: greetbind.unbind() del GREET_BINDS GREET_BINDS = list() GREET_BINDS.append(bind("join", "*", "*", joinGreetUser)) GREET_BINDS.append(bind("join", "o", "*", joinGreetOp)) print('Loaded greet.py') Whew! There's a lot going on here. You'll generally see scripts broken into a few key parts- the header, the config section, and the code section. Ok, let's go over this piece by piece. Any line prefixed by a # means it is comment, and thus ignored. You can type whatever you want, and it won't matter. When writing scripts (especially if you want to give them to other people, it is good to use comments in the code to show what you're doing. Because developers are lazy and don't want to manually reimplement every single Tcl command in Python code, Python includes a Tcl module that calls Tcl command via Python:: from eggdrop import bind from eggdrop.tcl import putmsg You can load the entire Tcl library by running ``import eggdrop.tcl``, or (the better solution to save memory) is to only load the commands you need via something like ``from eggdrop.tcl import putmsg``. The one exception to be aware of here is the bind command- you always want to load the custom python implementation of that so that it called a python bind, not a Tcl proc. Do this via ``from eggdrop import bind``. Next, let's create some functions that the main script will call:: def joinGreetUser(nick, host, handle, channel, **kwargs): putmsg(channel, f"Hello {nick}, welcome to {channel}") def joinGreetOp(nick, host, handle, channel, **kwargs): putmsg(channel, f"{nick} is an operator on this channel!") Above, we create a function called joinGreetUser and a function called joinGreetOp. This function calls the putmsg Tcl. Note that the arguments here are slightly different than Tcl, requiring a comma instead of a space between the arguments. We include the next code segment due to a limitation of Eggdrop's Python module. Currently, if Eggdrop is rehashed, previously existing binds will be duplicated instead of being overwritten. This is example code checks for previously-existing binds after the script reloaded and deletes them:: if 'GREET_BINDS' in globals(): for greetbind in GREET_BINDS: greetbind.unbind() del GREET_BINDS As part of the above code, we need to track the binds. To do that, the GREET_BINDS list is created, and then we add the binds to it:: GREET_BINDS = list() GREET_BINDS.append(bind("join", "*", "*", joinGreetUser)) # Again, arguments are separated with a comma. This bind requires the 'o' flag to be triggered. GREET_BINDS.append(bind("join", "o", "*", joinGreetOp)) Note that you must call binds at the end of a script (not the top, like we do in Tcl), because the functions must be defined before being called. And again, we create the binds using comma-separated arguments, not spaces like Tcl. ``GREET_BINDS`` is created as a JOIN bind that is triggered when a user with an operator flag joins. When it is triggered, it called the function joinGreetOp that we defined at the beginning of the script. And lastly, our lovely ``print('Loaded greet.py')`` line. This is a normal python command, nothing special about it- it will only print to standard output (ie, if you're running in a terminal). If you want to print to something like the partyline, you'd want to import and then use ``putlog("Loaded greet.py``. Copyright (C) 2003 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/setup.rst000066400000000000000000000403711524730042700247060ustar00rootroot00000000000000Setting Up Eggdrop ================== *This guide was based off perhaps the most helpful Eggdrop website ever written, egghelp.org by slennox. As happens with life, slennox has moved on and no longer updates it, but the information here is still incredibly useful. We have updated his setup page here and hope it will continue to prove useful to our users* Prerequisites ------------- Make sure Tcl AND it's dev packages are installed on your system. On Debian-based systems, this is done with:: sudo apt-get install tcl tcl-dev It is also STRONGLY recommended to have openssl installed, to enable SSL/TLS protection:: sudo apt-get install openssl libssl-dev The super-short version ----------------------- You can read the `Installation`_ section for a more detailed explanation of these steps. #. Download |dlink| to your shell via FTP, or simply type: :substitution-code:`wget geteggdrop.com -O eggdrop-|fullversion|.tar.gz` #. Next, from the commandline of the shell, ensure you are in the same directory that you downloaded the tar file into #. Type: :substitution-code:`tar zxvf eggdrop-|fullversion|.tar.gz` #. Type: :substitution-code:`cd eggdrop-|fullversion|` #. Type ``./configure`` #. Type ``make config`` #. Type ``make`` #. Type ``make install`` #. Type ``cd ~/eggdrop`` #. For a quick start, edit the eggdrop-basic.conf file. To take advantage of all Eggdrop's features, we recommend using eggdrop.conf instead. It is also a good idea to rename the file to something easy to remember and specific to your bot, like botnick.conf. #. Type ./eggdrop -m Getting the source ------------------ History ~~~~~~~ The current supported version of Eggdrop is the |majversion|.x tree. Only the current major version (|majversion|.x) is supported; earlier major versions are not. The most current version of Eggdrop, and the one appropriate for most users, is the current |majversion| series. It added many features such as SASL support, multi-ip listening, and a new password hashing module. It is the most complete, feature-rich, and functional version of Eggdrop. If you're just starting out with Eggdrop, you should use |version|. Prior to that, the 1.8 series added several major features, to include IPv6 support and SSL/TLS connections. 1.6.21, which is now over 10 years old, was the last release of the 1.6 series. Because Eggdrop remained at the 1.6.21 patchlevel for several years it is still often run by users who have become comfortable with that version and don't wish to move to a newer version- however, it is getting harder and harder to continue running 1.6 bots on modern Linux systems. Tcl scripts written for bots as far back as the 1.6 series generally work on all later versions of Eggdrop as well, so if you haven't already- upgrade! The |majversion| Eggdrop tree is currently under active development and the most recent changes are available in daily snapshots for users to download for testing. While the development snapshot will contain the most current, up-to-date features of Eggdrop, it is not yet considered stable and users stand a higher chance of encountering bugs during use. If you do use it and find a bug, it is highly encouraged to report it via the `Eggheads GitHub issues page. `_ Download locations ~~~~~~~~~~~~~~~~~~ The developers distribute Eggdrop via two main methods: FTP, and GitHub. For FTP, it is packaged in tarball format (with the .tar.gz filename extension), with the version number in the filename. The Eggdrop |version| source, for example, would be named :substitution-code:`eggdrop-|fullversion|.tar.gz`. `The Eggheads FTP `_ is a repository for |dlink|, as well as the most current development snapshot and previous stable releases. Eggdrop also maintains a `GitHub page `_ where you can download the development snapshot or a stable version, via either git commandline or by downloading a tarball. To download via git, type ``git clone https://github.com/eggheads/eggdrop.git``, then ``cd eggdrop``. This gives you the development version. To switch to the most recent stable version, type :substitution-code:`git checkout stable/|majversion|`. You can then skip to step 4 in the Installation section below. Installation ------------ Installing Eggdrop is a relatively simple process provided your shell has the required tools for successful compilation. On most commercial shell accounts which allow Eggdrop bots you won't have any problems with installation, but on some private boxes or a shell on your ISP you may experience errors during compilation. Below is a step by step guide to the installation process. These instructions apply to |majversion| bots. It assumes you will be installing :substitution-code:`eggdrop-|fullversion|.tar.gz`, so just change the numbers if you are installing another version. 1. Put the Eggdrop source on your shell using one of the specified download locations, either by downloading |dlink| to your local system and then uploading it to the shell via FTP, or downloading it directly to the shell via the shell's FTP client, git, wget, or curl. You don't need to put the .tar.gz file in its own directory (it'll be done automatically in the next step). 2. SSH to the shell (if you haven't already), and type :substitution-code:`tar zxvf eggdrop-|fullversion|.tar.gz` (if this doesn't work, try :substitution-code:`gunzip eggdrop-|fullversion|.tar.gz` then :substitution-code:`tar xvf eggdrop-|fullversion|.tar`). This will extract the Eggdrop source into its installation directory, named :substitution-code:`eggdrop-|fullversion|`. 3. Type :substitution-code:`cd eggdrop-|fullversion|` switch to the directory the Eggdrop source was extracted to. 4. Type ``./configure`` (that's a period followed by a slash followed by the word 'configure'). This makes sure the shell has all the right tools for compiling Eggdrop, and helps Eggdrop figure out how to compile on the shell. 5. When configure is done, type ``make config``. This sets up which modules are to be compiled. For a more efficient installation, you can use ``make iconfig`` to select the modules to compile, but if you're not sure just use make config. 6. Type ``make``. This compiles the Eggdrop. The process takes a brief moment on fast systems, longer on slow systems. 7. Type ``make install DEST=~/botdir``. This will install Eggdrop into a directory named 'botdir' in your home directory. You can change 'botdir' to anything you like. Note that in some cases you may need to specify the full path, e.g. ``make install DEST=/home/cooldude/botdir``, using the ~ character in make install won't always work. You can get the full path by typing ``pwd``. 8. You can safely delete the installation directory named :substitution-code:`eggdrop-|fullversion|` (to do this, type ``cd ~`` then :substitution-code:`rm -rf eggdrop-|fullversion|`) that was created previously, although some people may find it handy to keep that directory for performing additional or future installations of the same version without recompiling. That's it! Eggdrop is now installed into its own directory on the shell. It's time to edit the configuration files to make Eggdrop work the way you want it to. Configuration ------------- You will need to edit the configuration file before you can start up your Eggdrop. You can find the example configuration file in the directory you extracted the Eggdrop source to, under the name 'eggdrop.conf'. If you downloaded Eggdrop to your system, you can unzip the tarball (.tar.gz) file to its own directory using 7-Zip or a similar program, and view the example config file, botchk file, and all the documentation files locally. You can use Notepad to edit these files, although it's sometimes desirable to use an editor that supports the Unix file format such as EditPlus. To edit the file once it is on your shell, a program such as 'nano' or 'vim' is recommended. Editing the config file ~~~~~~~~~~~~~~~~~~~~~~~ Eggdrop comes with two versions of the configuration file- eggdrop.conf and eggdrop-basic.conf. While it is recommended that users edit a copy of eggdrop.conf to take advantage of all the features Eggdrop has to offer, using eggdrop-basic.conf to start will be a quicker path for some. Still, it is recommended that you come back to the full config file at some point to see what you are missing. It is first recommended to rename the sample config to something other than "eggdrop.conf". Giving it the name of the bot's nick (e.g. NiceBot.conf) is quite common. In the config file, you set up the IRC servers you want the bot to use and set Eggdrop's options to suit your needs. Eggdrop has many options to configure, and editing the configuration file can take some time. I recommend you go over the entire config file to ensure the bot will be configured properly for your needs. All of the options in the config file have written explanations - be sure to read them carefully. Some of them can be a little bit vague, though. To comment out a line (prevent the bot from reading that line), you can add a '#' in front of a line. When you come to a line that you need to edit, one popular option is to comment out the original and add your new line right below it. This preserves the original line as an example. For example:: # Set the nick the bot uses on IRC, and on the botnet unless you specify a # separate botnet-nick, here. #set nick "Lamestbot" set nick LlamaBot Below are some of the common settings used for Eggdrop: :set username: if your shell runs identd (most do), then you should set this to your account login name. :set vhost4: you'll need to set this if you want your bot to use a vhost. This setting lets you choose which IP to use if your shell has multiple. Use vhost4 for an IPv4 address (ie, 1.2.3.4) See also: vhost6 :set vhost6: the same as vhost4, only for IPv6 addresses (ie, 5254:dead:b33f::1337:f270). :logfile: keeping logs is a good idea. Generally, you should have one log for bot stuff, and one log for each of your channels. To capture bot stuff, add the line ``logfile mcobxs * "botnick.log"`` to the config. To capture channel stuff, add ``logfile jkp #donkeys "#donkeys.log"``, ``logfile jkp #horses "#horses.log"``, etc. Make sure you remove the sample logfile lines for the channel #lamest. If you'd like to put your logfiles in their own directory, specify the directory in the log name (e.g. ``logfile jkp #donkeys "logs/#donkeys.log"`` to write the logfiles in the /logs directory). :listen 3333 all: you will almost certainly want to change this, as 3333 will probably be in use if there are other Eggdrops running on the machine. Generally, you can choose any port from 1024 to 65535, but the 49152-65535 range is best as these are the private/dynamic ports least likely to be reserved by other processes. You can choose not to have a port by commenting this line out, but that will prevent any telnet connections to the bot (you won't be able to use the bot as a hub, won't be able to telnet to the bot yourself, and the bot won't respond to /ctcp botnick CHAT requests). :set protect-telnet: setting this to 1 is strongly recommended for security reasons. :set require-p: this is a useful feature allowing you to give party line access on a user-specific basis. I recommend setting it to 1. :set stealth-telnets: when you telnet to your bot, it will usually display the bot's nickname and version information. You probably don't want people seeing this info if they do a port scan on the bot's shell. Setting this to 1 will prevent the bot from displaying its nickname and version when someone telnets to it. :set notify-newusers: set this to the nick you will have on the bot. This setting isn't really used if you have learn-users switched off. :set owner: you should only put one person in this list - yourself. Set it to the nick you will have on the bot. Do NOT leave it set to the default "MrLame, MrsLame". :set default-flags: these are the flags automatically given to a user when they introduce themselves to the bot (if learn-users is on) or when they're added using .adduser. If you don't want the user to be given any flags initially, set this to "" or "-". :set must-be-owner: if you have the .tcl and .set commands enabled, you should definitely set this to 1. In 1.3.26 and later, you can set it to 2 for even better security. :set chanfile: the chanfile allows you to store 'dynamic' channels so that the bot rejoins the channel if restarted. Dynamic channels are those you make the bot join using the .+chan command - they aren't defined in the config file. The chanfile is good if you frequently add/remove channels from the bot, but can be a pain if you only like to add/remove channels using the config file since settings stored in the chanfile with overwrite those set in the config. You can choose not to use a chanfile by setting it to "". :set nick: this is what you use to specify your bot's nickname. I recommend against using [ ] { } \ character's in the bot's nick, since these can cause problems with some Tcl scripts, but if you'd like to use them, you'll need to precede each of those characters with a backslash in the setting, e.g. if you wanted your bot to have the nick [NiceBot], use ``set nick "\[NiceBot\]"``. :set altnick: if you want to use [ ] { } \ characters in the bot's alternate nick, follow the backslash rule described previously. :server add: you should specify multiple servers in this list, in case the bot is unable to connect to the first server. The format for this list is shown below: .. code-block:: tcl server add you.need.to.change.this:6667 server add another.example.com:7000:password server add [2001:db8:618:5c0:263::]:6669:password server add ssl.example.net:+6697 :set learn-users: this is an important setting that determines how users will be added to your Eggdrop. If set to 1, people can add themselves to the bot by sending 'hello' to it (the user will be added with the flags set in default-flags). If set to 0, users cannot add themselves - a master or owner must add them using the .adduser command. :set dcc-block: although the example config file recommends you set this to 0 (turbo-dcc), this may cause DCC transfers to abort prematurely. If you'll be using DCC transfers a lot, set this to 1024. Finally, be sure to remove the 'die' commands from the config (there are two of them 'hidden' in various places), or the bot won't start. Once you've finished editing the config file, make sure you rename it to something other than "eggdrop.conf" if you haven't already. Then, if you edited the config file locally, upload the config file to the directory you installed the bot. Starting the Eggdrop -------------------- Phew! Now that you've compiled, installed, and configured Eggdrop, it's time to start it up. Switch to the directory to which you installed the bot, cross your fingers, and type ``./eggdrop -m `` (where is the name you gave to the config file). Eggdrop should start up, and the bot should appear on IRC within a few minutes. The -m option creates a new userfile for your bot, and is only needed the first time you start your Eggdrop. In future, you will only need to type ./eggdrop to start the bot. Make sure you take the time to read what it tells you when you start it up! Once your bot is on IRC, it's important that you promptly introduce yourself to the bot. Msg it the 'hello' command you specified in the config file, e.g. ``/msg hello``. This will make you the bot's owner. Once that's done, you need to set a password using ``/msg pass ``. You can then DCC chat to the bot. Now that your Eggdrop is on IRC and you've introduced yourself as owner, it's time to learn how to use your Eggdrop! No show? ~~~~~~~~ If your bot didn't appear on IRC, you should log in to the shell and view the bot's logfile (the default in the config file is "logs/eggdrop.log"). Additionally, you can kill the bot via the command line (``kill pid``, the pid is shown to you when you started the bot or can be viewed by running ``ps x``) and then restart it with the -mnt flag, which will launch you directly into the partyline, to assist with troubleshooting. Note that if you use the -t flag, the bot will not persist and you will kill it once you quit the partyline. If you're still unsure what the problem is, try asking in #eggdrop on Libera, and be sure to include any relevant information from the logfile. Good luck! eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/tlssetup.rst000066400000000000000000000104761524730042700254340ustar00rootroot00000000000000Enabling TLS Security on Eggdrop ================================ There are several ways TLS encryption can protect communication between your Eggdrop and the world. This guide will walk you through a few common scenarios and how to properly set up TLS protection. Sidenote: Despite SSL (Secure Socket Layer) encryption being deprecated and no longer secure, the term "SSL" is a bit of an anachronism and still commonly used interchangeably with TLS (Transport Layer Security). If you see the term "SSL" used to describe a secure connection method, including Eggdrop's own documentation and configuration files, it is probably safe to assume it is actually referring to the secure TLS protocol. If you talk to someone and they use the term "SSL" be sure to correct them, we're sure they will *definitely* appreciate it :) Pre-requisites -------------- Your server must have OpenSSL (or an equivalent fork) installed. Most commonly this is done through your OS's package manager. Both the main package as well as the development headers must be installed. On a Debian/Ubuntu distro, this can be done by running:: apt-get install openssl libssl-dev where openssl is the main package binaries, and libssl-dev are the development headers. Without these packages, TLS protection is not possible. You can check if your Eggdrop properly detected the installation of OpenSSL by either reviewing the ./configure output for the following line:: checking for openssl/ssl.h... yes Or, for an Eggdrop that is already running, you can join the partyline and type:: .status and look for:: TLS support is enabled. Connecting to a TLS-enabled IRC server -------------------------------------- Many, if not most, IRC servers offer connection ports that add TLS protection around the data. Eggdrop uses a '+' symbol in front of the port to denote a port as a TLS-enabled port. To add a server in the config file that supports TLS, add it as normal but simply prefix the port with a '+'. For example, if irc.pretendNet.org says it offers TLS on port 7000, you would add it to your configuration file as:: server add irc.pretendNet.org +7000 No other action is necessary. Protecting Botnet Communications -------------------------------- Eggdrop has the ability to protect botnet (direct bot to bot) communications with TLS. Configuration File Preparation - Generating Keys ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If an Eggdrop is going to listen/accept connections on a TLS port (more on that in a moment), it must have a public/private certificate pair generated and configured. For most users, a self-signed certificate is sufficient for encryption (a certificate signed by a certificate authority would be more secure, but obtaining one is outside the scope of this tutorial. However, the implementation of a signed keypair is no different than a self-signed pair). To generate a self-signed key pair, enter the Eggdrop source directory (the directory you first compiled Eggdrop from, usually named eggdrop-X.Y.Z) and type:: make sslcert The wizard will walk you through generating a keypair and will, by default, install to ~/eggdrop (the install location can be changed by "make sslcert DEST=/path/to/eggdrop/install" In your config file, uncomment the "ssl-privatekey" and "ssl-certificate" settings. Eggdrop will look in the directory it is running from (~/eggdrop by default) for the files listed; add an absolute path if you installed them outside of Eggdrop's directory. Configuration File Preparation - Listening with TLS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now you need to tell Eggdrop to accept TLS connections. As an example, to listen with TLS on port 5555 on all available IPs, add to the config file:: listen +5555 all (There are numerous ways to format the listen command; read the config file and documentation for other alternatives) Connecting to an Eggdrop listening with TLS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To connect to a bot listening with TLS, prefix the port number with a '+'. For example:: .+bot HubBot 1.2.3.4 +5555 will use TLS to connect to 1.2.3.4 on port 5555 the next time a connection is attempted to HubBot. Additional Information ---------------------- For additional information and a more thorough explanation of Eggdrop's TLS implementation, please read the `TLS docs `_. eggheads-eggdrop-9c00fe1/doc/sphinx_source/tutorials/userfilesharing.rst000066400000000000000000000121721524730042700267360ustar00rootroot00000000000000Sharing Userfiles ================= One of the great features of Eggdrop is bot linking, allowing you to create your own 'botnet'. A botnet can consist of two linked bots to as many as hundreds of linked bots. If you're using an Eggdrop for channel management, the most important feature of linked bots is userfile sharing. With userfile sharing, the bots can keep their user lists, ban lists, and ignore lists in sync with one another. A botnet also has many other possible capabilities, such as allowing bots to op one another securely, and making all bots join a channel with one command. Linking bots can be a confusing process for first timers, but it's quite simple if you know how. The first thing you need to do is chose a hub bot. The hub is the main bot which all the other bots on the botnet (which will become leaf bots) connect to. Even if you are only using two bots, it's best to use one as a hub bot, and the other as a leaf. The hub bot should be the most reliable bot on your botnet, running on a fast, stable, and secure shell - so keep that in mind when deciding which bot will be the hub. Once you've decided on the hub, it's time to set up the linking. How to share userfiles- the super-short version ----------------------------------------------- (This example steps assume Eggdrop has been compiled with TLS support, and you are sharing all channels) On the Hub Bot ^^^^^^^^^^^^^^ 1. Ensure you know what ports your bot is listening on, this is set in the config and those ports will be used in the example below. For this example, our config looks like:: listen +3333/+4444 all 2. On the hub (for this example, the Hub is called Hubalicious), add the leaf bot (for this example, the Leaf is called LeifErikson) with ``.+bot LeifErikson +4444/+3333 ``. 3. On the hub, give the leaf bot the appropriate leaf sharing flags with ``.botattr LiefErikson +gs``. On the Leaf Bot ^^^^^^^^^^^^^^^ 1. Ensure you know what ports your bot is listening on, this is set in the config and those ports will be used in the example below. For this example, our config looks like:: listen +5555/+6666 all 2. Now on the leaf, add the hub bot with ``.+bot Hubalicious +6666/+5555 ``. 3. On the leaf, give the hub bot the appropriate hub and sharing flags with ``.botattr Hubalicious +ghp``. At this point, the leaf bot should attempt to connect to the hub bot within the next minute, or you can force the link connection with ``.link Hubalicious``. You can also use .bottree to see your botnet structure. Explaining the Linking/Sharing Process -------------------------------------- Eggdrop bots can talk to each other for a variety of reasons. In order for an Eggdrop to talk to another Eggdrop for anything, they must link. This is done by adding a bot record the remote bot on the hub bot (In our example above, using ``.+bot LeifErikson`` on Hubalicious, and using ``.+bot Hubalicious`` on LiefErikson). Once the bot records are added, bots can be manually connected using the ``.link`` command. In the example above, we add the +s bot flag to LiefErikson's bot record on Hubalicious to tell Hubalicious that LiefErikson is not only allowed to connect, but is authorized to download the userfiles from Hubalicious. Similarly, we add the +p bot flag to Hubalicious's bot record on LiefErikson to tell LiefErikson that Hubalicous is authorized to send userfiles to LiefErikson. The +h bot flag is added to Hubalicious's bot record on LiefErikson so tell LiefErikson that Hubalicious is the hub, and it should always try to automatically connect to Hubalicious. Lastly, the +g flag is used on both bot records to indicate that Hubalicious is authorized to send userfiles for all channels. This is a shortcut method to sharing all channels instead of setting the ``+shared`` channel setting on the hub for each channel userfile you wish the hub to share (set via .chanset), and using the ``|+s #channel`` bot flag for each channel userfile that the leaf is authorized to receive userfiles from the hub. As an example of channel-specific userfile sharing, you would use ``.botattr LiefErikson |+s #theforest`` on Hubalicious to set #theforest as a channel authorized to be shared to LiefErikson, and ``.chanset #theforest +shared`` to tell LiefErikson to accept the channel userfile for #theforest. One more commonly used flag is the ``+a`` flag. This flag specifies an alternate hub to connect to in case the primary hub goes down. The alternate hub should be linked to hub and maintain all channel userfiles the hub maintains to ensure there is no desynchronization while the hub bot is down. Should the hub bot go down, and assuming the ``+h`` flag is set for the hub on a leaf, the leafbost will automatically try to reconnect to the hub every minute, even if it makes a connection with the alternate hub in the meantime. An alternate hub is added the same as a hub, except swapping out the ``h`` for the ``a``: ``.botattr AltRock +agp``. Now that you have a hub and leaf bot successfully connected and sharing userfiles, you can repeat the `On the Leaf Bot`_ section to add additional leafs to your hub. eggheads-eggdrop-9c00fe1/doc/sphinx_source/using/000077500000000000000000000000001524730042700221065ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/doc/sphinx_source/using/accounts.rst000066400000000000000000000167501524730042700244700ustar00rootroot00000000000000Account tracking in Eggdrop =========================== In Eggdrop 1.9.3, Eggdrop added the ability to associate nicknames with the service accounts they are logged into. It is IMPORTANT to note that Eggdrop's ability to do this is dependent on an IRC server's implementation of three features- the IRCv3 extended-join capability, the IRCv3 account-notify capability, and WHOX support. All three of these features must be supported by the server and, in the case of extended-join and account-notify, requested by Eggdrop in order for Eggdrop to maintain "perfect" association between nicknames and account statuses. Required Server Capabilities ---------------------------- You're going to see this repeated a lot- the IRC server must support three features in order for Eggdrop to accurately associate accounts with nicknames. These three features allow Eggdrop to always know the current association between an account and a nickname by getting account statuses of users already on a channel when it joins, new users joining a channel, and users who authenticate while on a channel. extended-join ^^^^^^^^^^^^^ `extended-join `_ is an IRCv3-defined capability that adds the account name of a user to the JOIN message sent by the IRC server, alerting clients that a new member has joined a channel. Enabling this capability allows Eggdrop to immediately determine the account name associated with a user joining a channel account-notify ^^^^^^^^^^^^^^ `account-notify `_ is an IRCv3-defined capability that sends a message to a channel when a member of the channel either authenticates or deauthenticates from their account. Enabling this capability allows Eggdrop to immediately associate an account to a channel member when they authenticate or deauthenticate. WHOX ^^^^ 'WHOX `_ is a server feature that allows a client to request custom fields to be returned in a WHO response. If a server supports this capability, Eggdrop sends a WHOX query to the server when it joins a channel, allowing it to immediately determine accounts associated with channel members when Eggdrop joins a channel. Enabling Eggdrop Account Tracking --------------------------------- By default, the Eggdrop config file will attempt to enable all the capabilities required for account tracking. There are two settings to pay attention to :: # To request the account-notify feature via CAP, set this to 1 set account-notify 1 # To request the extended-join feature via CAP, set this to 1 set extended-join 1 The ability of a server to support WHOX queries is determined via a server's ISUPPORT (005) reply. If a server supports WHOX queries, Eggdrop will automatically enable this feature. Checking Account-tracking Status -------------------------------- While Eggdrop is running, join the partyline and type `.status`. If account-tracking is enabled (both the server supports and Eggdrop has requested), you'll see this line :: Loaded module information: #eggdroptest : (not on channel) Channels: #eggdroptest (trying) Account tracking: Enabled <--- This line Online as: BeerBot (BeerBot) Otherwise, the prompt will tell you which required capability is missing/not enabled :: Loaded module information: #eggdroptest : 2 members, enforcing "+tn" (greet) Channels: #eggdroptest (need ops) Account tracking: Best-effort (Missing capabilities: extended-join, see .status all for details) <---- This line Online as: Eggdrop (Eggdrop) Determining if a Server Supports Account Capabilities ----------------------------------------------------- A server announces the capabilities it supports via a CAP request. If you have Tcl enabled on the partyline (or via a raw message from a client), you can send `.tcl cap ls` and see if the extended-join and account-notify capabilities are supported by the server. If they are not listed, the server does not support it. A server announces if it supports WHOX via its ISUPPORT (005) announcement. If you have Tcl enabled on the partyline, you can send `.tcl issupport isset WHOX` and if it returns '1', WHOX is supported by the server. Best-Effort Account Tracking ---------------------------- If a server only supports some, but not all, of the required capabilities, Eggdrop will switch to 'best effort' account tracking. This means Eggdrop will update account statuses whenever it sees account information, but in this mode Eggdrop cannot guarantee that all account associations are up to date. If a server does not support extended-join, Eggdrop will not be able to determine the account associated with a user when they join. Eggdrop can update this information by sending a WHOX to the server. If a server does not support account-notify, Eggdrop will not be able to determine the account associated with a user if they authenticate/deauthenticate from their account after joining a channel. Eggdrop can update this information by sending a WHOX to the server. If a server does not support WHOX, Eggdrop will not be able to determine the accounts associated with users already on a channel before Eggdrop joined. There is no reliable way to update this information. One workaround to significantly increase the accuracy of account tracking for scripts in a 'best effort' scenario would be to issue a WHOX query (assuming the server supports it), wait for the reply from the server, and then query for the account information. account-tag ^^^^^^^^^^^ One supplementary capability that can assist a best-effort account tracking scenario is the IRCv3-defined `account-tag capability `_. The account-tag capability attaches a tag with the account name associated with the user sending a command. Enabling this capability allows Eggdrop to update its account tracking every time a user talks in channel, sets a mode, sends a kick, etc. While still not able to offer the same level of accuracy as enabling the "main three" account tracking features, it can increase the overall accuracy of the account list. Additionally, binds that react to user activity (pub, kick, mode, etc) containing account-tag will update the internal account list prior to executing the associated callback, so looking up the account name in the callback can be considered accurate. Using Accounts with Tcl Scripts ------------------------------- The Eggdrop Tcl ACCOUNT bind is triggered whenever an existing account record stored by Eggdrop is modified, such as a user de/authenticating to their account while in a channel, or information such as an account-tag being seen that updates an existing user. However, the ACCOUNT bind will NOT be triggered for the creation of a new user record, such as a user joining a channel. The bind is triggered for every channel the user is seen on- this means if a user is present with Eggdrop on four channels, the bind will be executed four times, each time with a different channel variable being passed to the associated Tcl procedure. Additionally, in a best-effort account tracking situation, Eggdrop will update the account associated with a user on all channels, not just the channel the event is seen on (and thus resulting in a bind trigger for each channel the user is on). In order to trigger Tcl script events to cover all instances where a user logs in, you need to pair an ACCOUNT bind with a JOIN bind. This will allow you to execute account-based events when a user joins as well as if they authenticate after joining. eggheads-eggdrop-9c00fe1/doc/sphinx_source/using/autoscripts.rst000066400000000000000000000400571524730042700252260ustar00rootroot00000000000000Eggdrop Autoscripts =================== Since it's inception, users have needed to load a Tcl script into Eggdrop by downloading a Tcl file, editing the file to customize settings, and then sourceing that file in the config file. In v1.10, the Autoscripts system was added to make this process a little more user-friendly. The autoscripts system helps by: * Centralizing commonly-used scripts in a single location * Allowing scripts to be downloaded via the partyline * Allowing script settings to be configured via the partyline * Allowing user-written scripts to be managed by the autoscripts system * Providing a documented API to write autoscripts-compatible scripts Autoscripts usage ----------------- To view available autoscript commands, type ``.autoscript`` on the partyline. This will open up a special Eggdrop console that doesn't require you to prefix commands with a '.' . The following sub-commands are available for use with script: remote ^^^^^^ This command will list scripts hosted on the Eggdrop website that are available to be downloaded and installed on your Eggdrop. fetch eggheads-eggdrop-9c00fe1/src/mod/webui.mod/webui.c000066400000000000000000000442201524730042700220430ustar00rootroot00000000000000/* * webui.c -- part of webui.mod */ /* * Copyright (C) 2023 - 2025 Michael Ortmann MIT License * Copyright (C) 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "src/mod/module.h" #ifdef TLS #define MODULE_NAME "webui" #include #include #include /* base64 encode b64_ntop() and base64 decode b64_pton() */ #include #include #include #include #include "src/version.h" #define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define WS_KEY "Sec-WebSocket-Key:" #define WS_KEYLEN 24 /* key is padded, so its always 24 bytes */ #define WS_LEN 28 /* length of Sec-WebSocket-Accept header field value * base64(len(sha1)) * import math; (4 * math.ceil(20 / 3)) */ #define WS_ECHO_ON 0x01 #define WS_ECHO_OFF 0x02 static Function *global = NULL; /* 0x15 = TLS ContentType alert * 0x0a = TLS Alert unexpected_message */ static const uint8_t alert[] = {0x15, 0x03, 0x01, 0x00, 0x02, 0x02, 0x0a}; static void webui_http_eof(int idx) { debug2("webui: webui_http_eof() idx %i sock %li", idx, dcc[idx].sock); killsock(dcc[idx].sock); dcc[idx].u.webui_listen_idx = 0; lostdcc(idx); } static void put_404(int idx) { int i; char *response; debug1("webui: put_404() idx %i", idx); i = snprintf(NULL, 0, "HTTP/1.1 404 \r\n" /* textual phrase is OPTIONAL */ "Content-Length: 13\r\n" "Content-Type: text/plain\r\n" "Server: %s\r\n" "\r\n" "404 Not Found", #ifdef EGG_PATCH stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER "+" EGG_PATCH); #else stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER); #endif response = nmalloc(i + 1); sprintf(response, "HTTP/1.1 404 \r\n" /* textual phrase is OPTIONAL */ "Content-Length: 13\r\n" "Content-Type: text/plain\r\n" "Server: %s\r\n" "\r\n" "404 Not Found", #ifdef EGG_PATCH stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER "+" EGG_PATCH); #else stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER); #endif tputs(dcc[idx].sock, response, i); nfree(response); killsock(dcc[idx].sock); dcc[idx].u.webui_listen_idx = 0; lostdcc(idx); } /* cache file data by modification time */ struct file_cache_struct { char filename[27]; char content_type[25]; time_t mtime; char *data; } file_cache[3] = { {"webui/apple-touch-icon.png", "image/png", -1, NULL}, {"webui/favicon.ico", "image/x-icon", -1, NULL}, {"webui/index.html", "text/html; charset=utf-8", -1, NULL} }; static void put_file(int idx, int file_cache_index) { struct stat sb; struct file_cache_struct *f = &file_cache[file_cache_index]; int fd, i; char *response; if (stat(f->filename, &sb) < 0) { putlog(LOG_MISC, "*", "WEBUI error: fstat(%s): %s", f->filename, strerror(errno)); return; } if (f->mtime != sb.st_mtime) { if ((fd = open(f->filename, O_RDONLY)) < 0) { putlog(LOG_MISC, "*", "WEBUI error: open(%s): %s", f->filename, strerror(errno)); put_404(idx); return; } f->data = nrealloc(f->data, sb.st_size); /* TODO: nfree() on module unloading */ if (read(fd, f->data, sb.st_size) < 0) { putlog(LOG_MISC, "*", "WEBUI error: read(%s): %s", f->filename, strerror(errno)); if ((fd = close(fd)) < 0) putlog(LOG_MISC, "*", "WEBUI error: close(%s): %s", f->filename, strerror(errno)); put_404(idx); return; } if ((fd = close(fd)) < 0) { putlog(LOG_MISC, "*", "WEBUI error: close(%s): %s", f->filename, strerror(errno)); put_404(idx); return; } f->mtime = sb.st_mtime; } i = snprintf(NULL, 0, "HTTP/1.1 200 \r\n" /* textual phrase is OPTIONAL */ "Content-Length: %jd\r\n" "Content-Type: %s\r\n" /* at least firefox 144 needs this */ "Server: %s\r\n" "\r\n", (intmax_t) sb.st_size, f->content_type, #ifdef EGG_PATCH stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER "+" EGG_PATCH); #else stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER); #endif response = nmalloc(i + sb.st_size); sprintf(response, "HTTP/1.1 200 \r\n" /* textual phrase is OPTIONAL */ "Content-Length: %jd\r\n" "Content-Type: %s\r\n" /* at least firefox 144 needs this */ "Server: %s\r\n" "\r\n", (intmax_t) sb.st_size, f->content_type, #ifdef EGG_PATCH stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER "+" EGG_PATCH); #else stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER); #endif memcpy(response + i, f->data, sb.st_size); tputs(dcc[idx].sock, response, i + sb.st_size); nfree(response); } static void webui_http_activity(int idx, char *buf, int len) { struct rusage ru1, ru2; int r, i, listen_idx; char *response; if (len < 6) { /* TODO: better len check */ putlog(LOG_MISC, "*", "WEBUI error: %s sent something other than http GET request", iptostr(&dcc[idx].sockname.addr.sa)); killsock(dcc[idx].sock); dcc[idx].u.webui_listen_idx = 0; lostdcc(idx); return; } if (buf[0] == 0x16) { /* 0x16 = TLS handshake */ putlog(LOG_MISC, "*", "WEBUI error: %s requested TLS handshake for non-ssl port", iptostr(&dcc[idx].sockname.addr.sa)); tputs(dcc[idx].sock, (char *) alert, sizeof alert); killsock(dcc[idx].sock); dcc[idx].u.webui_listen_idx = 0; lostdcc(idx); return; } r = getrusage(RUSAGE_SELF, &ru1); debug2("webui: webui_http_activity(): idx %i len %i", idx, len); buf[len] = '\0'; if (buf[5] == ' ') { debug1("webui: GET / idx %i", idx); put_file(idx, 2); } else if (buf[5] == 'f') { put_file(idx, 1); } else if (buf[5] == 'w') { debug1("webui: GET /w idx %i", idx); buf = strstr(buf, WS_KEY); if (!buf) { putlog(LOG_MISC, "*", "WEBUI error: Sec-WebSocket-Key not found ip %s", iptostr(&dcc[idx].sockname.addr.sa)); return; } buf += sizeof WS_KEY; for(i = 0; i < WS_KEYLEN; i++) if (!buf[i]) { putlog(LOG_MISC, "*", "WEBUI error: Sec-WebSocket-Key too short ip %s", iptostr(&dcc[idx].sockname.addr.sa)); return; } unsigned char hash[SHA_DIGEST_LENGTH]; #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); const EVP_MD *md = EVP_sha1(); unsigned int md_len; EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, buf, WS_KEYLEN); EVP_DigestUpdate(mdctx, WS_GUID, (sizeof WS_GUID) - 1); EVP_DigestFinal_ex(mdctx, hash, &md_len); EVP_MD_CTX_free(mdctx); #else SHA_CTX c; SHA1_Init(&c); SHA1_Update(&c, buf, WS_KEYLEN); SHA1_Update(&c, WS_GUID, (sizeof WS_GUID) - 1); SHA1_Final(hash, &c); #endif char out[WS_LEN + 1]; /* TODO: remove assert / debug */ if (b64_ntop(hash, sizeof hash, out, sizeof out) != WS_LEN) { putlog(LOG_MISC, "*", "WEBUI error: b64_ntop() != WS_LEN"); return; } i = snprintf(NULL, 0, "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "\r\n", out); response = nmalloc(i + 1); sprintf(response, "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "\r\n", out); tputs(dcc[idx].sock, response, i); nfree(response); sock_list* socklist_i = &socklist[findsock(dcc[idx].sock)]; socklist_i->flags |= SOCK_WS; socklist_i->flags &= ~ SOCK_BINARY; /* we need it for net.c sockgets(), is there better place to do this? */ debug2("webui: unset flag SOCK_BINARY idx %i sock %li", idx, dcc[idx].sock); //strcpy(dcc[idx].host, "*"); /* important for later dcc_telnet_id wild_match, is there better place to do this? */ /* .host becomes .nick in change_to_dcc_telnet_id() */ debug4("webui: set flag SOCK_WS socklist %i idx %i sock %li status %lu", findsock(dcc[idx].sock), idx, dcc[idx].sock, dcc[idx].status); dcc[idx].status |= STAT_USRONLY; /* magick */ listen_idx = dcc[idx].u.webui_listen_idx; dcc[idx].u.webui_listen_idx = 0; dcc_telnet_hostresolved2(idx, listen_idx); } else if (buf[5] == 'a') { put_file(idx, 0); } else put_404(idx); if ((dcc[idx].sock != -1) && (len == 511)) { /* sock == -1 if lostdcc() in dcc_telnet_hostresolved2() */ /* read probable remaining bytes */ SSL *ssl = socklist[findsock(dcc[idx].sock)].ssl; if (ssl) debug2("webui: SSL_read(): idx %i len %i", idx, SSL_read(ssl, buf, 511)); else debug2("webui: read(): idx %i len %li", idx, read(dcc[idx].sock, buf, 511)); } if (!r && !getrusage(RUSAGE_SELF, &ru2)) debug2("webui: webui_http_activity(): user %.3fms sys %.3fms", (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 + (double) (ru2.ru_utime.tv_sec - ru1.ru_utime.tv_sec ) * 1000, (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 + (double) (ru2.ru_stime.tv_sec - ru1.ru_stime.tv_sec ) * 1000); } static void webui_http_display(int idx, char *buf) { if (!dcc[idx].ssl) strcpy(buf, "webui http"); else strcpy(buf, "webui https"); } static struct dcc_table DCC_WEBUI_HTTP = { "WEBUI_HTTP", 0, webui_http_eof, webui_http_activity, NULL, NULL, webui_http_display, NULL, NULL, NULL, NULL }; static void webui_dcc_telnet_hostresolved(int idx, int listen_idx) { debug2("webui_dcc_telnet_hostresolved() idx %i listen_idx %i", idx, listen_idx); changeover_dcc(idx, &DCC_WEBUI_HTTP, 0); dcc[idx].u.webui_listen_idx = listen_idx; sockoptions(dcc[idx].sock, EGG_OPTION_SET, SOCK_BINARY); sockoptions(dcc[idx].sock, EGG_OPTION_UNSET, SOCK_BUFFER); } static size_t escape_html(char *dst, size_t dst_size, char *src, size_t src_size) { int i; char *d = dst; for (i = 0; i < src_size; i++) { switch ((unsigned char) src[i]) { case '"': if (dst_size < 6) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '&'; *d++ = 'q'; *d++ = 'u'; *d++ = 'o'; *d++ = 't'; *d++ = ';'; dst_size -= 6; break; case '&': if (dst_size < 5) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '&'; *d++ = 'a'; *d++ = 'm'; *d++ = 'p'; *d++ = ';'; dst_size -= 5; break; case '\'': if (dst_size < 6) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '&'; *d++ = 'a'; *d++ = 'p'; *d++ = 'o'; *d++ = 's'; *d++ = ';'; dst_size -= 6; break; case '<': if (dst_size < 4) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '&'; *d++ = 'l'; *d++ = 't'; *d++ = ';'; dst_size -= 4; break; case '>': if (dst_size < 4) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '&'; *d++ = 'g'; *d++ = 't'; *d++ = ';'; dst_size -= 4; break; case ESC: if ((i + 3) < src_size) { if ( ((unsigned char) src[i + 1] == '[') && ((unsigned char) src[i + 2] == '0') && ((unsigned char) src[i + 3] == 'm')) { if (dst_size < 4) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '<'; *d++ = '/'; *d++ = 'b'; *d++ = '>'; dst_size -= 4; } else if (((unsigned char) src[i + 1] == '[') && ((unsigned char) src[i + 2] == '1') && ((unsigned char) src[i + 3] == 'm')) { if (dst_size < 3) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = '<'; *d++ = 'b'; *d++ = '>'; dst_size -= 3; } else debug3("webui: escape_html(): unknown escape sequence found, skipping, %02x %02x %02x, PLEASE REPORT THIS BUG", (unsigned char) src[i + 1], (unsigned char) src[i + 2], (unsigned char) src[i + 3]); i += 3; } else debug0("webui: escape_html(): unknown SHORT escape sequence found, skipping, PLEASE REPORT THIS BUG"); break; case TLN_IAC: if ((i + 2) < src_size) { if (dst_size < 1) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } if ( ((unsigned char) src[i + 1] == TLN_WILL) && ((unsigned char) src[i + 2] == TLN_ECHO)) { *d++ = WS_ECHO_OFF; dst_size--; } else if (((unsigned char) src[i + 1] == TLN_WONT) && ((unsigned char) src[i + 2] == TLN_ECHO)) { *d++ = WS_ECHO_ON; dst_size--; } else debug2("webui: escape_html(): unknown telnet command found, skipping, %02x %02x, PLEASE REPORT THIS BUG", (unsigned char) src[i + 1], (unsigned char) src[i + 2]); i += 2; } else debug0("webui: escape_html(): unknown SHORT telnet command found, skipping, PLEASE REPORT THIS BUG"); break; default: if (dst_size < 1) { debug0("webui: escape_html(): destination string too long, PLEASE REPORT THIS BUG"); return d - dst; } *d++ = src[i]; dst_size--; } } return d - dst; } static size_t webui_frame(char **dst, char *src, size_t len) { static char buf[LOGLINELEN]; uint16_t len2; /* escape/replace html code chars * write to buf + offset 4 to leave room for webui frame header */ len = escape_html(buf + 4, (sizeof buf) - 4, src, len); /* we must not use putlog() or debug() here or we get recursion */ /* A server MUST NOT mask any frames that it sends to the client */ /* we use text, not binary, so escape_html() must output valid html */ if (len < 0x7e) { buf[2] =0x81; /* FIN + text frame */ buf[3] = len; *dst = buf + 2; len += 2; } else { buf[0] =0x81; /* FIN + text frame */ buf[1] = 0x7e; len2 = htons(len); memcpy(buf + 2, &len2, 2); *dst = buf; len += 4; } /* we dont need to implement len > 0xffff, * because eggdrop wont send that much data at once, * we also limit by sizeof buf */ return len; } /* TODO: return error code ? */ static void webui_unframe(int sock, char *buf, int *len) { int i; uint8_t *key, *payload; uint16_t status_code; if (*len < 6) { /* TODO: better len check */ /* TODO: return error code ? */ putlog(LOG_MISC, "*", "WEBUI error: bogus WebSocket frame from sock %i", sock); killsock(sock); lostdcc(findanyidx(sock)); return; } /* xor decrypt */ key = (uint8_t *) buf; if (key[1] < 0xfe) { key += 2; *len -= 6; } else if (key[1] == 0xfe) { key += 4; *len -= 8; } else { key += 10; *len -= 14; } payload = key + 4; for (i = 0; i < *len; i++) payload[i] = payload[i] ^ key[i % 4]; if (buf[0] & 0x08) { status_code = ntohs(*((uint16_t *) payload)); putlog(LOG_MISC, "*", "WEBUI: connection closed by peer with status code %i%s sock %i", status_code, status_code == 1001 ? " (Going Away)" : "", sock); killsock(sock); lostdcc(findanyidx(sock)); return; } memmove(buf, payload, *len); /* we switched back from binary sock to text sock for sockgets() needs this for dcc_telnet_id() */ /* so now we have to add \r\n here :/ */ strcpy(buf + *len, "\r\n"); *len+= 2; } static char *webui_close(void) { int idx; del_hook(HOOK_DCC_TELNET_HOSTRESOLVED, (Function) webui_dcc_telnet_hostresolved); del_hook(HOOK_WEBUI_FRAME, (Function) webui_frame); del_hook(HOOK_WEBUI_UNFRAME, (Function) webui_unframe); for (idx = 0; idx < dcc_total; idx++) { if (!strcmp(dcc[idx].nick, "(webui)") || !strcmp(dcc[idx].type->name, "WEBUI_HTTP") || (socklist[findsock(dcc[idx].sock)].flags & SOCK_WS)) { debug2("webui: webui_close(): closing sock idx %i, %li", idx, dcc[idx].sock); killsock(dcc[idx].sock); if (!strcmp(dcc[idx].type->name, "WEBUI_HTTP")) dcc[idx].u.webui_listen_idx = 0; lostdcc(idx); } } return NULL; } EXPORT_SCOPE char *webui_start(); static Function webui_table[] = { (Function) webui_start, (Function) webui_close, NULL, NULL, }; #endif char *webui_start(Function *global_funcs) { #ifdef TLS global = global_funcs; module_register(MODULE_NAME, webui_table, 0, 10); if (!module_depend(MODULE_NAME, "eggdrop", 110, 2)) { module_undepend(MODULE_NAME); return "This module requires Eggdrop 1.10.2 or later."; } add_hook(HOOK_DCC_TELNET_HOSTRESOLVED, (Function) webui_dcc_telnet_hostresolved); add_hook(HOOK_WEBUI_FRAME, (Function) webui_frame); add_hook(HOOK_WEBUI_UNFRAME, (Function) webui_unframe); return NULL; #else return "Initialization failure: configured with --disable-tls or openssl not found"; #endif } eggheads-eggdrop-9c00fe1/src/mod/woobie.mod/000077500000000000000000000000001524730042700207335ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/src/mod/woobie.mod/Makefile000066400000000000000000000023201524730042700223700ustar00rootroot00000000000000# Makefile for src/mod/woobie.mod/ srcdir = . doofus: @echo "" && \ echo "Let's try this from the right directory..." && \ echo "" && \ cd ../../../ && $(MAKE) static: ../woobie.o modules: ../../../woobie.$(MOD_EXT) ../woobie.o: $(CC) $(CFLAGS) $(CPPFLAGS) -DMAKING_MODS -c $(srcdir)/woobie.c && mv -f woobie.o ../ ../../../woobie.$(MOD_EXT): ../woobie.o $(LD) $(CFLAGS) -o ../../../woobie.$(MOD_EXT) ../woobie.o $(XLIBS) $(MODULE_XLIBS) && $(STRIP) ../../../woobie.$(MOD_EXT) depend: $(CC) $(CFLAGS) -MM $(srcdir)/woobie.c -MT ../woobie.o > .depend clean: @rm -f .depend *.o *.$(MOD_EXT) *~ *.gcda *.gcno distclean: clean #safety hash ../woobie.o: woobie.c ../../../src/mod/module.h ../../../src/main.h \ ../../../config.h ../../../eggint.h ../../../lush.h ../../../src/lang.h \ ../../../src/eggdrop.h ../../../src/compat/in6.h ../../../src/flags.h \ ../../../src/cmdt.h ../../../src/tclegg.h ../../../src/tclhash.h \ ../../../src/chan.h ../../../src/users.h ../../../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 eggheads-eggdrop-9c00fe1/src/mod/woobie.mod/modinfo000066400000000000000000000002721524730042700223120ustar00rootroot00000000000000DESC:This module is for demonstrative purposes only. DESC: DESC:Only programmers interested in module programming might want to enable DESC:this module. Everyone else should DISABLE it. eggheads-eggdrop-9c00fe1/src/mod/woobie.mod/woobie.c000066400000000000000000000076171524730042700223760ustar00rootroot00000000000000/* * woobie.c -- part of woobie.mod * nonsensical command to exemplify module programming * * Originally written by ButchBub 15 July 1997 * Comments by Fabian Knittel 29 December 1999 */ /* * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define MODULE_NAME "woobie" #define MAKING_WOOBIE #include "src/mod/module.h" #include #undef global /* Pointer to the eggdrop core function table. Gets initialized in * woobie_start(). */ static Function *global = NULL; /* Calculate the memory we keep allocated. */ static int woobie_expmem() { int size = 0; return size; } static int cmd_woobie(struct userrec *u, int idx, char *par) { /* Log the command as soon as you're sure all parameters are valid. */ putlog(LOG_CMDS, "*", "#%s# woobie", dcc[idx].nick); dprintf(idx, "WOOBIE!\n"); return 0; } /* A report on the module status. * * details is either 0 or 1: * 0 - `.status' * 1 - `.status all' or `.module woobie' */ static void woobie_report(int idx, int details) { if (details) { int size = woobie_expmem(); dprintf(idx, " Using %d byte%s of memory\n", size, (size != 1) ? "s" : ""); } } /* Note: The tcl-name is automatically created if you set it to NULL. In * the example below it would be just "*dcc:woobie". If you specify * "woobie:woobie" it would be "*dcc:woobie:woobie" instead. * ^----- command name ^--- table name * ^------------ module name * * This is only useful for stackable binding tables (and H_dcc isn't * stackable). */ static cmd_t mydcc[] = { /* command flags function tcl-name */ {"woobie", "", cmd_woobie, NULL}, {NULL, NULL, NULL, NULL} /* Mark end. */ }; static char *woobie_close() { rem_builtins(H_dcc, mydcc); module_undepend(MODULE_NAME); return NULL; } /* Define the prototype here, to avoid warning messages in the * woobie_table. */ EXPORT_SCOPE char *woobie_start(); /* This function table is exported and may be used by other modules and * the core. * * The first four have to be defined (you may define them as NULL), as * they are checked by eggdrop core. */ static Function woobie_table[] = { (Function) woobie_start, (Function) woobie_close, (Function) woobie_expmem, (Function) woobie_report, }; char *woobie_start(Function *global_funcs) { /* Assign the core function table. After this point you use all normal * functions defined in src/mod/modules.h */ global = global_funcs; /* Register the module. */ module_register(MODULE_NAME, woobie_table, 2, 1); /* ^--- minor module version * ^------ major module version * ^-------------------- module function table * ^--------------------------------- module name */ if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) { module_undepend(MODULE_NAME); return "This module requires Eggdrop 1.8.0 or later."; } /* Add command table to bind list H_dcc, responsible for dcc commands. * Currently we only add one command, `woobie'. */ add_builtins(H_dcc, mydcc); return NULL; } eggheads-eggdrop-9c00fe1/src/modules.c000066400000000000000000001061041524730042700177300ustar00rootroot00000000000000/* * modules.c -- handles: * support for modules in eggdrop * * by Darrin Smith (beldin@light.iinet.net.au) */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "main.h" #include "modules.h" #include "tandem.h" #include "md5/md5.h" #include "users.h" #ifndef STATIC # ifdef MOD_USE_SHL # include # endif # ifdef MOD_USE_DYLD # include # define DYLDFLAGS NSLINKMODULE_OPTION_BINDNOW|NSLINKMODULE_OPTION_PRIVATE|NSLINKMODULE_OPTION_RETURN_ON_ERROR # endif # ifdef MOD_USE_LOADER # include # endif # ifdef MOD_USE_DL # include # ifndef RTLD_GLOBAL # define RTLD_GLOBAL 0 # endif # ifndef RTLD_NOW # define RTLD_NOW 1 # endif # define DLFLAGS RTLD_NOW|RTLD_GLOBAL # endif /* MOD_USE_DL */ #endif /* !STATIC */ extern struct dcc_t *dcc; extern struct userrec *userlist, *lastuser; extern struct chanset_t *chanset; extern char botnetnick[], botname[], origbotname[], botuser[], ver[], log_ts[], admin[], userfile[], notify_new[], helpdir[], version[], quit_msg[], pid_file[]; extern int parties, noshare, dcc_total, egg_numver, userfile_perm, ignore_time, must_be_owner, raw_log, max_dcc, make_userfile, default_flags, require_p, share_greet, use_invites, use_exempts, password_timeout, force_expire, protect_readonly, reserved_port_min, reserved_port_max, quiet_reject, stealth_telnets; extern volatile sig_atomic_t do_restart; int copy_to_tmp = 1; /* TODO: remove from module API for eggdrop 2.0 */ #ifdef IPV6 extern int pref_af; #endif #ifdef TLS extern int tls_vfyclients, tls_vfydcc, tls_vfybots; #endif extern party_t *party; extern time_t now, online_since; extern tand_t *tandbot; extern Tcl_Interp *interp; extern sock_list *socklist; extern const char *argv0; int xtra_kill(); int xtra_unpack(); static int module_rename(char *name, char *newname); #ifndef STATIC char moddir[121] = "modules/"; #endif #ifdef STATIC struct static_list { struct static_list *next; char *name; char *(*func) (); } *static_modules = NULL; void check_static(char *name, char *(*func) ()) { struct static_list *p = nmalloc(sizeof(struct static_list)); p->name = nmalloc(strlen(name) + 1); strcpy(p->name, name); p->func = func; p->next = static_modules; static_modules = p; } #endif /* STATIC */ /* The null functions */ void null_func() { } int false_func() { return 0; } /* The REAL hooks. When these are called, a return of 0 indicates unhandled; * 1 indicates handled. */ struct hook_entry *hook_list[REAL_HOOKS]; static void null_share(int idx, char *x) { if ((x[0] == 'u') && (x[1] == 'n')) { putlog(LOG_BOTS, "*", "User file rejected by %s: %s", dcc[idx].nick, x + 3); dcc[idx].status &= ~STAT_OFFERED; if (!(dcc[idx].status & STAT_GETTING)) { dcc[idx].status &= ~STAT_SHARE; } } else if ((x[0] != 'v') && (x[0] != 'e')) { dprintf(idx, "s un Not sharing userfile.\n"); } } void (*encrypt_pass) (char *, char *) = 0; char *(*encrypt_pass2) (char *) = 0; char *(*verify_pass2) (char *, char *) = 0; char *(*encrypt_string) (char *, char *) = 0; char *(*decrypt_string) (char *, char *) = 0; void (*shareout) () = null_func; void (*sharein) (int, char *) = null_share; void (*qserver) (int, char *, int) = (void (*)(int, char *, int)) null_func; void (*add_mode) () = null_func; int (*match_noterej) (struct userrec *, char *) = (int (*)(struct userrec *, char *)) false_func; int (*rfc_casecmp) (const char *, const char *) = _rfc_casecmp; int (*rfc_ncasecmp) (const char *, const char *, int) = _rfc_ncasecmp; int (*rfc_toupper) (int) = _rfc_toupper; int (*rfc_tolower) (int) = _rfc_tolower; void (*dns_hostbyip) (sockname_t *) = core_dns_hostbyip; void (*dns_ipbyhost) (char *) = core_dns_ipbyhost; void (*webui_dcc_telnet_hostresolved) (int, int) = (void (*)(int, int)) null_func; size_t (*webui_frame) (char **, char *, size_t) = (size_t (*)(char **, char * ,size_t)) null_func; void (*webui_unframe) (int, char *, int *) = (void (*)(int, char *, int *)) null_func; module_entry *module_list; dependancy *dependancy_list = NULL; /* The horrible global lookup table for functions * BUT it makes the whole thing *much* more portable than letting each * OS screw up the symbols their own special way :/ */ Function global_table[] = { /* 0 - 3 */ (Function) mod_malloc, (Function) mod_free, (Function) 0, /* was eggContext() */ (Function) module_rename, /* 4 - 7 */ (Function) module_register, (Function) module_find, (Function) module_depend, (Function) module_undepend, /* 8 - 11 */ (Function) add_bind_table, (Function) del_bind_table, (Function) find_bind_table, (Function) check_tcl_bind, /* 12 - 15 */ (Function) add_builtins, (Function) rem_builtins, (Function) add_tcl_commands, (Function) rem_tcl_commands, /* 16 - 19 */ (Function) add_tcl_ints, (Function) rem_tcl_ints, (Function) add_tcl_strings, (Function) rem_tcl_strings, /* 20 - 23 */ (Function) base64_to_int, (Function) int_to_base64, (Function) int_to_base10, (Function) simple_sprintf, /* 24 - 27 */ (Function) botnet_send_zapf, (Function) botnet_send_zapf_broad, (Function) botnet_send_unlinked, (Function) botnet_send_bye, /* 28 - 31 */ (Function) botnet_send_chat, (Function) botnet_send_filereject, (Function) botnet_send_filesend, (Function) botnet_send_filereq, /* 32 - 35 */ (Function) botnet_send_join_idx, (Function) botnet_send_part_idx, (Function) updatebot, (Function) nextbot, /* 36 - 39 */ (Function) zapfbot, (Function) n_free, (Function) u_pass_match, (Function) _user_malloc, /* 40 - 43 */ (Function) get_user, (Function) set_user, (Function) add_entry_type, (Function) del_entry_type, /* 44 - 47 */ (Function) get_user_flagrec, (Function) set_user_flagrec, (Function) get_user_by_host, (Function) get_user_by_handle, /* 48 - 51 */ (Function) find_entry_type, (Function) find_user_entry, (Function) adduser, (Function) deluser, /* 52 - 55 */ (Function) addhost_by_handle, (Function) delhost_by_handle, (Function) readuserfile, (Function) write_userfile, /* 56 - 59 */ (Function) geticon, (Function) clear_chanlist, (Function) reaffirm_owners, (Function) change_handle, /* 60 - 63 */ (Function) write_user, (Function) clear_userlist, (Function) count_users, (Function) sanity_check, /* 64 - 67 */ (Function) break_down_flags, (Function) build_flags, (Function) flagrec_eq, (Function) flagrec_ok, /* 68 - 71 */ (Function) & shareout, (Function) dprintf, (Function) chatout, (Function) chanout_but, /* 72 - 75 */ (Function) check_validity, (Function) egg_list_delete, (Function) egg_list_append, (Function) egg_list_contains, /* 76 - 79 */ (Function) answer, (Function) getvhost, #ifdef TLS (Function) ssl_handshake, #else (Function) 0, #endif (Function) tputs, /* 80 - 83 */ (Function) new_dcc, (Function) lostdcc, (Function) getsock, (Function) killsock, /* 84 - 87 */ (Function) open_listen, (Function) getdccaddr, (Function) _get_data_ptr, (Function) open_telnet, /* 88 - 91 */ (Function) check_tcl_event, (Function) memcpy, /* was egg_memcpy -- use memcpy() instead */ (Function) my_atoul, (Function) my_strcpy, /* 92 - 95 */ (Function) & dcc, /* struct dcc_t * */ (Function) & chanset, /* struct chanset_t * */ (Function) & userlist, /* struct userrec * */ (Function) & lastuser, /* struct userrec * */ /* 96 - 99 */ (Function) & global_bans, /* struct banrec * */ (Function) & global_ign, /* struct igrec * */ (Function) & password_timeout, /* int */ (Function) & share_greet, /* int */ /* 100 - 103 */ (Function) & max_dcc, /* int */ (Function) & require_p, /* int */ (Function) & ignore_time, /* int */ #ifdef TLS (Function) dcc_fingerprint, #else (Function) 0, /* was use_console_r */ #endif /* 104 - 107 */ (Function) & reserved_port_min, (Function) & reserved_port_max, (Function) & raw_log, /* int */ (Function) & noshare, /* int */ /* 108 - 111 */ #ifdef TLS (Function) & tls_vfybots, /* int */ #else (Function) 0, /* gban_total -- UNUSED! (Eule) */ #endif (Function) & make_userfile, /* int */ (Function) & default_flags, /* int */ (Function) & dcc_total, /* int */ /* 112 - 115 */ (Function) 0, /* was tempdir */ #ifdef TLS (Function) & tls_vfyclients, /* int */ (Function) & tls_vfydcc, /* int */ #else (Function) 0, /* was natip */ (Function) 0, /* was myip -- use getvhost() instead */ #endif (Function) origbotname, /* char * */ /* 116 - 119 */ (Function) botuser, /* char * */ (Function) admin, /* char * */ (Function) userfile, /* char * */ (Function) ver, /* char * */ /* 120 - 123 */ (Function) notify_new, /* char * */ (Function) helpdir, /* char * */ (Function) version, /* char * */ (Function) botnetnick, /* char * */ /* 124 - 127 */ (Function) & DCC_CHAT_PASS, /* struct dcc_table * */ (Function) & DCC_BOT, /* struct dcc_table * */ (Function) & DCC_LOST, /* struct dcc_table * */ (Function) & DCC_CHAT, /* struct dcc_table * */ /* 128 - 131 */ (Function) & interp, /* Tcl_Interp * */ (Function) & now, /* time_t */ (Function) findanyidx, (Function) findchan, /* 132 - 135 */ (Function) cmd_die, (Function) days, (Function) daysago, (Function) daysdur, /* 136 - 139 */ (Function) ismember, (Function) newsplit, (Function) splitnick, (Function) splitc, /* 140 - 143 */ (Function) addignore, (Function) match_ignore, (Function) delignore, (Function) fatal, /* 144 - 147 */ (Function) xtra_kill, (Function) xtra_unpack, (Function) movefile, (Function) copyfile, /* 148 - 151 */ (Function) do_tcl, (Function) readtclprog, (Function) get_language, (Function) def_get, /* 152 - 155 */ (Function) makepass, (Function) _wild_match, (Function) maskaddr, (Function) show_motd, /* 156 - 159 */ (Function) tellhelp, (Function) showhelp, (Function) add_help_reference, (Function) rem_help_reference, /* 160 - 163 */ (Function) touch_laston, (Function) & add_mode, /* Function * */ (Function) rmspace, (Function) in_chain, /* 164 - 167 */ (Function) add_note, (Function) del_lang_section, (Function) detect_dcc_flood, (Function) flush_lines, /* 168 - 171 */ (Function) expected_memory, (Function) tell_mem_status, (Function) & do_restart, /* volatile sig_atomic_t */ (Function) check_tcl_filt, /* 172 - 175 */ (Function) add_hook, (Function) del_hook, (Function) & H_dcc, /* p_tcl_bind_list * */ (Function) & H_filt, /* p_tcl_bind_list * */ /* 176 - 179 */ (Function) & H_chon, /* p_tcl_bind_list * */ (Function) & H_chof, /* p_tcl_bind_list * */ (Function) & H_load, /* p_tcl_bind_list * */ (Function) & H_unld, /* p_tcl_bind_list * */ /* 180 - 183 */ (Function) & H_chat, /* p_tcl_bind_list * */ (Function) & H_act, /* p_tcl_bind_list * */ (Function) & H_bcst, /* p_tcl_bind_list * */ (Function) & H_bot, /* p_tcl_bind_list * */ /* 184 - 187 */ (Function) & H_link, /* p_tcl_bind_list * */ (Function) & H_disc, /* p_tcl_bind_list * */ (Function) & H_away, /* p_tcl_bind_list * */ (Function) & H_nkch, /* p_tcl_bind_list * */ /* 188 - 191 */ (Function) & USERENTRY_BOTADDR, /* struct user_entry_type * */ (Function) & USERENTRY_BOTFL, /* struct user_entry_type * */ (Function) & USERENTRY_HOSTS, /* struct user_entry_type * */ (Function) & USERENTRY_PASS, /* struct user_entry_type * */ /* 192 - 195 */ (Function) & USERENTRY_XTRA, /* struct user_entry_type * */ (Function) user_del_chan, (Function) & USERENTRY_INFO, /* struct user_entry_type * */ (Function) & USERENTRY_COMMENT, /* struct user_entry_type * */ /* 196 - 199 */ (Function) & USERENTRY_LASTON, /* struct user_entry_type * */ (Function) putlog, (Function) botnet_send_chan, (Function) list_type_kill, /* 200 - 203 */ (Function) logmodes, (Function) masktype, (Function) stripmodes, (Function) stripmasktype, /* 204 - 207 */ (Function) sub_lang, (Function) & online_since, /* time_t * */ (Function) cmd_loadlanguage, (Function) check_dcc_attrs, /* 208 - 211 */ (Function) check_dcc_chanattrs, (Function) add_tcl_coups, (Function) rem_tcl_coups, (Function) botname, /* 212 - 215 */ (Function) 0, /* remove_gunk() -- UNUSED! (drummer) */ (Function) check_tcl_chjn, (Function) sanitycheck_dcc, (Function) isowner, /* 216 - 219 */ (Function) fcopyfile, (Function) copyfilef, (Function) & rfc_casecmp, /* Function * */ (Function) & rfc_ncasecmp, /* Function * */ /* 220 - 223 */ (Function) & global_exempts, /* struct exemptrec * */ (Function) & global_invites, /* struct inviterec * */ (Function) 0, /* ginvite_total -- UNUSED! (Eule) */ (Function) 0, /* gexempt_total -- UNUSED! (Eule) */ /* 224 - 227 */ (Function) & H_event, /* p_tcl_bind_list * */ (Function) & use_exempts, /* int */ (Function) & use_invites, /* int */ (Function) & force_expire, /* int */ /* 228 - 231 */ (Function) add_lang_section, (Function) _user_realloc, (Function) mod_realloc, (Function) xtra_set, /* 232 - 235 */ (Function) 0, /* was eggContextNote() */ #ifdef DEBUG_ASSERT (Function) eggAssert, #else (Function) 0, #endif (Function) allocsock, (Function) call_hostbyip, /* 236 - 239 */ (Function) call_ipbyhost, (Function) iptostr, (Function) & DCC_DNSWAIT, /* struct dcc_table * */ (Function) hostsanitycheck_dcc, /* 240 - 243 */ (Function) dcc_dnsipbyhost, (Function) dcc_dnshostbyip, (Function) changeover_dcc, (Function) make_rand_str, /* 244 - 247 */ (Function) & protect_readonly, /* int */ (Function) findchan_by_dname, (Function) removedcc, (Function) & userfile_perm, /* int */ /* 248 - 251 */ (Function) sock_has_data, (Function) bots_in_subtree, (Function) users_in_subtree, (Function) egg_inet_aton, /* 252 - 255 */ (Function) egg_snprintf, (Function) egg_vsnprintf, (Function) memset, /* was egg_memset -- use memset() or egg_bzero() instead */ (Function) strcasecmp, /* was egg_strcasecmp -- use strcasecmp() instead */ /* 256 - 259 */ (Function) strncasecmp, /* was egg_strncasecmp -- use strncasecmp() instead */ (Function) is_file, (Function) & must_be_owner, /* int */ (Function) & tandbot, /* tand_t * */ /* 260 - 263 */ (Function) & party, /* party_t * */ (Function) open_address_listen, (Function) str_escape, (Function) strchr_unescape, /* 264 - 267 */ (Function) str_unescape, (Function) egg_strcatn, (Function) clear_chanlist_member, (Function) 0, /* was fixfrom */ /* 268 - 271 */ (Function) & socklist, /* sock_list * */ (Function) sockoptions, (Function) flush_inbuf, (Function) kill_bot, /* 272 - 275 */ (Function) quit_msg, /* char * */ (Function) module_load, (Function) module_unload, (Function) & parties, /* int */ /* 276 - 279 */ (Function) tell_bottree, (Function) MD5_Init, (Function) MD5_Update, (Function) MD5_Final, /* 280 - 283 */ (Function) _wild_match_per, (Function) killtransfer, (Function) write_ignores, (Function) & copy_to_tmp, /* int */ /* 284 - 287 */ (Function) & quiet_reject, /* int */ (Function) file_readable, (Function) setsockname, (Function) open_telnet_raw, /* 288 - 291 */ #ifdef IPV6 (Function) & pref_af, /* int */ #else (Function) 0, /* IPv6 leftovers: 288 */ #endif (Function) strip_mirc_codes, (Function) check_ansi, (Function) oatoi, /* 292 - 295 */ (Function) str_isdigit, (Function) remove_crlf, (Function) addr_match, (Function) mask_match, /* 296 - 299 */ (Function) check_conflags, (Function) increase_socks_max, (Function) log_ts, (Function) tcl_resultempty, /* 300 - 303 */ (Function) tcl_resultint, (Function) tcl_resultstring, (Function) getdccfamilyaddr, #ifndef HAVE_STRLCPY (Function) strlcpy, #else (Function) 0, #endif /* 304 - 307 */ (Function) strlcpy, /* was strncpyz() -- use strlcpy() instead */ #ifndef HAVE_BASE64 (Function) b64_ntop, (Function) b64_pton, #else (Function) 0, (Function) 0, #endif (Function) check_validpass, /* 308 - 311 */ (Function) make_rand_str_from_chars, (Function) add_tcl_objcommands, (Function) pid_file, /* char */ #ifndef HAVE_EXPLICIT_BZERO (Function) explicit_bzero, #else (Function) 0, #endif /* 312 - 315 */ (Function) & USERENTRY_PASS2, /* struct user_entry_type * */ (Function) crypto_verify, (Function) egg_uname, (Function) get_expire_time, /* 316 - 319 */ (Function) & USERENTRY_ACCOUNT, /* struct user_entry_type * */ (Function) get_user_by_account, (Function) delhost_by_handle, (Function) check_tcl_event_arg, /* 320 - 323 */ (Function) bind_bind_entry, (Function) unbind_bind_entry, (Function) & argv0, (Function) lookup_user_record, /* 324 - 327 */ (Function) find_member_from_nick, (Function) get_user_from_member, (Function) dcc_telnet_hostresolved2, (Function) findsock, /* 328 - 331 */ (Function) & stealth_telnets, /* int */ (Function) parse_irc, (Function) join_str_array, (Function) splitcn }; void init_modules(void) { int i; module_list = nmalloc(sizeof(module_entry)); module_list->name = nmalloc(8); strcpy(module_list->name, "eggdrop"); module_list->major = (egg_numver) / 10000; module_list->minor = (egg_numver / 100) % 100; #ifndef STATIC module_list->hand = NULL; #endif module_list->next = NULL; module_list->funcs = NULL; for (i = 0; i < REAL_HOOKS; i++) hook_list[i] = NULL; } int expmem_modules(int y) { int c = 0, i; module_entry *p; dependancy *d; struct hook_entry *q; Function *f; #ifdef STATIC struct static_list *s; for (s = static_modules; s; s = s->next) c += sizeof(struct static_list) + strlen(s->name) + 1; #endif for (i = 0; i < REAL_HOOKS; i++) for (q = hook_list[i]; q; q = q->next) c += sizeof(struct hook_entry); for (d = dependancy_list; d; d = d->next) c += sizeof(dependancy); for (p = module_list; p; p = p->next) { c += sizeof(module_entry); c += strlen(p->name) + 1; f = p->funcs; if (f && f[MODCALL_EXPMEM] && !y) c += (int) (f[MODCALL_EXPMEM] ()); } return c; } int module_register(char *name, Function *funcs, int major, int minor) { module_entry *p; for (p = module_list; p && p->name; p = p->next) { if (!strcasecmp(name, p->name)) { p->major = major; p->minor = minor; p->funcs = funcs; return 1; } } return 0; } const char *module_load(char *name) { module_entry *p; char *e; Function f; #ifdef STATIC struct static_list *sl; #endif #ifndef STATIC size_t len; char workbuf[PATH_MAX]; # ifdef MOD_USE_SHL shl_t hand; # endif # ifdef MOD_USE_DYLD NSObjectFileImage file; NSObjectFileImageReturnCode ret; NSModule hand; NSSymbol sym; # endif # ifdef MOD_USE_LOADER ldr_module_t hand; # endif # ifdef MOD_USE_DL void *hand; # endif #endif /* !STATIC */ if (module_find(name, 0, 0) != NULL) return MOD_ALREADYLOAD; #ifndef STATIC if (moddir[0] != '/') { if (getcwd(workbuf, sizeof workbuf) == NULL) { debug1("modules: getcwd(): %s\n", strerror(errno)); return MOD_BADCWD; } len = strlen(workbuf); snprintf(workbuf + len, (sizeof workbuf) - len, "/%s%s." EGG_MOD_EXT, moddir, name); } else { snprintf(workbuf, sizeof workbuf, "%s%s." EGG_MOD_EXT, moddir, name); } # ifdef MOD_USE_SHL hand = shl_load(workbuf, BIND_IMMEDIATE, 0L); if (!hand) return "Can't load module."; sprintf(workbuf, "%s_start", name); if (shl_findsym(&hand, workbuf, (short) TYPE_PROCEDURE, (void *) &f)) f = NULL; if (f == NULL) { /* Some OS's require a _ to be prepended to the symbol name (Darwin, etc). */ sprintf(workbuf, "_%s_start", name); if (shl_findsym(&hand, workbuf, (short) TYPE_PROCEDURE, (void *) &f)) f = NULL; } if (f == NULL) { shl_unload(hand); return MOD_NOSTARTDEF; } # endif /* MOD_USE_SHL */ # ifdef MOD_USE_DYLD ret = NSCreateObjectFileImageFromFile(workbuf, &file); if (ret != NSObjectFileImageSuccess) return "Can't load module."; hand = NSLinkModule(file, workbuf, DYLDFLAGS); sprintf(workbuf, "_%s_start", name); sym = NSLookupSymbolInModule(hand, workbuf); if (sym) f = (Function) NSAddressOfSymbol(sym); else f = NULL; if (f == NULL) { NSUnLinkModule(hand, NSUNLINKMODULE_OPTION_NONE); return MOD_NOSTARTDEF; } # endif /* MOD_USE_DYLD */ # ifdef MOD_USE_LOADER hand = load(workbuf, LDR_NOFLAGS); if (hand == LDR_NULL_MODULE) return "Can't load module."; sprintf(workbuf, "%s_start", name); f = (Function) ldr_lookup_package(hand, workbuf); if (f == NULL) { sprintf(workbuf, "_%s_start", name); f = (Function) ldr_lookup_package(hand, workbuf); } if (f == NULL) { unload(hand); return MOD_NOSTARTDEF; } # endif /* MOD_USE_LOADER */ # ifdef MOD_USE_DL hand = dlopen(workbuf, DLFLAGS); if (!hand) return dlerror(); sprintf(workbuf, "%s_start", name); f = (Function) dlsym(hand, workbuf); if (f == NULL) { sprintf(workbuf, "_%s_start", name); f = (Function) dlsym(hand, workbuf); } if (f == NULL) { dlclose(hand); return MOD_NOSTARTDEF; } # endif /* MOD_USE_DL */ #endif /* !STATIC */ #ifdef STATIC for (sl = static_modules; sl && strcasecmp(sl->name, name); sl = sl->next); if (!sl) return "Unknown module."; f = (Function) sl->func; #endif /* STATIC */ p = nmalloc(sizeof(module_entry)); if (p == NULL) return "Malloc error"; p->name = nmalloc(strlen(name) + 1); strcpy(p->name, name); p->major = 0; p->minor = 0; #ifndef STATIC p->hand = hand; #endif p->funcs = 0; p->next = module_list; module_list = p; e = (((char *(*)()) f) (global_table)); if (e) { module_list = module_list->next; nfree(p->name); nfree(p); return e; } check_tcl_load(name); if (exist_lang_section(name)) putlog(LOG_MISC, "*", MOD_LOADED_WITH_LANG, name); else putlog(LOG_MISC, "*", MOD_LOADED, name); return NULL; } char *module_unload(char *name, char *user) { module_entry *p = module_list, *o = NULL; char *e; Function *f; while (p) { if ((p->name != NULL) && !strcmp(name, p->name)) { dependancy *d; for (d = dependancy_list; d; d = d->next) if (d->needed == p) return MOD_NEEDED; f = p->funcs; if (f && !f[MODCALL_CLOSE]) return MOD_NOCLOSEDEF; if (f) { check_tcl_unld(name); e = (((char *(*)()) f[MODCALL_CLOSE]) (user)); if (e != NULL) return e; #ifndef STATIC # ifdef MOD_USE_SHL shl_unload(p->hand); # endif # ifdef MOD_USE_DYLD NSUnLinkModule(p->hand, NSUNLINKMODULE_OPTION_NONE); # endif # ifdef MOD_USE_LOADER unload(p->hand); # endif # ifdef MOD_USE_DL dlclose(p->hand); # endif #endif /* !STATIC */ } nfree(p->name); if (o == NULL) module_list = p->next; else o->next = p->next; nfree(p); putlog(LOG_MISC, "*", "%s %s", MOD_UNLOADED, name); return NULL; } o = p; p = p->next; } return MOD_NOSUCH; } module_entry *module_find(char *name, int major, int minor) { module_entry *p; for (p = module_list; p && p->name; p = p->next) { if ( (((major < p->major) || !major) || ((major == p->major) && (minor <= p->minor))) && !strcasecmp(name, p->name) ) return p; } return NULL; } static int module_rename(char *name, char *newname) { module_entry *p; for (p = module_list; p; p = p->next) if (!strcasecmp(newname, p->name)) return 0; for (p = module_list; p && p->name; p = p->next) { if (!strcasecmp(name, p->name)) { nfree(p->name); p->name = nmalloc(strlen(newname) + 1); strcpy(p->name, newname); return 1; } } return 0; } Function *module_depend(char *name1, char *name2, int major, int minor) { module_entry *p = module_find(name2, major, minor); module_entry *o = module_find(name1, 0, 0); dependancy *d; if (!p) { if (module_load(name2)) return 0; p = module_find(name2, major, minor); } if (!p || !o) return 0; d = nmalloc(sizeof(dependancy)); d->needed = p; d->needing = o; d->next = dependancy_list; d->major = major; d->minor = minor; dependancy_list = d; return p->funcs ? p->funcs : (Function *) 1; } int module_undepend(char *name1) { int ok = 0; module_entry *p = module_find(name1, 0, 0); dependancy *d = dependancy_list, *o = NULL; if (p == NULL) return 0; while (d != NULL) { if (d->needing == p) { if (o == NULL) { dependancy_list = d->next; } else { o->next = d->next; } nfree(d); if (o == NULL) d = dependancy_list; else d = o->next; ok++; } else { o = d; d = d->next; } } return ok; } void *mod_malloc(int size, const char *modname, const char *filename, int line) { #ifdef DEBUG_MEM char x[100], *p; p = strrchr(filename, '/'); egg_snprintf(x, sizeof x, "%s:%s", modname, p ? p + 1 : filename); x[19] = 0; return n_malloc(size, x, line); #else return nmalloc(size); #endif } void *mod_realloc(void *ptr, int size, const char *modname, const char *filename, int line) { #ifdef DEBUG_MEM char x[100], *p; p = strrchr(filename, '/'); egg_snprintf(x, sizeof x, "%s:%s", modname, p ? p + 1 : filename); x[19] = 0; return n_realloc(ptr, size, x, line); #else return nrealloc(ptr, size); #endif } void mod_free(void *ptr, const char *modname, const char *filename, int line) { char x[100], *p; p = strrchr(filename, '/'); egg_snprintf(x, sizeof x, "%s:%s", modname, p ? p + 1 : filename); x[19] = 0; n_free(ptr, x, line); } /* Hooks, various tables of functions to call on certain events */ void add_hook(int hook_num, Function func) { if (hook_num < REAL_HOOKS) { struct hook_entry *p; for (p = hook_list[hook_num]; p; p = p->next) if (p->func == func) return; /* Don't add it if it's already there */ p = nmalloc(sizeof(struct hook_entry)); p->next = hook_list[hook_num]; hook_list[hook_num] = p; p->func = func; } else switch (hook_num) { case HOOK_ENCRYPT_PASS: encrypt_pass = (void (*)(char *, char *)) func; break; case HOOK_ENCRYPT_PASS2: encrypt_pass2 = (char *(*)(char *)) func; break; case HOOK_VERIFY_PASS2: verify_pass2 = (char *(*)(char *, char*)) func; break; case HOOK_ENCRYPT_STRING: encrypt_string = (char *(*)(char *, char *)) func; break; case HOOK_DECRYPT_STRING: decrypt_string = (char *(*)(char *, char *)) func; break; case HOOK_SHAREOUT: shareout = (void (*)()) func; break; case HOOK_SHAREIN: sharein = (void (*)(int, char *)) func; break; case HOOK_QSERV: if (qserver == (void (*)(int, char *, int)) null_func) qserver = (void (*)(int, char *, int)) func; break; case HOOK_ADD_MODE: if (add_mode == (void (*)()) null_func) add_mode = (void (*)()) func; break; /* special hook */ case HOOK_RFC_CASECMP: if (func == NULL) { rfc_casecmp = egg_strcasecmp; rfc_ncasecmp = (int (*)(const char *, const char *, int)) egg_strncasecmp; rfc_tolower = tolower; rfc_toupper = toupper; } else { rfc_casecmp = _rfc_casecmp; rfc_ncasecmp = _rfc_ncasecmp; rfc_tolower = _rfc_tolower; rfc_toupper = _rfc_toupper; } break; case HOOK_MATCH_NOTEREJ: if (match_noterej == false_func) match_noterej = (int (*)(struct userrec *, char *)) func; break; case HOOK_DNS_HOSTBYIP: if (dns_hostbyip == core_dns_hostbyip) dns_hostbyip = (void (*)(sockname_t *)) func; break; case HOOK_DNS_IPBYHOST: if (dns_ipbyhost == core_dns_ipbyhost) dns_ipbyhost = (void (*)(char *)) func; break; case HOOK_DCC_TELNET_HOSTRESOLVED: webui_dcc_telnet_hostresolved = (void (*)(int, int)) func; break; case HOOK_WEBUI_FRAME: webui_frame = (size_t (*)(char **, char *, size_t)) func; break; case HOOK_WEBUI_UNFRAME: webui_unframe = (void (*)(int, char *, int *)) func; break; } } void del_hook(int hook_num, Function func) { if (hook_num < REAL_HOOKS) { struct hook_entry *p = hook_list[hook_num], *o = NULL; while (p) { if (p->func == func) { if (o == NULL) hook_list[hook_num] = p->next; else o->next = p->next; nfree(p); break; } o = p; p = p->next; } } else switch (hook_num) { case HOOK_ENCRYPT_PASS: if (encrypt_pass == (void (*)(char *, char *)) func) encrypt_pass = (void (*)(char *, char *)) null_func; break; case HOOK_ENCRYPT_PASS2: if (encrypt_pass2 == (char *(*)(char *)) func) encrypt_pass2 = (char *(*)(char *)) null_func; break; case HOOK_VERIFY_PASS2: if (verify_pass2 == (char *(*)(char *, char *)) func) verify_pass2 = (char *(*)(char *, char *)) null_func; break; case HOOK_ENCRYPT_STRING: if (encrypt_string == (char *(*)(char *, char *)) func) encrypt_string = (char *(*)(char *, char *)) null_func; break; case HOOK_DECRYPT_STRING: if (decrypt_string == (char *(*)(char *, char *)) func) decrypt_string = (char *(*)(char *, char *)) null_func; break; case HOOK_SHAREOUT: if (shareout == (void (*)()) func) shareout = null_func; break; case HOOK_SHAREIN: if (sharein == (void (*)(int, char *)) func) sharein = null_share; break; case HOOK_QSERV: if (qserver == (void (*)(int, char *, int)) func) qserver = null_func; break; case HOOK_ADD_MODE: if (add_mode == (void (*)()) func) add_mode = null_func; break; case HOOK_MATCH_NOTEREJ: if (match_noterej == (int (*)(struct userrec *, char *)) func) match_noterej = false_func; break; case HOOK_DNS_HOSTBYIP: if (dns_hostbyip == (void (*)(sockname_t *)) func) dns_hostbyip = core_dns_hostbyip; break; case HOOK_DNS_IPBYHOST: if (dns_ipbyhost == (void (*)(char *)) func) dns_ipbyhost = core_dns_ipbyhost; break; case HOOK_DCC_TELNET_HOSTRESOLVED: if (webui_dcc_telnet_hostresolved == (void (*)(int, int)) func) webui_dcc_telnet_hostresolved = (void (*)(int, int)) null_func; break; case HOOK_WEBUI_FRAME: if (webui_frame == (size_t (*)(char **, char *, size_t)) func) webui_frame = (size_t (*)(char**, char *, size_t)) null_func; break; case HOOK_WEBUI_UNFRAME: if (webui_unframe == (void (*)(int, char *, int *)) func) webui_unframe = (void (*)(int, char *, int *)) null_func; break; } } void do_module_report(int idx, int details, char *which) { module_entry *p = module_list; if (p && !which) dprintf(idx, "Loaded module information:\n"); for (; p; p = p->next) { if (!which || !strcasecmp(which, p->name)) { dependancy *d; if (details) dprintf(idx, " Module: %s, v %d.%d\n", p->name ? p->name : "CORE", p->major, p->minor); if (details > 1) { for (d = dependancy_list; d; d = d->next) if (d->needing == p) dprintf(idx, " requires: %s, v %d.%d\n", d->needed->name, d->major, d->minor); } if (p->funcs) { Function f = p->funcs[MODCALL_REPORT]; if (f != NULL) f(idx, details); } if (which) return; } } if (which) dprintf(idx, "No such module.\n"); } eggheads-eggdrop-9c00fe1/src/modules.h000066400000000000000000000046621524730042700177430ustar00rootroot00000000000000/* * modules.h * support for modules in eggdrop * * by Darrin Smith (beldin@light.iinet.net.au) */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_MODULE_H #define _EGG_MODULE_H /* Module related structures */ #include "mod/modvals.h" #ifndef MAKING_NUMMODS /* Modules specific functions and functions called by Eggdrop */ void do_module_report(int, int, char *); int module_register(char *, Function *, int, int); const char *module_load(char *); char *module_unload(char *, char *); module_entry *module_find(char *, int, int); Function *module_depend(char *, char *, int, int); int module_undepend(char *); void *mod_malloc(int, const char *, const char *, int); void *mod_realloc(void *, int, const char *, const char *, int); void mod_free(void *, const char *, const char *, int); void add_hook(int, Function); void del_hook(int, Function); void *get_next_hook(int, void *); extern struct hook_entry { struct hook_entry *next; Function func; } *hook_list[REAL_HOOKS]; #define call_hook(x) do { \ struct hook_entry *p, *pn; \ \ for (p = hook_list[x]; p; p = pn) { \ pn = p->next; \ p->func(); \ } \ } while (0) #endif typedef struct _dependancy { struct _module_entry *needed; struct _module_entry *needing; struct _dependancy *next; int major; int minor; } dependancy; extern dependancy *dependancy_list; #endif /* _EGG_MODULE_H */ eggheads-eggdrop-9c00fe1/src/net.c000066400000000000000000001614071524730042700170550ustar00rootroot00000000000000/* * net.c -- handles: * all raw network i/o */ /* * This is hereby released into the public domain. * Robey Pointer, robey@netcom.com * * Changes after Feb 23, 1999 Copyright Eggheads Development Team * * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "main.h" #include "modules.h" #include #include #if HAVE_SYS_SELECT_H # include #endif #include #include #include #if HAVE_UNISTD_H # include #endif #include #ifdef TLS # include #endif extern struct dcc_t *dcc; extern int backgrd, use_stderr, resolve_timeout, dcc_total; extern unsigned long otraffic_irc_today, otraffic_bn_today, otraffic_dcc_today, otraffic_filesys_today, otraffic_trans_today, otraffic_unknown_today; extern time_t online_since; char nat_ip[INET_ADDRSTRLEN] = ""; /* Public IPv4 to report for systems behind NAT */ char nat_ip_string[11]; char listen_ip[121] = ""; /* IP (or hostname) for listening sockets */ char vhost[121] = ""; /* IPv4 vhost for outgoing connections */ #ifdef IPV6 char vhost6[121] = ""; /* IPv6 vhost for outgoing connections */ int pref_af = 0; /* Prefer IPv6 over IPv4? */ #endif char firewall[121] = ""; /* Socks server for firewall. */ int firewallport = 1080; /* Default port of socks 4/5 firewalls. */ char botuser[USERLEN + 1] = "eggdrop"; /* Username of the user running the bot*/ int dcc_sanitycheck = 0; /* Do some sanity checking on dcc connections. */ sock_list *socklist = NULL; /* Enough to be safe. */ sigjmp_buf alarmret; /* Env buffer for alarm() returns. */ /* Types of proxies */ #define PROXY_SOCKS 1 #define PROXY_SUN 2 /* I need an UNSIGNED long for dcc type stuff */ IP my_atoul(char *s) { IP ret = 0; while ((*s >= '0') && (*s <= '9')) { ret *= 10; ret += ((*s) - '0'); s++; } return ret; } int expmem_net() { int i, tot = 0; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { if (!(td->socklist[i].flags & (SOCK_UNUSED | SOCK_TCL))) { if (td->socklist[i].handler.sock.inbuf != NULL) tot += strlen(td->socklist[i].handler.sock.inbuf) + 1; if (td->socklist[i].handler.sock.outbuf != NULL) tot += td->socklist[i].handler.sock.outbuflen; } } return tot; } /* Extract the IP address from a sockaddr struct and convert it * to presentation format. */ char *iptostr(struct sockaddr *sa) { static char s[EGG_INET_ADDRSTRLEN] = ""; #ifdef IPV6 if (sa->sa_family == AF_INET6) inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, s, sizeof s); else #endif inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr.s_addr, s, sizeof s); return s; } /* Fills in a sockname struct with the given server and port. If the string * pointed by src isn't an IP address and allowres is not null, the function * will assume it's a hostname and will attempt to resolve it. This is * convenient, but you should use the async dns functions where possible, to * avoid blocking the bot while the lookup is performed. */ int setsockname(sockname_t *addr, char *src, int port, int allowres) { char *endptr, *src2 = src; long val; IP ip; volatile int af = AF_UNSPEC; char ip2[EGG_INET_ADDRSTRLEN]; #ifdef IPV6 volatile int pref; struct addrinfo *res0 = NULL, *res; int error; #else struct hostent *hp; int i, count; #endif /* DCC CHAT ip is expressed as integer but inet_pton() only accepts dotted * addresses */ val = strtol(src, &endptr, 10); if (val && !*endptr) { ip = htonl(val); if (inet_ntop(AF_INET, &ip, ip2, sizeof ip2)) { debug2("net: setsockname(): ip %s -> %s", src, ip2); src2 = ip2; } } #ifdef IPV6 /* Clean start */ egg_bzero(addr, sizeof(sockname_t)); pref = pref_af ? AF_INET6 : AF_INET; if (pref == AF_INET) { if (inet_pton(AF_INET, src2, &addr->addr.s4.sin_addr) == 1) af = AF_INET; else if (inet_pton(AF_INET6, src2, &addr->addr.s6.sin6_addr) == 1) af = AF_INET6; else af = AF_UNSPEC; } else { if (inet_pton(AF_INET6, src2, &addr->addr.s6.sin6_addr) == 1) af = AF_INET6; else if (inet_pton(AF_INET, src2, &addr->addr.s4.sin_addr) == 1) af = AF_INET; else af = AF_UNSPEC; } if (af == AF_UNSPEC && allowres && *src) { /* src is a hostname. Attempt to resolve it.. */ if (!sigsetjmp(alarmret, 1)) { alarm(resolve_timeout); error = getaddrinfo(src, NULL, NULL, &res0); if (!error) { for (res = res0; res; res = res->ai_next) { if (res == res0 || res->ai_family == (pref_af ? AF_INET6 : AF_INET)) { af = res->ai_family; memcpy(&addr->addr.sa, res->ai_addr, res->ai_addrlen); if (res->ai_family == (pref_af ? AF_INET6 : AF_INET)) { break; } } } if (res0) /* The behavior of freeadrinfo(NULL) is left unspecified by RFCs * 2553 and 3493. Avoid to be compatible with all OSes. */ freeaddrinfo(res0); } else if (error == EAI_NONAME) debug1("net: setsockname(): getaddrinfo(): hostname %s not known", src); else debug1("net: setsockname(): getaddrinfo(): error = %s", gai_strerror(error)); alarm(0); } else { debug1("net: setsockname(): getaddrinfo(): hostname %s resolve timeout", src); } } addr->family = (af == AF_UNSPEC) ? pref : af; addr->addr.sa.sa_family = addr->family; if (addr->family == AF_INET6) { addr->addrlen = sizeof(struct sockaddr_in6); addr->addr.s6.sin6_port = htons(port); addr->addr.s6.sin6_family = AF_INET6; } else { addr->addrlen = sizeof(struct sockaddr_in); addr->addr.s4.sin_port = htons(port); addr->addr.s4.sin_family = AF_INET; } #else egg_bzero(addr, sizeof(sockname_t)); /* If it's not an IPv4 address, check if its IPv6 (so it can fail/error * appropriately). If it's not, and allowres is 1, use gethostbyname() * to try and resolve. If allowres is 0, return AF_UNSPEC to allow * dns.mod to do it's thing. * Also, because we can't be sure inet_pton exists on the system, we * have to resort to hackishly counting :s to see if its IPv6 or not. * Go internet. */ if (!inet_pton(AF_INET, src2, &addr->addr.s4.sin_addr)) { /* Boring way to count :s */ count = 0; for (i = 0; src[i]; i++) { if (src[i] == ':') { count++; if (count == 2) break; } } if (count > 1) { putlog(LOG_MISC, "*", "ERROR: This looks like an IPv6 address, \ but this Eggdrop was not compiled with IPv6 support."); af = AF_UNSPEC; } else if (allowres) { /* src is a hostname. Attempt to resolve it.. */ if (!sigsetjmp(alarmret, 1)) { alarm(resolve_timeout); hp = gethostbyname(src); alarm(0); } else hp = NULL; if (hp) { memcpy(&addr->addr.s4.sin_addr, hp->h_addr_list[0], hp->h_length); af = hp->h_addrtype; } } else af = AF_UNSPEC; } else af = AF_INET; addr->family = addr->addr.s4.sin_family = AF_INET; addr->addr.sa.sa_family = addr->family; addr->addrlen = sizeof(struct sockaddr_in); addr->addr.s4.sin_port = htons(port); #endif return af; } /* Get socket address to bind to for outbound connections */ void getvhost(sockname_t *addr, int af) { char *h = NULL; if (af == AF_INET) h = vhost; #ifdef IPV6 else h = vhost6; #endif if (!h || !h[0] || setsockname(addr, (h ? h : ""), 0, 1) != af) setsockname(addr, (af == AF_INET ? "0.0.0.0" : "::"), 0, 0); /* Remember this 'self-lookup failed' thingie? I have good news - you won't see it again ;) */ } /* Sets/Unsets options for a specific socket. * * Returns: 0 - on success * -1 - socket not found * -2 - illegal operation */ int sockoptions(int sock, int operation, int sock_options) { int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) if ((td->socklist[i].sock == sock) && !(td->socklist[i].flags & SOCK_UNUSED)) { if (operation == EGG_OPTION_SET) td->socklist[i].flags |= sock_options; else if (operation == EGG_OPTION_UNSET) td->socklist[i].flags &= ~sock_options; else return -2; return 0; } return -1; } /* Return a free entry in the socket entry */ int allocsock(int sock, int options) { int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { if (td->socklist[i].flags & SOCK_UNUSED) { /* yay! there is table space */ td->socklist[i].handler.sock.inbuf = NULL; td->socklist[i].handler.sock.outbuf = NULL; td->socklist[i].handler.sock.inbuflen = 0; td->socklist[i].handler.sock.outbuflen = 0; td->socklist[i].flags = options; td->socklist[i].sock = sock; #ifdef TLS td->socklist[i].ssl = 0; #endif return i; } } /* Try again if enlarging socketlist works */ if (increase_socks_max()) return -1; else return allocsock(sock, options); } /* Return a free entry in the socket entry for a tcl socket * * alloctclsock() can be called by Tcl threads */ int alloctclsock(int sock, int mask, Tcl_FileProc *proc, ClientData cd) { int f = -1; int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { if (td->socklist[i].flags & SOCK_UNUSED) { if (f == -1) f = i; } else if ((td->socklist[i].flags & SOCK_TCL) && td->socklist[i].sock == sock) { f = i; break; } } if (f != -1) { td->socklist[f].sock = sock; td->socklist[f].flags = SOCK_TCL; td->socklist[f].handler.tclsock.mask = mask; td->socklist[f].handler.tclsock.proc = proc; td->socklist[f].handler.tclsock.cd = cd; return f; } /* Try again if enlarging socketlist works */ if (increase_socks_max()) return -1; else return alloctclsock(sock, mask, proc, cd); } /* Request a normal socket for i/o */ void setsock(int sock, int options) { int i = allocsock(sock, options), parm = 1; struct threaddata *td = threaddata(); struct linger linger = {0}; if (i == -1) { putlog(LOG_MISC, "*", "Sockettable full."); return; } if (((sock != STDOUT) || backgrd) && !(td->socklist[i].flags & SOCK_NONSOCK)) { if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &parm, sizeof parm)) debug2("net: setsock(): setsockopt() s %i level SOL_SOCKET optname SO_KEEPALIVE error %s", sock, strerror(errno)); if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger))) debug2("net: setsock(): setsockopt() s %i level SOL_SOCKET optname SO_LINGER error %s", sock, strerror(errno)); /* Turn off Nagle's algorithm, see man tcp */ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &parm, sizeof parm)) debug2("net: setsock(): setsockopt() s %i level IPPROTO_TCP optname TCP_NODELAY error %s", sock, strerror(errno)); } if (options & SOCK_LISTEN) { /* Tris says this lets us grab the same port again next time */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &parm, sizeof parm)) debug2("net: setsock(): setsockopt() s %i level SOL_SOCKET optname SO_REUSEADDR error %s", sock, strerror(errno)); } /* Yay async i/o ! */ if ((sock != STDOUT) || backgrd) fcntl(sock, F_SETFL, O_NONBLOCK); } int getsock(int af, int options) { int sock = socket(af, SOCK_STREAM, 0); if (sock >= 0) setsock(sock, options); else putlog(LOG_MISC, "*", "Warning: Can't create new socket: %s!", strerror(errno)); return sock; } /* Done with a socket */ void killsock(int sock) { int i; struct threaddata *td = threaddata(); /* Ignore invalid sockets and stdout/stderr. */ if ((sock < 0) || (sock == STDOUT) || (sock == STDERR)) return; for (i = 0; i < td->MAXSOCKS; i++) { if ((td->socklist[i].sock == sock) && !(td->socklist[i].flags & SOCK_UNUSED)) { if (!(td->socklist[i].flags & SOCK_TCL)) { /* nothing to free for tclsocks */ #ifdef TLS if (td->socklist[i].ssl) { SSL_shutdown(td->socklist[i].ssl); nfree(SSL_get_app_data(td->socklist[i].ssl)); SSL_free(td->socklist[i].ssl); td->socklist[i].ssl = NULL; } #endif close(td->socklist[i].sock); if (td->socklist[i].handler.sock.inbuf != NULL) { nfree(td->socklist[i].handler.sock.inbuf); td->socklist[i].handler.sock.inbuf = NULL; } if (td->socklist[i].handler.sock.outbuf != NULL) { nfree(td->socklist[i].handler.sock.outbuf); td->socklist[i].handler.sock.outbuf = NULL; td->socklist[i].handler.sock.outbuflen = 0; } } td->socklist[i].flags = SOCK_UNUSED; return; } } putlog(LOG_MISC, "*", "Warning: Attempt to kill un-allocated socket %d!", sock); } /* Done with a tcl socket * * killtclsock() can be called by Tcl threads */ void killtclsock(int sock) { int i; struct threaddata *td = threaddata(); if (sock < 0) return; for (i = 0; i < td->MAXSOCKS; i++) { if ((td->socklist[i].flags & SOCK_TCL) && td->socklist[i].sock == sock) { td->socklist[i].flags = SOCK_UNUSED; return; } } } /* Send connection request to proxy */ static int proxy_connect(int sock, sockname_t *addr) { sockname_t name; char host[121], s[256]; int i, port, proxy; struct threaddata *td = threaddata(); if (!firewall[0]) return -2; #ifdef IPV6 if (addr->family == AF_INET6) { putlog(LOG_MISC, "*", "Eggdrop doesn't support IPv6 connections " "through proxies yet."); return -1; } #endif if (firewall[0] == '!') { proxy = PROXY_SUN; strlcpy(host, &firewall[1], sizeof host); } else { proxy = PROXY_SOCKS; strcpy(host, firewall); } port = addr->addr.s4.sin_port; setsockname(&name, host, firewallport, 1); if (connect(sock, &name.addr.sa, name.addrlen) < 0 && errno != EINPROGRESS) return -1; if (proxy == PROXY_SOCKS) { for (i = 0; i < td->MAXSOCKS; i++) if (!(socklist[i].flags & SOCK_UNUSED) && socklist[i].sock == sock) socklist[i].flags |= SOCK_PROXYWAIT; /* drummer */ memcpy(host, &addr->addr.s4.sin_addr.s_addr, 4); egg_snprintf(s, sizeof s, "\004\001%c%c%c%c%c%c%s", port % 256, (port >> 8) % 256, host[0], host[1], host[2], host[3], botuser); tputs(sock, s, strlen(botuser) + 9); /* drummer */ } else if (proxy == PROXY_SUN) { inet_ntop(AF_INET, &addr->addr.s4.sin_addr, host, sizeof host); egg_snprintf(s, sizeof s, "%s %d\n", host, port); tputs(sock, s, strlen(s)); /* drummer */ } return sock; } /* FIXME: if we can break compatibility for 1.9 or 2.0, we can replace this * workaround with an additional port parameter for functions in need */ static int get_port_from_addr(const sockname_t *addr) { #ifdef IPV6 return ntohs((addr->family == AF_INET) ? addr->addr.s4.sin_port : addr->addr.s6.sin6_port); #else return ntohs(addr->addr.s4.sin_port); #endif } /* Check for O_NONBLOCK connect() EINPROGRESS could be ECONNREFUSED * eggdrop sockets are always O_NONBLOCK, see setsock() */ int connect_nonblock(int s, sockname_t *addr, int check_tcl_event_ident) { int rc, res, e; struct timeval tv; fd_set sockset; socklen_t res_len; rc = connect(s, &addr->addr.sa, addr->addrlen); /* To minimize a proven race condition, call ident here (especially when * rc < 0 and errno == EINPROGRESS) */ if (check_tcl_event_ident) { /* push/pop errno, better safe than sorry */ e = errno; check_tcl_event("ident"); errno = e; } if (rc < 0) { if (errno == EINPROGRESS) { /* Async connection... don't return socket descriptor * until after we confirm if it was successful or not */ tv.tv_sec = 0; tv.tv_usec = 500000; /* 0.5 second timeout, more than enough to detect * ECONNREFUSED */ FD_ZERO(&sockset); FD_SET(s, &sockset); select(s + 1, NULL, &sockset, NULL, &tv); res_len = sizeof(res); getsockopt(s, SOL_SOCKET, SO_ERROR, &res, &res_len); if (res == EINPROGRESS) /* Operation now in progress */ return s; /* This could probably fail somewhere */ if (res == ECONNREFUSED) { /* Connection refused */ debug2("net: attempted socket connection refused: %s:%i", iptostr(&addr->addr.sa), get_port_from_addr(addr)); errno = res; return -4; } if (res != 0) { debug1("net: getsockopt error %d", res); return -1; } return s; /* async success! */ } debug2("net: check_connect(): socket %i error %s", s, strerror(errno)); return -1; } return s; } /* Starts a connection attempt through a socket * * The server address should be filled in addr by setsockname() or by the * non-blocking dns functions and setsnport(). * * returns < 0 if connection refused: * -1 strerror() type error */ int open_telnet_raw(int sock, sockname_t *addr) { int i, j; sockname_t name; struct threaddata *td = threaddata(); for (i = 0; i < dcc_total; i++) if (dcc[i].sock == sock) { /* Got idx from sock ? */ #ifdef TLS debug5("net: open_telnet_raw(): idx %i host %s ip %s port %i ssl %i", i, dcc[i].host, iptostr(&addr->addr.sa), dcc[i].port, dcc[i].ssl); #else debug4("net: open_telnet_raw(): idx %i host %s ip %s port %i", i, dcc[i].host, iptostr(&addr->addr.sa), dcc[i].port); #endif break; } getvhost(&name, addr->family); if (bind(sock, &name.addr.sa, name.addrlen) < 0) { return -1; } for (j = 0; j < td->MAXSOCKS; j++) { if (!(socklist[j].flags & SOCK_UNUSED) && (socklist[j].sock == sock)) socklist[j].flags = (socklist[j].flags & ~SOCK_VIRTUAL) | SOCK_CONNECT; } if (addr->family == AF_INET && firewall[0]) return proxy_connect(sock, addr); return connect_nonblock(sock, addr, dcc[i].status & STAT_SERV); } /* Ordinary non-binary connection attempt * Return values: * >=0: connect successful, returned is the socket number * -1: look at errno or use strerror() * -2: lookup failed or server is not a valid IP string * -3: could not allocate socket * -4: connection failed */ int open_telnet(int idx, char *server, int port) { int ret; ret = setsockname(&dcc[idx].sockname, server, port, 1); if (ret == AF_UNSPEC) return -2; dcc[idx].port = port; dcc[idx].sock = getsock(ret, 0); if (dcc[idx].sock < 0) return -3; ret = open_telnet_raw(dcc[idx].sock, &dcc[idx].sockname); if (ret < 0) { if (findidx(dcc[idx].sock) >= 0) { killsock(dcc[idx].sock); } } return ret; } /* Returns a socket number for a listening socket that will accept any * connection on the given address. The address can be filled in by * setsockname(). */ int open_address_listen(sockname_t *addr) { int sock = 0; sock = getsock(addr->family, SOCK_LISTEN); if (sock < 0) return -1; #if defined IPV6 && IPV6_V6ONLY if (addr->family == AF_INET6) { int on = 0; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, sizeof(on)); } #endif if (bind(sock, &addr->addr.sa, addr->addrlen) < 0) { killsock(sock); return -2; } if (getsockname(sock, &addr->addr.sa, &addr->addrlen) < 0) { killsock(sock); return -1; } if (listen(sock, 1) < 0) { killsock(sock); return -1; } return sock; } /* Returns a socket number for a listening socket that will accept any * connection -- port # is returned in port */ int open_listen(int *port) { int sock; sockname_t name; (void) setsockname(&name, listen_ip, *port, 1); sock = open_address_listen(&name); if (name.addr.sa.sa_family == AF_INET) *port = ntohs(name.addr.s4.sin_port); #ifdef IPV6 else *port = ntohs(name.addr.s6.sin6_port); #endif return sock; } /* Short routine to answer a connect received on a listening socket. * Returned is the new socket. * If port is not NULL, it points to an integer to hold the port number * of the caller. */ int answer(int sock, sockname_t *caller, uint16_t *port, int binary) { int new_sock; caller->addrlen = sizeof(caller->addr); new_sock = accept(sock, &caller->addr.sa, &caller->addrlen); if (new_sock < 0) return -1; caller->family = caller->addr.sa.sa_family; if (port) { if (caller->family == AF_INET) *port = ntohs(caller->addr.s4.sin_port); #ifdef IPV6 else *port = ntohs(caller->addr.s6.sin6_port); #endif } setsock(new_sock, (binary ? SOCK_BINARY : 0)); return new_sock; } int getdccaddr(sockname_t *addr, char *s, size_t l) { return getdccfamilyaddr(addr, s, l, AF_UNSPEC); } /* Get DCC compatible address for a client to connect (e.g. 1660944385) * If addr is not NULL, it should point to the listening socket's address. * Otherwise, this function will try to figure out the public address of the * machine, using listen_ip and nat_ip. If restrict_af is set, it will limit * the possible IPs to the specified family. The result is a string usable * for DCC requests */ int getdccfamilyaddr(sockname_t *addr, char *s, size_t l, int restrict_af) { char h[256]; sockname_t name, *r = &name; int af = AF_UNSPEC; #ifdef IPV6 IP ip = 0; #endif if (addr) r = addr; else setsockname(r, listen_ip, 0, 1); if ( #ifdef IPV6 ((r->family == AF_INET6) && IN6_IS_ADDR_UNSPECIFIED(&r->addr.s6.sin6_addr)) || #endif (r->family == AF_INET && !r->addr.s4.sin_addr.s_addr)) { /* We can't send :: or 0.0.0.0 for dcc, so try to figure out some real address */ #ifdef IPV6 /* If it's listening on an IPv6 :: address, try using vhost6 as the source IP */ if (r->family == AF_INET6 && restrict_af != AF_INET) { if (inet_pton(AF_INET6, vhost6, &r->addr.s6.sin6_addr) != 1) { r = &name; gethostname(h, sizeof h); setsockname(r, h, 0, 1); if (r->family == AF_INET) { /* setsockname tries to resolve both ipv4 and ipv6. ipv4 dns resolution comes later in precedence, so if we get an ipv4 back, reset it to the original addr struct and try again */ if (addr) { r = addr; } else { setsockname(r, listen_ip, 0, 1); } } else af = AF_INET6; } } #endif /* If IPv6 didn't work or is disabled, or it's listening on an IPv4 0.0.0.0 address, try using vhost4 as the source */ if (!af #ifdef IPV6 && restrict_af != AF_INET6 #endif ) { if (inet_pton(AF_INET, vhost, &r->addr.s4.sin_addr) != 1) { /* And if THAT fails, try DNS resolution of hostname */ r = &name; gethostname(h, sizeof h); setsockname(r, h, 0, 1); } } } if ( #ifdef IPV6 ((r->family == AF_INET6) && IN6_IS_ADDR_UNSPECIFIED(&r->addr.s6.sin6_addr)) || ((r->family == AF_INET6) && (restrict_af == AF_INET)) || ((r->family == AF_INET) && (restrict_af == AF_INET6)) || #endif (!nat_ip_string[0] && (r->family == AF_INET) && !r->addr.s4.sin_addr.s_addr)) return 0; #ifdef IPV6 if (r->family == AF_INET6) { if (IN6_IS_ADDR_V4MAPPED(&r->addr.s6.sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(&r->addr.s6.sin6_addr)) { if (*nat_ip_string) strlcpy(s, nat_ip_string, l); else { memcpy(&ip, r->addr.s6.sin6_addr.s6_addr + 12, sizeof ip); snprintf(s, l, "%" PRIu32, ntohl(ip)); } } else inet_ntop(AF_INET6, &r->addr.s6.sin6_addr, s, l); } else #endif { if (*nat_ip_string) strlcpy(s, nat_ip_string, l); else snprintf(s, l, "%" PRIu32, ntohl(r->addr.s4.sin_addr.s_addr)); } return 1; } /* Builds the fd_sets for select(). Eggdrop only cares about readable * sockets, but tcl also cares for writable/exceptions. * preparefdset() can be called by Tcl Threads */ static int preparefdset(fd_set *fds, sock_list *slist, int slistmax, int tclonly, int tclmask) { int fd, i, maxfd = -1; FD_ZERO(fds); for (i = 0; i < slistmax; i++) { if (!(slist[i].flags & (SOCK_UNUSED | SOCK_VIRTUAL))) { if ((slist[i].sock == STDOUT) && !backgrd) fd = STDIN; else fd = slist[i].sock; /* * Looks like that having more than a call, in the same * program, to the FD_SET macro, triggers a bug in gcc. * SIGBUS crashing binaries used to be produced on a number * (prolly all?) of 64 bits architectures. * Make your best to avoid to make it happen again. * * ITE */ if (slist[i].flags & SOCK_TCL) { if (!(slist[i].handler.tclsock.mask & tclmask)) continue; } else if (tclonly) continue; if (fd > maxfd) maxfd = fd; FD_SET(fd, fds); } } return maxfd; } /* A safer version of write() that deals with partial writes. */ void safe_write(int fd, const void *buf, size_t count) { const char *bytes = buf; ssize_t ret; static int inhere = 0; do { if ((ret = write(fd, bytes, count)) > -1) { bytes += ret; count -= ret; } else if (errno != EINTR) { if (!inhere) { inhere = 1; putlog(LOG_MISC, "*", "Unexpected write() failure on attempt to write %zu bytes to fd %d: %i: %s.", count, fd, errno, strerror(errno)); inhere = 0; } break; } } while (count > 0); } /* Attempts to read from all sockets in slist (upper array boundary slistmax-1) * fills s with up to 511 bytes if available, and returns the array index * Also calls all handler procs for Tcl sockets * sockread() can be called by Tcl threads * * on EOF: returns -1, with socket in len * on socket error: returns -2 * if nothing is ready: returns -3 * tcl sockets busy: returns -5 */ int sockread(char *s, int *len, sock_list *slist, int slistmax, int tclonly) { struct timeval t; fd_set fdr, fdw, fde; int i, x, maxfd_r, maxfd_w, maxfd_e; int grab = READMAX, tclsock = -1, events = 0; struct threaddata *td = threaddata(); int maxfd; #ifdef EGG_TDNS int fd; struct dns_thread_node *dtn, *dtn_prev; void *res; #endif maxfd_r = preparefdset(&fdr, slist, slistmax, tclonly, TCL_READABLE); #ifdef EGG_TDNS for (dtn = dns_thread_head->next; dtn; dtn = dtn->next) { fd = dtn->fildes[0]; FD_SET(fd, &fdr); if (fd > maxfd_r) maxfd_r = fd; } #endif maxfd_w = preparefdset(&fdw, slist, slistmax, 1, TCL_WRITABLE); maxfd_e = preparefdset(&fde, slist, slistmax, 1, TCL_EXCEPTION); maxfd = maxfd_r; if (maxfd_w > maxfd) maxfd = maxfd_w; if (maxfd_e > maxfd) maxfd = maxfd_e; /* select() may modify the timeval argument - copy it */ 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) return -3; /* idle */ for (i = 0; i < slistmax; i++) { if (!tclonly && ((!(slist[i].flags & (SOCK_UNUSED | SOCK_TCL))) && ((FD_ISSET(slist[i].sock, &fdr)) || #ifdef TLS (slist[i].ssl && !SSL_is_init_finished(slist[i].ssl)) || #endif ((slist[i].sock == STDOUT) && (!backgrd) && (FD_ISSET(STDIN, &fdr)))))) { if (slist[i].flags & (SOCK_LISTEN | SOCK_CONNECT)) { /* Listening socket -- don't read, just return activity */ /* Same for connection attempt */ /* (for strong connections, require a read to succeed first) */ if (slist[i].flags & SOCK_PROXYWAIT) /* drummer */ /* Hang around to get the return code from proxy */ grab = 10; #ifdef TLS else if (!(slist[i].flags & SOCK_STRONGCONN) && (!(slist[i].ssl) || SSL_is_init_finished(slist[i].ssl))) { #else else if (!(slist[i].flags & SOCK_STRONGCONN)) { #endif debug1("net: connect! sock %d", slist[i].sock); s[0] = 0; *len = 0; return i; } } else if (slist[i].flags & SOCK_PASS) { s[0] = 0; *len = 0; return i; } errno = 0; if ((slist[i].sock == STDOUT) && !backgrd) x = read(STDIN, s, grab); else #ifdef TLS { if (slist[i].ssl) { x = SSL_read(slist[i].ssl, s, grab); 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; else if (err == SSL_ERROR_SYSCALL) { debug0("net: sockread(): SSL_read() SSL_ERROR_SYSCALL"); putlog(LOG_MISC, "*", "NET: SSL read failed. Non-SSL connection?"); } else { long err2 = ERR_get_error(); debug3("net: sockread(): SSL_read() error = %s (%i) (%li)", ERR_error_string(err2, 0), err, err2); if ((err == SSL_ERROR_SSL) && (ERR_GET_REASON(err2) == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE)) putlog(LOG_MISC, "*", "NET: SSL read failed. Peer did not return a certificate, which is mandatory due to ssl-verify settings."); } x = -1; } } else x = read(slist[i].sock, s, grab); } #else x = read(slist[i].sock, s, grab); #endif if (x <= 0) { /* eof */ if (errno != EAGAIN) { /* EAGAIN happens when the operation would * block on a non-blocking socket, if the * socket is going to die, it will die later, * otherwise it will connect. */ *len = slist[i].sock; slist[i].flags &= ~SOCK_CONNECT; debug1("net: eof!(read) socket %d", slist[i].sock); return -1; } else { debug3("sockread EAGAIN: %d %d (%s)", slist[i].sock, errno, strerror(errno)); continue; /* EAGAIN */ } } #ifdef TLS if (socklist[i].flags & SOCK_WS) webui_unframe(slist[i].sock, s, &x); #endif /* TLS */ s[x] = 0; *len = x; if (slist[i].flags & SOCK_PROXYWAIT) { debug2("net: socket: %d proxy errno: %d", slist[i].sock, s[1]); slist[i].flags &= ~(SOCK_CONNECT | SOCK_PROXYWAIT); switch (s[1]) { case 90: /* Success */ s[0] = 0; *len = 0; return i; case 91: /* Failed */ errno = ECONNREFUSED; break; case 92: /* No identd */ case 93: /* Identd said wrong username */ /* A better error message would be "socks misconfigured" * or "identd not working" but this is simplest. */ errno = ENETUNREACH; break; } *len = slist[i].sock; return -1; } return i; } else if (tclsock == -1 && (slist[i].flags & SOCK_TCL)) { events = FD_ISSET(slist[i].sock, &fdr) ? TCL_READABLE : 0; events |= FD_ISSET(slist[i].sock, &fdw) ? TCL_WRITABLE : 0; events |= FD_ISSET(slist[i].sock, &fde) ? TCL_EXCEPTION : 0; events &= slist[i].handler.tclsock.mask; if (events) tclsock = i; } } if (!tclonly) { s[0] = 0; *len = 0; } if (tclsock != -1) { (*slist[tclsock].handler.tclsock.proc)(slist[tclsock].handler.tclsock.cd, events); return -5; } #ifdef EGG_TDNS dtn_prev = dns_thread_head; for (dtn = dtn_prev->next; dtn; dtn = dtn->next) { fd = dtn->fildes[0]; if (FD_ISSET(fd, &fdr)) { pthread_mutex_lock(&dtn->mutex); if (*dtn->strerror) debug2("%s: hostname %s", dtn->strerror, dtn->host); if (dtn->type == DTN_TYPE_HOSTBYIP) call_hostbyip(&dtn->addr, dtn->host, !*dtn->strerror); else call_ipbyhost(dtn->host, &dtn->addr, !*dtn->strerror); pthread_mutex_unlock(&dtn->mutex); close(fd); if (pthread_join(dtn->thread_id, &res)) putlog(LOG_MISC, "*", "sockread(): pthread_join(): error = %s", strerror(errno)); dtn_prev->next = dtn->next; pthread_mutex_destroy(&dtn->mutex); nfree(dtn); dtn = dtn_prev; } dtn_prev = dtn; } #endif return -3; } /* sockgets: buffer and read from sockets * * Attempts to read from all registered sockets for up to one second. if * after one second, no complete data has been received from any of the * sockets, 's' will be empty, 'len' will be 0, and sockgets will return -3. * if there is returnable data received from a socket, the data will be * in 's' (null-terminated if non-binary), the length will be returned * in len, and the socket number will be returned. * normal sockets have their input buffered, and each call to sockgets * will return one line terminated with a '\n'. binary sockets are not * buffered and return whatever coems in as soon as it arrives. * listening sockets will return an empty string when a connection comes in. * connecting sockets will return an empty string on a successful connect, * or EOF on a failed connect. * if an EOF is detected from any of the sockets, that socket number will be * put in len, and -1 will be returned. * the maximum length of the string returned is 512 (including null) * * Returns -4 if we handled something that shouldn't be handled by the * dcc functions. Simply ignore it. * Returns -5 if tcl sockets are busy but not eggdrop sockets. */ int sockgets(char *s, int *len) { char xx[READMAX + 2], *p, *px, *p2; int ret, i, data = 0; size_t len2; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { /* Check for stored-up data waiting to be processed */ if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_TCL | SOCK_BUFFER)) && (socklist[i].handler.sock.inbuf != NULL)) { if (!(socklist[i].flags & SOCK_BINARY)) { /* IRC messages are always lines of characters terminated with a CR-LF * (Carriage Return - Line Feed) pair. */ p = strpbrk(socklist[i].handler.sock.inbuf, "\r\n"); if (p != NULL) { /* this function is used not only for irc connections. dont remove * empty lines for they could be important like for example for http * header termination. */ p2 = p; if (*p == '\r') p++; if (*p == '\n') p++; *p2 = 0; strlcpy(s, socklist[i].handler.sock.inbuf, READMAX + 1); if (*p) { len2 = strlen(p) + 1; px = nmalloc(len2); memcpy(px, p, len2); nfree(socklist[i].handler.sock.inbuf); socklist[i].handler.sock.inbuf = px; } else { nfree(socklist[i].handler.sock.inbuf); socklist[i].handler.sock.inbuf = NULL; } *len = strlen(s); return socklist[i].sock; } } else { /* Handling buffered binary data (must have been SOCK_BUFFER before). */ if (socklist[i].handler.sock.inbuflen <= READMAX) { *len = socklist[i].handler.sock.inbuflen; memcpy(s, socklist[i].handler.sock.inbuf, socklist[i].handler.sock.inbuflen); nfree(socklist[i].handler.sock.inbuf); socklist[i].handler.sock.inbuf = NULL; socklist[i].handler.sock.inbuflen = 0; } else { /* Split up into chunks of READMAX bytes. */ *len = READMAX; memcpy(s, socklist[i].handler.sock.inbuf, *len); memcpy(socklist[i].handler.sock.inbuf, socklist[i].handler.sock.inbuf + *len, *len); socklist[i].handler.sock.inbuflen -= *len; socklist[i].handler.sock.inbuf = nrealloc(socklist[i].handler.sock.inbuf, socklist[i].handler.sock.inbuflen); } return socklist[i].sock; } } /* Also check any sockets that might have EOF'd during write */ if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].flags & SOCK_EOFD)) { s[0] = 0; *len = socklist[i].sock; return -1; } } /* No pent-up data of any worth -- down to business */ *len = 0; ret = sockread(xx, len, socklist, td->MAXSOCKS, 0); if (ret < 0) { s[0] = 0; return ret; } /* sockread can return binary data while socket still has connectflag, process first */ if (socklist[ret].flags & SOCK_BINARY && *len > 0) { socklist[ret].flags &= ~SOCK_CONNECT; memcpy(s, xx, *len); return socklist[ret].sock; } /* Binary, listening and passed on sockets don't get buffered. */ if (socklist[ret].flags & SOCK_CONNECT) { if (socklist[ret].flags & SOCK_STRONGCONN) { socklist[ret].flags &= ~SOCK_STRONGCONN; /* Buffer any data that came in, for future read. */ socklist[ret].handler.sock.inbuflen = *len; socklist[ret].handler.sock.inbuf = nmalloc(*len + 1); /* It might be binary data. You never know. */ memcpy(socklist[ret].handler.sock.inbuf, xx, *len); socklist[ret].handler.sock.inbuf[*len] = 0; } socklist[ret].flags &= ~SOCK_CONNECT; s[0] = 0; return socklist[ret].sock; } if (socklist[ret].flags & (SOCK_LISTEN | SOCK_PASS | SOCK_TCL)) { s[0] = 0; /* for the dcc traffic counters in the mainloop */ return socklist[ret].sock; } if (socklist[ret].flags & SOCK_BUFFER) { socklist[ret].handler.sock.inbuf = nrealloc(socklist[ret].handler.sock.inbuf, socklist[ret].handler.sock.inbuflen + *len + 1); memcpy(socklist[ret].handler.sock.inbuf + socklist[ret].handler.sock.inbuflen, xx, *len); socklist[ret].handler.sock.inbuflen += *len; /* We don't know whether it's binary data. Make sure normal strings * will be handled properly later on too. */ socklist[ret].handler.sock.inbuf[socklist[ret].handler.sock.inbuflen] = 0; return -4; /* Ignore this one. */ } /* Might be necessary to prepend stored-up data! */ if (socklist[ret].handler.sock.inbuf != NULL) { p = socklist[ret].handler.sock.inbuf; 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); nfree(socklist[ret].handler.sock.inbuf); socklist[ret].handler.sock.inbuf = NULL; socklist[ret].handler.sock.inbuflen = 0; } else { p = socklist[ret].handler.sock.inbuf; socklist[ret].handler.sock.inbuflen = strlen(p) - READMAX; socklist[ret].handler.sock.inbuf = nmalloc(socklist[ret].handler.sock.inbuflen + 1); strcpy(socklist[ret].handler.sock.inbuf, p + READMAX); *(p + READMAX) = 0; strcpy(xx, p); nfree(p); /* (leave the rest to be post-pended later) */ } } /* Look for EOL marker; if it's there, i have something to show */ for (p = xx; *p != 0 ; p++) { if ((*p == '\r') || (*p == '\n')) { memcpy(s, xx, p - xx); s[p - xx] = 0; for (p++; (*p == '\r') || (*p == '\n'); p++); memmove(xx, p, strlen(p) + 1); data = 1; /* DCC_CHAT may now need to process a blank line */ break; } } /* NO! */ /* if (!s[0]) strcpy(s," "); */ if (!data) { s[0] = 0; if (strlen(xx) >= READMAX) { /* String is too long, so just insert fake \n */ strcpy(s, xx); xx[0] = 0; data = 1; } } *len = strlen(s); /* Anything left that needs to be saved? */ if (!xx[0]) { if (data) return socklist[ret].sock; else return -3; } /* Prepend old data back */ if (socklist[ret].handler.sock.inbuf != NULL) { p = socklist[ret].handler.sock.inbuf; len2 = strlen(xx); socklist[ret].handler.sock.inbuflen = len2 + strlen(p); socklist[ret].handler.sock.inbuf = nmalloc(socklist[ret].handler.sock.inbuflen + 1); 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); len2 = socklist[ret].handler.sock.inbuflen + 1; socklist[ret].handler.sock.inbuf = nmalloc(len2); memcpy(socklist[ret].handler.sock.inbuf, xx, len2); } if (data) return socklist[ret].sock; else return -3; } /* Dump something to a socket */ void tputs(int z, char *s, unsigned int len) { int i, x, idx; char *p, *s2 = 0; static int inhere = 0; struct threaddata *td = threaddata(); if (z < 0) /* um... HELLO?! sanity check please! */ return; if (((z == STDOUT) || (z == STDERR)) && (!backgrd || use_stderr)) { safe_write(z, s, len); return; } for (i = 0; i < td->MAXSOCKS; i++) { if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == z)) { for (idx = 0; idx < dcc_total; idx++) { if ((dcc[idx].sock == z) && dcc[idx].type && dcc[idx].type->name) { if (!strncmp(dcc[idx].type->name, "BOT", 3)) otraffic_bn_today += len; else if (!strcmp(dcc[idx].type->name, "SERVER")) otraffic_irc_today += len; else if (!strncmp(dcc[idx].type->name, "CHAT", 4)) otraffic_dcc_today += len; else if (!strncmp(dcc[idx].type->name, "FILES", 5)) otraffic_filesys_today += len; else if (!strcmp(dcc[idx].type->name, "SEND")) otraffic_trans_today += len; else if (!strcmp(dcc[idx].type->name, "FORK_SEND")) otraffic_trans_today += len; else if (!strncmp(dcc[idx].type->name, "GET", 3)) otraffic_trans_today += len; else otraffic_unknown_today += len; break; } } if (socklist[i].handler.sock.outbuf != NULL) { /* Already queueing: just add it */ p = nrealloc(socklist[i].handler.sock.outbuf, socklist[i].handler.sock.outbuflen + len); memcpy(p + socklist[i].handler.sock.outbuflen, s, len); socklist[i].handler.sock.outbuf = p; socklist[i].handler.sock.outbuflen += len; return; } #ifdef TLS if (!(socklist[i].flags & SOCK_WS)) s2 = s; else len = webui_frame(&s2, s, len); if (socklist[i].ssl) { ERR_clear_error(); x = SSL_write(socklist[i].ssl, s2, len); if (x < 0) { int err = SSL_get_error(socklist[i].ssl, x); if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { errno = EAGAIN; } else if (err == SSL_ERROR_ZERO_RETURN) { /* Peer sent close notify, lostdcc_deferred() was already * scheduled from ssl_info(). Don't queue more data. */ return; } else if (!inhere) { /* Out there, somewhere */ inhere = 1; debug1("tputs(): SSL error = %s", ERR_error_string(ERR_get_error(), 0)); inhere = 0; } x = -1; } } else /* not ssl, use regular write() */ #else s2 = s; #endif /* TLS */ /* Try. */ x = write(z, s2, len); if (x == -1) x = 0; if (x < len) { /* Socket is full, queue it */ socklist[i].handler.sock.outbuf = nmalloc(len - x); memcpy(socklist[i].handler.sock.outbuf, &s2[x], len - x); socklist[i].handler.sock.outbuflen = len - x; } return; } } /* Make sure we don't cause a crash by looping here */ if (!inhere) { inhere = 1; putlog(LOG_MISC, "*", "!!! writing to nonexistent socket: %d", z); s[len - 1] = 0; putlog(LOG_MISC, "*", "!-> '%s'", s); inhere = 0; } } /* tputs might queue data for sockets, let's dump as much of it as * possible. */ void dequeue_sockets() { int i, x; fd_set wfds; struct timeval tv; int maxfd = -1; struct threaddata *td = threaddata(); /* ^-- start poptix test code, this should avoid writes to sockets not ready to be written to. */ FD_ZERO(&wfds); tv.tv_sec = 0; tv.tv_usec = 0; /* we only want to see if it's ready for writing, no need to actually wait.. */ for (i = 0; i < td->MAXSOCKS; i++) if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_TCL)) && (socklist[i].handler.sock.outbuf != NULL) #ifdef TLS && !(socklist[i].ssl && !SSL_is_init_finished(socklist[i].ssl)) #endif ) { if (socklist[i].sock > maxfd) maxfd = socklist[i].sock; FD_SET(socklist[i].sock, &wfds); } if (maxfd < 0) return; /* nothing to write */ x = select((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG5 &tv); /* end poptix */ if (x <= 0) return; for (i = 0; i < td->MAXSOCKS; i++) { if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_TCL)) && (socklist[i].handler.sock.outbuf != NULL) && (FD_ISSET(socklist[i].sock, &wfds))) { #ifdef CYGWIN_HACKS int res; socklen_t res_len; if (socklist[i].flags == SOCK_CONNECT) { res_len = sizeof(res); getsockopt(socklist[i].sock, SOL_SOCKET, SO_ERROR, &res, &res_len); if (res == ECONNREFUSED) { /* Connection refused */ int idx = findanyidx(socklist[i].sock); putlog(LOG_MISC, "*", "Connection refused: %s:%i", dcc[idx].host, dcc[idx].port); socklist[i].flags |= SOCK_EOFD; return ; } } #endif /* Trick tputs into doing the work */ errno = 0; #ifdef TLS if (socklist[i].ssl) { ERR_clear_error(); x = SSL_write(socklist[i].ssl, socklist[i].handler.sock.outbuf, socklist[i].handler.sock.outbuflen); if (x < 0) { int err = SSL_get_error(socklist[i].ssl, x); if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) errno = EAGAIN; else debug1("dequeue_sockets(): SSL error = %s", ERR_error_string(ERR_get_error(), 0)); x = -1; } } else #endif x = write(socklist[i].sock, socklist[i].handler.sock.outbuf, socklist[i].handler.sock.outbuflen); if ((x < 0) && (errno != EAGAIN) #ifdef EBADSLT && (errno != EBADSLT) #endif #ifdef ENOTCONN && (errno != ENOTCONN) #endif ) { /* This detects an EOF during writing */ debug3("net: eof!(write) socket %d (%s,%d)", socklist[i].sock, strerror(errno), errno); socklist[i].flags |= SOCK_EOFD; } else if (x == socklist[i].handler.sock.outbuflen) { /* If the whole buffer was sent, nuke it */ nfree(socklist[i].handler.sock.outbuf); socklist[i].handler.sock.outbuf = NULL; socklist[i].handler.sock.outbuflen = 0; } else if (x > 0) { char *p = socklist[i].handler.sock.outbuf; /* This removes any sent bytes from the beginning of the buffer */ socklist[i].handler.sock.outbuf = nmalloc(socklist[i].handler.sock.outbuflen - x); memcpy(socklist[i].handler.sock.outbuf, p + x, socklist[i].handler.sock.outbuflen - x); socklist[i].handler.sock.outbuflen -= x; nfree(p); } else { debug3("dequeue_sockets(): errno = %d (%s) on %d", errno, strerror(errno), socklist[i].sock); } /* All queued data was sent. Call handler if one exists and the * dcc entry wants it. */ if (!socklist[i].handler.sock.outbuf) { int idx = findanyidx(socklist[i].sock); if (idx > 0 && dcc[idx].type && dcc[idx].type->outdone) dcc[idx].type->outdone(idx); } } } } /* * Debugging stuff */ void tell_netdebug(int idx) { int i; char s[80]; struct threaddata *td = threaddata(); dprintf(idx, "Open sockets:"); for (i = 0; i < td->MAXSOCKS; i++) { if (!(socklist[i].flags & SOCK_UNUSED)) { sprintf(s, " %d", socklist[i].sock); if (socklist[i].flags & SOCK_BINARY) strcat(s, " (binary)"); if (socklist[i].flags & SOCK_LISTEN) strcat(s, " (listen)"); if (socklist[i].flags & SOCK_PASS) strcat(s, " (passed on)"); if (socklist[i].flags & SOCK_CONNECT) strcat(s, " (connecting)"); if (socklist[i].flags & SOCK_STRONGCONN) strcat(s, " (strong)"); if (socklist[i].flags & SOCK_NONSOCK) strcat(s, " (file)"); #ifdef TLS if (socklist[i].ssl) strcat(s, " (TLS)"); #endif if (socklist[i].flags & SOCK_TCL) strcat(s, " (tcl)"); if (!(socklist[i].flags & SOCK_TCL)) { if (socklist[i].handler.sock.inbuf != NULL) sprintf(&s[strlen(s)], " (inbuf: %04X)", (unsigned int) strlen(socklist[i].handler.sock.inbuf)); if (socklist[i].handler.sock.outbuf != NULL) sprintf(&s[strlen(s)], " (outbuf: %06lX)", socklist[i].handler.sock.outbuflen); } strcat(s, ","); dprintf(idx, "%s", s); } } dprintf(idx, " done.\n"); } /* Security-flavoured sanity checking on DCC connections of all sorts can be * done with this routine. Feed it the proper information from your DCC * before you attempt the connection, and this will make an attempt at * figuring out if the connection is really that person, or someone screwing * around. It's not foolproof, but anything that fails this check probably * isn't going to work anyway due to masquerading firewalls, NAT routers, * or bugs in mIRC. */ int sanitycheck_dcc(char *nick, char *from, char *ipaddy, char *port) { /* According to the latest RFC, the clients SHOULD be able to handle * DNS names that are up to 255 characters long. This is not broken. */ char badaddress[INET_ADDRSTRLEN]; #ifdef IPV6 sockname_t name; IP ip = 0; #else IP ip = my_atoul(ipaddy); #endif int prt = atoi(port); /* It is disabled HERE so we only have to check in *one* spot! */ if (!dcc_sanitycheck) return 1; if (prt < 1) { putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible port of %u!", nick, from, prt); return 0; } #ifdef IPV6 if (strchr(ipaddy, ':')) { if (inet_pton(AF_INET6, ipaddy, &name.addr.s6.sin6_addr) != 1) { putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an invalid IPv6 " "address of %s!", nick, from, ipaddy); return 0; } if (IN6_IS_ADDR_V4MAPPED(&name.addr.s6.sin6_addr)) { memcpy(&ip, name.addr.s6.sin6_addr.s6_addr + 12, sizeof ip); ip = ntohl(ip); } } #endif if (ip && inet_ntop(AF_INET, &ip, badaddress, sizeof badaddress) && (ip < (1 << 24))) { putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible IP of %s!", nick, from, badaddress); return 0; } return 1; } int hostsanitycheck_dcc(char *nick, char *from, sockname_t *ip, char *dnsname, char *prt) { char badaddress[EGG_INET_ADDRSTRLEN]; /* According to the latest RFC, the clients SHOULD be able to handle * DNS names that are up to 255 characters long. This is not broken. */ char hostn[256]; /* It is disabled HERE so we only have to check in *one* spot! */ if (!dcc_sanitycheck) return 1; strlcpy(badaddress, iptostr(&ip->addr.sa), sizeof badaddress); strlcpy(hostn, extracthostname(from), sizeof hostn); if (!strcasecmp(hostn, dnsname)) { putlog(LOG_DEBUG, "*", "DNS information for submitted IP checks out."); return 1; } if (!strcmp(badaddress, dnsname)) putlog(LOG_MISC, "*", "ALERT: (%s!%s) sent a DCC request with bogus IP " "information of %s port %s. %s does not resolve to %s!", nick, from, badaddress, prt, from, badaddress); else return 1; /* <- usually happens when we have * a user with an unresolved hostmask! */ return 0; } /* Checks whether the referenced socket has data queued. * * Returns true if the incoming/outgoing (depending on 'type') queues * contain data, otherwise false. */ int sock_has_data(int type, int sock) { int ret = 0, i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) if (!(socklist[i].flags & SOCK_UNUSED) && socklist[i].sock == sock) break; if (i < td->MAXSOCKS) { switch (type) { case SOCK_DATA_OUTGOING: ret = (socklist[i].handler.sock.outbuf != NULL); break; case SOCK_DATA_INCOMING: ret = (socklist[i].handler.sock.inbuf != NULL); break; } } else debug1("sock_has_data: could not find socket #%d, returning false.", sock); return ret; } /* flush_inbuf(): * checks if there's data in the incoming buffer of an connection * and flushes the buffer if possible * * returns: -1 if the dcc entry wasn't found * -2 if dcc[idx].type->activity doesn't exist and the data couldn't * be handled * 0 if buffer was empty * otherwise length of flushed buffer */ int flush_inbuf(int idx) { int i, len; char *inbuf; struct threaddata *td = threaddata(); Assert((idx >= 0) && (idx < dcc_total)); for (i = 0; i < td->MAXSOCKS; i++) { if ((dcc[idx].sock == socklist[i].sock) && !(socklist[i].flags & SOCK_UNUSED)) { len = socklist[i].handler.sock.inbuflen; if ((len > 0) && socklist[i].handler.sock.inbuf) { if (dcc[idx].type && dcc[idx].type->activity) { inbuf = socklist[i].handler.sock.inbuf; socklist[i].handler.sock.inbuf = NULL; dcc[idx].type->activity(idx, inbuf, len); nfree(inbuf); return len; } else return -2; } else return 0; } } return -1; } /* Find sock in socklist. * * Returns index in socklist or -1 if not found. */ int findsock(int sock) { int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) if (td->socklist[i].sock == sock) break; if (i == td->MAXSOCKS) return -1; return i; } /* Trace on my-ip and my-hostname variable to handle transition into vhost4/vhost6/listen-addr. */ char *traced_myiphostname(ClientData cd, Tcl_Interp *irp, EGG_CONST char *name1, EGG_CONST char *name2, int flags) { const char *value; if (Tcl_InterpDeleted(irp)) return NULL; /* Recover trace in case of unset. */ if (flags & TCL_TRACE_DESTROYED) { Tcl_TraceVar2(irp, name1, name2, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_myiphostname, cd); return NULL; } value = Tcl_GetVar2(irp, name1, name2, TCL_GLOBAL_ONLY); strlcpy(vhost, value, sizeof vhost); strlcpy(listen_ip, value, sizeof listen_ip); putlog(LOG_MISC, "*", "WARNING: You are using the DEPRECATED variable '%s' in your config file.\n", name1); putlog(LOG_MISC, "*", " To prevent future incompatibility, please use the vhost4/listen-addr variables instead.\n"); putlog(LOG_MISC, "*", " More information on this subject can be found in the eggdrop/doc/IPV6 file, or\n"); putlog(LOG_MISC, "*", " in the comments above those settings in the example eggdrop.conf that is included with Eggdrop.\n"); return NULL; } char *traced_natip(ClientData cd, Tcl_Interp *irp, EGG_CONST char *name1, EGG_CONST char *name2, int flags) { const char *value; int r; struct in_addr ia; /* Recover trace in case of unset. */ if (flags & TCL_TRACE_DESTROYED) { Tcl_TraceVar2(irp, name1, name2, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_natip, cd); return NULL; } value = Tcl_GetVar2(irp, name1, name2, TCL_GLOBAL_ONLY); if (*value) { r = inet_pton(AF_INET, value, &ia); if (!r) { if (!online_since) fatal("ERROR: nat-ip is not a valid IPv4 address", 0); return "nat-ip is not a valid IPv4 address"; } if (r < 0) { if (!online_since) fatal("ERROR: inet_pton(): nat-ip", 0); putlog(LOG_MISC, "*", "ERROR: inet_pton(): nat-ip %s", value); return strerror(errno); } snprintf(nat_ip_string, sizeof nat_ip_string, "%" PRIu32, ntohl(ia.s_addr)); } else *nat_ip_string = '\0'; return NULL; } eggheads-eggdrop-9c00fe1/src/proto.h000066400000000000000000000303761524730042700174370ustar00rootroot00000000000000/* * proto.h * prototypes for every function used outside its own module * * (i guess i'm not very modular, cuz there are a LOT of these.) * with full prototyping, some have been moved to other .h files * because they use structures in those * (saves including those .h files EVERY time) - Beldin */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_PROTO_H #define _EGG_PROTO_H #include "lush.h" #include "misc_file.h" #define dprintf dprintf_eggdrop struct chanset_t; /* keeps the compiler warnings down :) */ struct userrec; struct user_entry; struct maskrec; struct igrec; struct flag_record; struct list_type; struct tand_t_struct; #ifndef MAKING_MODS extern void (*encrypt_pass) (char *, char *); extern char *(*encrypt_pass2) (char *); extern char *(*verify_pass2) (char *, char *); extern char *(*encrypt_string) (char *, char *); extern char *(*decrypt_string) (char *, char *); extern int (*rfc_casecmp) (const char *, const char *); extern int (*rfc_ncasecmp) (const char *, const char *, int); extern int (*rfc_toupper) (int); extern int (*rfc_tolower) (int); extern int (*match_noterej) (struct userrec *, char *); extern void (*webui_dcc_telnet_hostresolved) (int, int); extern size_t (*webui_frame) (char **, char *, size_t); extern void (*webui_unframe) (int, char *, int *); #endif /* botcmd.c */ void bot_share(int, char *); int base64_to_int(char *); /* botnet.c */ void set_botnetnick(const char *); void answer_local_whom(int, int); char *lastbot(char *); int nextbot(char *); int in_chain(char *); void tell_bots(int); void tell_bottree(int, int); int botlink(char *, int, char *); int botunlink(int, char *, char *, char *); void dump_links(int); void addbot(char *, char *, char *, char, int, int); void updatebot(int, char *, char, int); void rembot(char *); struct tand_t_struct *findbot(char *); void unvia(int, struct tand_t_struct *); void check_botnet_pings(void); int partysock(char *, char *); int addparty(char *, char *, int, char, int, char *, int *); void remparty(char *, int); void partystat(char *, int, int, int); int partynick(char *, int, char *); int partyidle(char *, char *); void partysetidle(char *, int, int); void partyaway(char *, int, char *); void zapfbot(int); void tandem_relay(int, char *, int); int getparty(char *, int); /* botmsg.c */ int add_note(char *, char *, char *, int, int); int simple_sprintf(char *buf, const char *formatt, ...); void tandout_but(int, const char *format, ...) ATTRIBUTE_FORMAT(printf,2,3); char *int_to_base10(int); char *unsigned_int_to_base10(unsigned int); char *int_to_base64(unsigned int); /* chanprog.c */ void tell_verbose_uptime(int); void tell_verbose_status(int); void tell_settings(int); int logmodes(char *); int isowner(char *); const char *masktype(int); char *maskname(int); void reaffirm_owners(void); void add_hq_user(void); void rehash(void); void reload(void); void chanprog(void); void check_timers(void); void check_utimers(void); void rmspace(char *s); void check_timers(void); void set_chanlist(const char *host, struct userrec *rec); void clear_chanlist(void); void clear_chanlist_member(const char *nick); float getcputime(void); /* cmds.c */ int check_dcc_attrs(struct userrec *, int); int check_dcc_chanattrs(struct userrec *, char *, int, int); int check_int_range(char *value, int min, int max); int stripmodes(char *); const char *stripmasktype(int); char *check_validpass(struct userrec *, char *); void cmd_die(struct userrec *, int, char *); /* dcc.c */ void failed_link(int); void strip_mirc_codes(int, char *); int check_ansi(char *); void dupwait_notify(char *); #ifdef TLS int dcc_fingerprint(int); #endif void dcc_telnet_hostresolved2(int, int); /* dccutil.c */ int increase_socks_max(void); int findidx(int); int findanyidx(int); char *add_cr(char *); void dprint(int, char *, int); void dprintf (int, const char *format, ...) ATTRIBUTE_FORMAT(printf,2,3); void chatout (const char *format, ...) ATTRIBUTE_FORMAT(printf,1,2); extern void (*shareout) (); extern void (*sharein) (int, char *); void chanout_but(int x, int chan, const char *format, ...) ATTRIBUTE_FORMAT(printf,3,4); void dcc_chatter(int); void lostdcc(int); void lostdcc_deferred(int); void killtransfer(int); void removedcc(int); void makepass(char *); void tell_dcc(int); void not_away(int); void set_away(int, char *); void *_get_data_ptr(int, char *, int); void dcc_remove_lost(void); void do_boot(int, char *, char *); int detect_dcc_flood(time_t *, struct chat_info *, int); #define get_data_ptr(x) _get_data_ptr(x,__FILE__,__LINE__) void flush_lines(int, struct chat_info *); struct dcc_t *find_idx(int); int new_dcc(struct dcc_table *, int); void del_dcc(int); void changeover_dcc(int, struct dcc_table *, int); /* dns.c */ void init_tdns(); extern void (*dns_hostbyip) (sockname_t *); void core_dns_hostbyip(sockname_t *); void call_hostbyip(sockname_t *, char *, int); void call_ipbyhost(char *, sockname_t *, int); void dcc_dnshostbyip(sockname_t *); extern void (*dns_ipbyhost) (char *); void core_dns_ipbyhost(char *); void dcc_dnsipbyhost(char *); /* language.c */ char *get_language(int); int cmd_loadlanguage(struct userrec *, int, char *); void add_lang_section(char *); int del_lang_section(char *); int exist_lang_section(char *); /* main.c */ void fatal(const char *, int); int expected_memory(void); void eggAssert(const char *, int, const char *); void backup_userfile(void); int expmem_modules(int); /* match.c */ int casecharcmp(unsigned char, unsigned char); int charcmp(unsigned char, unsigned char); int _wild_match(unsigned char *, unsigned char *); int _wild_match_per(unsigned char *, unsigned char *, int (*)(unsigned char, unsigned char), int (*)(unsigned char, unsigned char), unsigned char *); int addr_match(char *, char *, int, int); int mask_match(char *, char *); int cidr_match(char *, char *, int); int cron_match(const char *, const char *); #define wild_match(a,b) _wild_match((unsigned char *)(a),(unsigned char *)(b)) #define wild_match_per(a,b) _wild_match_per((unsigned char *)(a), \ (unsigned char *)(b),casecharcmp,NULL,NULL) #define wild_match_partial_case(a,b) _wild_match_per((unsigned char *)(a), \ (unsigned char *)(b),casecharcmp,charcmp, \ (unsigned char *)strchr((b),' ')) #define match_addr(a,b) addr_match((char *)(a),(char *)(b),0,0) #define match_useraddr(a,b) addr_match((char *)(a),(char *)(b),1,0) #define cmp_masks(a,b) addr_match((char *)(a),(char *)(b),0,1) #define cmp_usermasks(a,b) addr_match((char *)(a),(char *)(b),1,1) /* mem.c */ void *n_malloc(int, const char *, int); void *n_realloc(void *, int, const char *, int); void n_free(void *, const char *, int); void tell_mem_status(char *); void tell_mem_status_dcc(int); void debug_mem_to_dcc(int); /* misc.c */ int egg_strcatn(char *, const char *, size_t); int my_strcpy(char *, const char *); void putlog(int type, char *chname, const char *format, ...) ATTRIBUTE_FORMAT(printf,3,4); void check_logsize(void); void splitc(char *, char *, char); void splitcn(char *, char *, char, size_t); void remove_crlf(char **); parsed_irc_t parse_irc(char *); char *join_str_array(char **, int, char *, char *, size_t); char *newsplit(char **); char *splitnick(char **); void stridx(char *, char *, int); void dumplots(int, const char *, const char *); void daysago(time_t, time_t, char *); void days(time_t, time_t, char *); void daysdur(time_t, time_t, char *); char *egg_uname(void); void help_subst(char *, char *, struct flag_record *, int, char *); void sub_lang(int, char *); void show_motd(int); void tellhelp(int, char *, struct flag_record *, int); void tellwildhelp(int, char *, struct flag_record *); void tellallhelp(int, char *, struct flag_record *); void showhelp(char *, char *, struct flag_record *, int); void rem_help_reference(char *); void add_help_reference(char *); void debug_help(int); void reload_help_data(void); char *extracthostname(char *); void show_banner(int i); void make_rand_str_from_chars(char *, int, char *); void make_rand_str(char *, int); int oatoi(const char *); int is_file(const char *); void logsuffix_change(char *); char *str_escape(const char *, const char, const char); char *strchr_unescape(char *, const char, const char); void str_unescape(char *, const char); int str_isdigit(const char *); void kill_bot(char *, char *); void maskaddr(const char *, char *, int); #define maskhost(a,b) maskaddr((a),(b),3) #define maskban(a,b) maskaddr((a),(b),3) int crypto_verify(const char *, const char *); /* net.c */ IP my_atoul(char *); void setsock(int, int); int allocsock(int, int); int alloctclsock(int, int, Tcl_FileProc *, ClientData); int getsock(int, int); void killsock(int); void killtclsock(int); int open_listen(int *); void getvhost(sockname_t *, int); int setsockname(sockname_t *, char *, int, int); int open_address_listen(sockname_t *); int connect_nonblock(int, sockname_t *, int); int open_telnet_raw(int, sockname_t *); int open_telnet(int, char *, int); int answer(int, sockname_t *, uint16_t *, int); int getdccaddr(sockname_t *, char *, size_t); int getdccfamilyaddr(sockname_t *, char *, size_t, int); void tputs(int, char *, unsigned int); void dequeue_sockets(void); int sockread(char *, int *, sock_list *, int, int); int sockgets(char *, int *); void tell_netdebug(int); int sanitycheck_dcc(char *, char *, char *, char *); int hostsanitycheck_dcc(char *, char *, sockname_t *, char *, char *); char *iptostr(struct sockaddr *); int sock_has_data(int, int); int sockoptions(int sock, int operation, int sock_options); int flush_inbuf(int idx); int findsock(int sock); void safe_write(int, const void *, size_t); /* tcl.c */ struct threaddata *threaddata(void); void init_threaddata(int); void protect_tcl(void); void unprotect_tcl(void); void do_tcl(char *, char *); int readtclprog(char *fname); /* tls.c */ #ifdef TLS void verify_cert_expiry(int); void ssl_cleanup(); int ssl_handshake(int, int, int, int, char *, IntFunc); char *ssl_fpconv(char *in, char *out); const char *ssl_getuid(int sock); char *ssl_getfp(int sock); #endif /* userent.c */ void list_type_kill(struct list_type *); int list_type_expmem(struct list_type *); int xtra_set(struct userrec *,struct user_entry *, void *); /* userrec.c */ struct userrec *adduser(struct userrec *, char *, char *, char *, int); void addhost_by_handle(char *, char *); void addaccount_by_handle(char *, char *); void clear_masks(struct maskrec *); void clear_userlist(struct userrec *); int u_pass_match(struct userrec *, char *); int delhost_by_handle(char *, char *); int delaccount_by_handle(char *, char *); int ishost_for_handle(char *, char *); int count_users(struct userrec *); int deluser(char *); void freeuser(struct userrec *); int change_handle(struct userrec *, char *); void correct_handle(char *); int write_user(struct userrec *, FILE *, int); int write_ignores(FILE *f, int); void write_userfile(int); void touch_laston(struct userrec *, char *, time_t); void user_del_chan(char *); int check_conflags(struct flag_record *fr, int md); /* users.c */ void addignore(char *, char *, char *, time_t); int delignore(char *); void tell_ignores(int, char *); int match_ignore(char *); void check_expired_ignores(void); void autolink_cycle(char *); void tell_file_stats(int, char *); void tell_user_ident(int, char *); void tell_users_match(int, char *, int, int, char *); int readuserfile(char *, struct userrec **); /* rfc1459.c */ int _rfc_casecmp(const char *, const char *); int _rfc_ncasecmp(const char *, const char *, int); int _rfc_toupper(int); int _rfc_tolower(int); #endif /* _EGG_PROTO_H */ eggheads-eggdrop-9c00fe1/src/rfc1459.c000066400000000000000000000113121524730042700173510ustar00rootroot00000000000000/* * rfc1459.c */ /* * Copyright (C) 1990 Jarkko Oikarinen * Copyright (C) 1999 - 2025 Eggheads Development Team * * This code was more or less cloned from the ircd-hybrid 5.3 source. * The original code was written by Otto Harkoonen and even though it * it not entirely in synch with section 2.2 of RFC1459 in that it * additionally defines carat as an uppercase version of tilde, it's * what is in the servers themselves so we're going with it this way. * * If for some reason someone who maintains the source for ircd decides * to change the code to be completely RFC compliant, the change here * would be absolutely minuscule. * * BTW, since carat characters are allowed in nicknames and tildes are * not, I strongly suggest that people convert to uppercase when doing * comparisons or creation of hash elements (which tcl laughably calls * arrays) to avoid making entries with impossible nicknames in them. * * --+ Dagmar */ #include "main.h" int _rfc_casecmp(const char *s1, const char *s2) { unsigned char *str1 = (unsigned char *) s1; unsigned char *str2 = (unsigned char *) s2; int res; while (!(res = rfc_toupper(*str1) - rfc_toupper(*str2))) { if (*str1 == '\0') return 0; str1++; str2++; } return res; } int _rfc_ncasecmp(const char *str1, const char *str2, int n) { unsigned char *s1 = (unsigned char *) str1; unsigned char *s2 = (unsigned char *) str2; int res; while (!(res = rfc_toupper(*s1) - rfc_toupper(*s2))) { s1++; s2++; n--; if (!n || (*s1 == '\0' && *s2 == '\0')) return 0; } return res; } unsigned char rfc_tolowertab[] = { 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; unsigned char rfc_touppertab[] = { 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', 0x5f, '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; int _rfc_tolower(int c) { return rfc_tolowertab[(unsigned char) (c)]; } int _rfc_toupper(int c) { return rfc_touppertab[(unsigned char) (c)]; } eggheads-eggdrop-9c00fe1/src/stat.h000066400000000000000000000024761524730042700172470ustar00rootroot00000000000000/* * stat.h * file attributes */ /* * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_STAT_H #define _EGG_STAT_H #ifndef S_ISDIR # ifndef S_IFMT # define S_IFMT 0170000 /* Bitmask for the file type bitfields */ # endif # ifndef S_IFDIR # define S_IFDIR 0040000 /* Directory */ # endif # define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR)) #endif #ifndef S_IFREG # define S_IFREG 0100000 /* Regular file */ #endif #ifndef S_IFLNK # define S_IFLNK 0120000 /* Symbolic link */ #endif #endif /* _EGG_STAT_H */ eggheads-eggdrop-9c00fe1/src/tandem.h000066400000000000000000000066451524730042700175460ustar00rootroot00000000000000/* * tandem.h */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_TANDEM_H #define _EGG_TANDEM_H /* Keep track of tandem-bots in the chain */ typedef struct tand_t_struct { char bot[HANDLEN + 1]; struct tand_t_struct *via; struct tand_t_struct *uplink; struct tand_t_struct *next; int ver; char share; int ssl; } tand_t; /* Keep track of party-line members */ typedef struct { char nick[HANDLEN + 1]; char bot[HANDLEN + 1]; int sock; int chan; char *from; char flag; char status; time_t timer; /* Track idle time */ char *away; } party_t; /* Status: */ #define PLSTAT_AWAY 0x001 #define IS_PARTY 0x002 /* Minimum version that uses tokens & base64 ints * for channel msg's */ #define NEAT_BOTNET 1029900 #define GLOBAL_CHANS 100000 #ifndef MAKING_MODS void send_tand_but(int, char *, int); void botnet_send_chan(int, char *, char *, int, char *); void botnet_send_chat(int, char *, char *); void botnet_send_act(int, char *, char *, int, char *); void botnet_send_ping(int); void botnet_send_pong(int); void botnet_send_priv (int idx, char *from, char *to, char *tobot, const char *format, ...) ATTRIBUTE_FORMAT(printf,5,6); void botnet_send_who(int, char *, char *, int); void botnet_send_infoq(int, char *); void botnet_send_unlinked(int, char *, char *); void botnet_send_traced(int, char *, char *); void botnet_send_trace(int, char *, char *, char *); void botnet_send_unlink(int, char *, char *, char *, char *); void botnet_send_link(int, char *, char *, char *); void botnet_send_update(int, tand_t *); void botnet_send_nlinked(int, char *, char *, char, int); void botnet_send_reject(int, char *, char *, char *, char *, char *); void botnet_send_zapf(int, char *, char *, char *); void botnet_send_zapf_broad(int, char *, char *, char *); void botnet_send_motd(int, char *, char *); void botnet_send_filereq(int, char *, char *, char *); void botnet_send_filereject(int, char *, char *, char *); void botnet_send_filesend(int, char *, char *, char *); void botnet_send_away(int, char *, int, char *, int); void botnet_send_idle(int, char *, int, int, char *); void botnet_send_join_idx(int, int); void botnet_send_join_party(int, int, int, int); void botnet_send_part_idx(int, char *); void botnet_send_part_party(int, int, char *, int); void botnet_send_bye(void); void botnet_send_nkch_part(int, int, char *); void botnet_send_nkch(int, char *); int bots_in_subtree(tand_t *); int users_in_subtree(tand_t *); #endif /* MAKING_MODS */ #define b_status(a) (dcc[a].status) #define b_version(a) (dcc[a].u.bot->version) #define b_linker(a) (dcc[a].u.bot->linker) #define b_numver(a) (dcc[a].u.bot->numver) #endif /* _EGG_TANDEM_H */ eggheads-eggdrop-9c00fe1/src/tcl.c000066400000000000000000001175661524730042700170600ustar00rootroot00000000000000/* * tcl.c -- handles: * the code for every command eggdrop adds to Tcl * Tcl initialization * getting and setting Tcl/eggdrop variables */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include /* getenv() */ #include /* setlocale() */ #include "main.h" /* Used for read/write to internal strings */ typedef struct { char *str; /* Pointer to actual string in eggdrop */ int max; /* max length (negative: read-only var * when protect is on) (0: read-only ALWAYS) */ int flags; /* 1 = directory */ } strinfo; typedef struct { int *var; int ro; } intinfo; extern time_t now, online_since; extern char origbotname[], botuser[], motdfile[], admin[], userfile[], firewall[], helpdir[], notify_new[], vhost[], moddir[], owner[], network[], botnetnick[], bannerfile[], egg_version[], nat_ip[], configfile[], logfile_suffix[], log_ts[], textdir[], pid_file[], listen_ip[], stealth_prompt[], language[]; extern int flood_telnet_thr, flood_telnet_time, shtime, share_greet, require_p, keep_all_logs, allow_new_telnets, stealth_telnets, use_telnet_banner, default_flags, conmask, switch_logfiles_at, connect_timeout, firewallport, notify_users_at, flood_thr, tands, ignore_time, reserved_port_min, reserved_port_max, max_logs, max_logsize, dcc_total, raw_log, identtimeout, dcc_sanitycheck, dupwait_timeout, egg_numver, share_unlinks, protect_telnet, resolve_timeout, default_uflags, userfile_perm, cidr_support, remove_pass, show_uname; #ifdef IPV6 extern char vhost6[]; extern int pref_af; #endif #ifdef TLS extern int tls_maxdepth, tls_vfybots, tls_vfyclients, tls_vfydcc, tls_auth; extern char tls_capath[], tls_cafile[], tls_certfile[], tls_keyfile[], tls_protocols[], tls_dhparam[], tls_ciphers[]; #endif extern struct dcc_t *dcc; extern tcl_timer_t *timer, *utimer; Tcl_Interp *interp; int protect_readonly = 0; /* Enable read-only protection? */ char whois_fields[1025] = ""; int dcc_flood_thr = 3; int use_invites = 0; int use_exempts = 0; int force_expire = 0; int remote_boots = 2; int allow_dk_cmds = 1; int must_be_owner = 1; int quiet_reject = 1; int max_socks = 100; int par_telnet_flood = 1; int quiet_save = 0; int strtot = 0; int handlen = HANDLEN; extern Tcl_VarTraceProc traced_myiphostname, traced_natip, traced_remove_pass; /* Unicode workaround for Tcl versions (8.5/8.6) that only support BMP characters (3 byte utf-8) */ #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 6 && TCL_UTF_MAX < 4 # define TCL_WORKAROUND_UNICODESUP 1 struct tcl_unicodesup_info { const char *subcmd; Tcl_Obj *cmd; }; #endif int expmem_tcl() { return strtot; } static void botnet_change(char *new) { if (strcasecmp(botnetnick, new)) { /* Trying to change bot's nickname */ if (tands > 0) { putlog(LOG_MISC, "*", "* Tried to change my botnet nick, but I'm still " "linked to a botnet."); putlog(LOG_MISC, "*", "* (Unlink and try again.)"); return; } else { if (botnetnick[0]) putlog(LOG_MISC, "*", "* IDENTITY CHANGE: %s -> %s", botnetnick, new); set_botnetnick(new); } } } /* * Vars, traces, misc */ int init_misc(); /* Used for read/write to integer couplets */ typedef struct { int *left; /* left side of couplet */ int *right; /* right side */ } coupletinfo; /* FIXME: tcl_eggcouplet() should be redesigned so we can use * TCL_TRACE_WRITES | TCL_TRACE_READS as the bit mask instead * of 2 calls as is done in add_tcl_coups(). */ /* Read/write integer couplets (int1:int2) */ static char *tcl_eggcouplet(ClientData cdata, Tcl_Interp *irp, EGG_CONST char *name1, EGG_CONST char *name2, int flags) { char *s, s1[41]; coupletinfo *cp = (coupletinfo *) cdata; if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) { egg_snprintf(s1, sizeof s1, "%d:%d", *(cp->left), *(cp->right)); Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY); if (flags & TCL_TRACE_UNSETS) Tcl_TraceVar(interp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, tcl_eggcouplet, cdata); } else { /* writes */ s = (char *) Tcl_GetVar2(interp, name1, name2, 0); if (s != NULL) { int nr1, nr2; nr1 = nr2 = 0; if (strlen(s) > 40) s[40] = 0; sscanf(s, "%d%*c%d", &nr1, &nr2); *(cp->left) = nr1; *(cp->right) = nr2; } } return NULL; } /* Read or write normal integer. */ static char *tcl_eggint(ClientData cdata, Tcl_Interp *irp, EGG_CONST char *name1, EGG_CONST char *name2, int flags) { char *s, s1[40], *endptr; long l; intinfo *ii = (intinfo *) cdata; int p; if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) { /* Special cases */ if ((int *) ii->var == &conmask) strlcpy(s1, masktype(conmask), sizeof s1); else if ((int *) ii->var == &default_flags) { struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; fr.global = default_flags; fr.udef_global = default_uflags; build_flags(s1, &fr, 0); } else if ((int *) ii->var == &userfile_perm) { egg_snprintf(s1, sizeof s1, "0%o", userfile_perm); } else egg_snprintf(s1, sizeof s1, "%d", *(int *) ii->var); Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY); if (flags & TCL_TRACE_UNSETS) Tcl_TraceVar(interp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, tcl_eggint, cdata); return NULL; } else { /* Writes */ s = (char *) Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY); if (s != NULL) { if ((int *) ii->var == &conmask) { if (s[0]) conmask = logmodes(s); else conmask = LOG_MODES | LOG_MISC | LOG_CMDS; } else if ((int *) ii->var == &default_flags) { struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; break_down_flags(s, &fr, 0); default_flags = sanity_check(fr.global); /* drummer */ default_uflags = fr.udef_global; } else if ((int *) ii->var == &userfile_perm) { p = strtol(s, &endptr, 8); if ((p < 01) || (p > 0777) || (*endptr)) return "Invalid userfile permissions, must be octal between 01 and 0777"; userfile_perm = p; } else if ((ii->ro == 2) || ((ii->ro == 1) && protect_readonly)) return "Read-only variable"; else { if (Tcl_ExprLong(interp, s, &l) == TCL_ERROR) return "Variable must have integer value"; if ((int *) ii->var == &max_socks) { if (l < threaddata()->MAXSOCKS) return "Decreasing max-socks requires a restart"; max_socks = l; } else if ((int *) ii->var == &max_logs) { if (l < 5) return "ERROR: max-logs cannot be less than 5"; if (l < max_logs) return "ERROR: Decreasing max-logs requires a restart"; max_logs = l; init_misc(); } else *(ii->var) = (int) l; } } return NULL; } } /* Read/write normal string variable */ static char *tcl_eggstr(ClientData cdata, Tcl_Interp *irp, EGG_CONST char *name1, EGG_CONST char *name2, int flags) { char *s; strinfo *st = (strinfo *) cdata; if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) { if ((st->str == firewall) && (firewall[0])) { char s1[127]; egg_snprintf(s1, sizeof s1, "%s:%d", firewall, firewallport); Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY); } else Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY); if (flags & TCL_TRACE_UNSETS) { Tcl_TraceVar(interp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, tcl_eggstr, cdata); if ((st->max <= 0) && (protect_readonly || (st->max == 0))) return "read-only variable"; /* it won't return the error... */ } return NULL; } else { /* writes */ if ((st->max <= 0) && (protect_readonly || (st->max == 0))) { Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY); return "read-only variable"; } s = (char *) Tcl_GetVar2(interp, name1, name2, 0); if (s != NULL) { if (strlen(s) > abs(st->max)) { putlog(LOG_MISC, "*", "WARNING: Value for %s truncated to %i chars", name1, abs(st->max)); s[abs(st->max)] = 0; } if (st->str == botnetnick) botnet_change(s); else if (st->str == logfile_suffix) logsuffix_change(s); else if (st->str == firewall) { splitc(firewall, s, ':'); if (!firewall[0]) strcpy(firewall, s); else firewallport = atoi(s); } else strcpy(st->str, s); if ((st->flags) && (s[0])) { if (st->str[strlen(st->str) - 1] != '/') strcat(st->str, "/"); } } return NULL; } } struct tcl_call_stringinfo { Tcl_CmdProc *proc; ClientData cd; }; static void tcl_cleanup_stringinfo(ClientData cd) { nfree(cd); } /* Compatibility wrapper that calls Tcl functions with String API * * Is reentrant, can call itself recursively, so argv is dynamically allocated * Tcl_IncrRefCount() is needed to preserve the strings we get Tcl_GetString() * from being cleaned up if Tcl is invoked from this */ static int tcl_call_stringproc_cd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { const char **argv; int i, ret; struct tcl_call_stringinfo *info = cd; /* The string API guarantees argv[argc] == NULL, unlike the obj API */ argv = nmalloc((objc + 1) * sizeof *argv); for (i = 0; i < objc; i++) { Tcl_IncrRefCount(objv[i]); argv[i] = Tcl_GetString(objv[i]); } argv[objc] = NULL; ret = (info->proc)(info->cd, interp, objc, argv); for (i = 0; i < objc; i++) { Tcl_DecrRefCount(objv[i]); } nfree(argv); return ret; } /* The standard case of no actual cd */ static int tcl_call_stringproc(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { struct tcl_call_stringinfo info; info.proc = cd; info.cd = NULL; return tcl_call_stringproc_cd(&info, interp, objc, objv); } /* Add/remove tcl commands */ void add_tcl_commands(tcl_cmds *table) { int i; for (i = 0; table[i].name; i++) Tcl_CreateObjCommand(interp, table[i].name, tcl_call_stringproc, table[i].func, NULL); } void add_cd_tcl_cmds(cd_tcl_cmd *table) { struct tcl_call_stringinfo *info; while (table->name) { if (table->cdata) { info = nmalloc(sizeof *info); strtot += sizeof(struct tcl_call_stringinfo); info->proc = table->callback; info->cd = table->cdata; Tcl_CreateObjCommand(interp, table->name, tcl_call_stringproc_cd, info, tcl_cleanup_stringinfo); } else { Tcl_CreateObjCommand(interp, table->name, tcl_call_stringproc, table->callback, NULL); } table++; } } void rem_tcl_commands(tcl_cmds *table) { int i; for (i = 0; table[i].name; i++) Tcl_DeleteCommand(interp, table[i].name); } void add_tcl_objcommands(tcl_cmds *table) { int i; for (i = 0; table[i].name; i++) Tcl_CreateObjCommand(interp, table[i].name, table[i].func, (ClientData) 0, NULL); } /* Get the current tcl result string. */ const char *tcl_resultstring() { const char *result; result = Tcl_GetStringResult(interp); return result; } int tcl_resultempty() { const char *result; result = tcl_resultstring(); return (result && result[0]) ? 0 : 1; } /* Get the current tcl result as int. replaces atoi(interp->result) */ int tcl_resultint() { int result; if (Tcl_GetIntFromObj(NULL, Tcl_GetObjResult(interp), &result) != TCL_OK) result = 0; return result; } static tcl_strings def_tcl_strings[] = { {"botnet-nick", botnetnick, HANDLEN, 0}, {"userfile", userfile, 120, STR_PROTECT}, {"motd", motdfile, 120, STR_PROTECT}, {"admin", admin, 120, 0}, {"help-path", helpdir, 120, STR_DIR | STR_PROTECT}, {"text-path", textdir, 120, STR_DIR | STR_PROTECT}, #ifdef TLS {"ssl-capath", tls_capath, 120, STR_DIR | STR_PROTECT}, {"ssl-cafile", tls_cafile, 120, STR_PROTECT}, {"ssl-protocols", tls_protocols, 60, STR_PROTECT}, {"ssl-dhparam", tls_dhparam, 120, STR_PROTECT}, {"ssl-ciphers", tls_ciphers, 2048, STR_PROTECT}, {"ssl-privatekey", tls_keyfile, 120, STR_PROTECT}, {"ssl-certificate", tls_certfile, 120, STR_PROTECT}, #endif #ifndef STATIC {"mod-path", moddir, 120, STR_DIR | STR_PROTECT}, #endif {"notify-newusers", notify_new, 120, 0}, {"owner", owner, 120, STR_PROTECT}, {"vhost4", vhost, 120, 0}, #ifdef IPV6 {"vhost6", vhost6, 120, 0}, #endif {"listen-addr", listen_ip, 120, 0}, {"network", network, 40, 0}, {"whois-fields", whois_fields, 1024, 0}, {"nat-ip", nat_ip, INET_ADDRSTRLEN - 1, 0}, {"username", botuser, USERLEN, 0}, {"version", egg_version, 0, 0}, {"firewall", firewall, 120, 0}, {"config", configfile, 0, 0}, {"telnet-banner", bannerfile, 120, STR_PROTECT}, {"logfile-suffix", logfile_suffix, 20, 0}, {"timestamp-format",log_ts, 32, 0}, {"pidfile", pid_file, 120, STR_PROTECT}, {"configureargs", EGG_AC_ARGS, 0, STR_PROTECT}, {"stealth-prompt", stealth_prompt, 80, 0}, {"language", language, 64, STR_PROTECT}, {NULL, NULL, 0, 0} }; static tcl_ints def_tcl_ints[] = { {"ignore-time", &ignore_time, 0}, {"handlen", &handlen, 2}, #ifdef TLS {"ssl-chain-depth", &tls_maxdepth, 0}, {"ssl-verify-dcc", &tls_vfydcc, 0}, {"ssl-verify-clients", &tls_vfyclients, 0}, {"ssl-verify-bots", &tls_vfybots, 0}, {"ssl-cert-auth", &tls_auth, 0}, #endif {"dcc-flood-thr", &dcc_flood_thr, 0}, {"hourly-updates", ¬ify_users_at, 0}, {"switch-logfiles-at", &switch_logfiles_at, 0}, {"connect-timeout", &connect_timeout, 0}, {"reserved-port", &reserved_port_min, 0}, {"require-p", &require_p, 0}, {"keep-all-logs", &keep_all_logs, 0}, {"open-telnets", &allow_new_telnets, 0}, {"stealth-telnets", &stealth_telnets, 0}, {"use-telnet-banner", &use_telnet_banner, 0}, {"uptime", (int *) &online_since, 2}, {"console", &conmask, 0}, {"default-flags", &default_flags, 0}, {"numversion", &egg_numver, 2}, {"remote-boots", &remote_boots, 1}, {"max-socks", &max_socks, 0}, {"max-logs", &max_logs, 0}, {"max-logsize", &max_logsize, 0}, {"raw-log", &raw_log, 1}, {"protect-telnet", &protect_telnet, 0}, {"dcc-sanitycheck", &dcc_sanitycheck, 0}, {"ident-timeout", &identtimeout, 0}, {"share-unlinks", &share_unlinks, 0}, {"log-time", &shtime, 0}, {"allow-dk-cmds", &allow_dk_cmds, 0}, {"resolve-timeout", &resolve_timeout, 0}, {"must-be-owner", &must_be_owner, 1}, {"paranoid-telnet-flood", &par_telnet_flood, 0}, {"use-exempts", &use_exempts, 0}, {"use-invites", &use_invites, 0}, {"quiet-save", &quiet_save, 0}, {"force-expire", &force_expire, 0}, {"dupwait-timeout", &dupwait_timeout, 0}, {"userfile-perm", &userfile_perm, 0}, {"quiet-reject", &quiet_reject, 0}, {"cidr-support", &cidr_support, 0}, {"remove-pass", &remove_pass, 0}, #ifdef IPV6 {"prefer-ipv6", &pref_af, 0}, #endif {"show-uname", &show_uname, 0}, {"share-greet", &share_greet, 0}, {NULL, NULL, 0} }; static tcl_coups def_tcl_coups[] = { {"telnet-flood", &flood_telnet_thr, &flood_telnet_time}, {"reserved-portrange", &reserved_port_min, &reserved_port_max}, {NULL, NULL, NULL} }; /* Set up Tcl variables that will hook into eggdrop internal vars via * trace callbacks. */ static void init_traces() { add_tcl_coups(def_tcl_coups); add_tcl_strings(def_tcl_strings); add_tcl_ints(def_tcl_ints); Tcl_TraceVar(interp, "my-hostname", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_myiphostname, NULL); Tcl_TraceVar(interp, "my-ip", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_myiphostname, NULL); Tcl_TraceVar(interp, "nat-ip", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_natip, NULL); Tcl_TraceVar(interp, "remove-pass", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES, traced_remove_pass, NULL); } void kill_tcl() { rem_tcl_coups(def_tcl_coups); rem_tcl_strings(def_tcl_strings); rem_tcl_ints(def_tcl_ints); Tcl_UntraceVar(interp, "my-hostname", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_myiphostname, NULL); Tcl_UntraceVar(interp, "my-ip", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_myiphostname, NULL); Tcl_UntraceVar(interp, "nat-ip", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, traced_natip, NULL); Tcl_UntraceVar(interp, "remove-pass", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES, traced_remove_pass, NULL); kill_bind(); Tcl_DeleteInterp(interp); } extern tcl_cmds tcluser_cmds[], tcldcc_cmds[], tclmisc_cmds[], tclmisc_objcmds[], tcldns_cmds[]; #ifdef TLS extern tcl_cmds tcltls_cmds[]; #endif /* The tickle_*() functions replace the Tcl Notifier * The tickle_*() functions can be called by Tcl threads */ void tickle_SetTimer (TCL_CONST86 Tcl_Time *timePtr) { struct threaddata *td = threaddata(); /* we can block 1 second maximum, because we have SECONDLY events */ if (!timePtr || timePtr->sec > 1 || (timePtr->sec == 1 && timePtr->usec > 0)) { td->blocktime.tv_sec = 1; td->blocktime.tv_usec = 0; } else { td->blocktime.tv_sec = timePtr->sec; td->blocktime.tv_usec = timePtr->usec; } } int tickle_WaitForEvent (TCL_CONST86 Tcl_Time *timePtr) { struct threaddata *td = threaddata(); tickle_SetTimer(timePtr); return (*td->mainloopfunc)(0); } void tickle_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, ClientData cd) { alloctclsock(fd, mask, proc, cd); } void tickle_DeleteFileHandler(int fd) { killtclsock(fd); } void tickle_FinalizeNotifier(ClientData cd) { struct threaddata *td = threaddata(); if (td->socklist) nfree(td->socklist); } ClientData tickle_InitNotifier() { static int ismainthread = 1; init_threaddata(ismainthread); if (ismainthread) ismainthread = 0; return NULL; } void tickle_AlertNotifier(ClientData cd) { if (cd) { fatal("Error calling Tcl_AlertNotifier", 0); } } void tickle_ServiceModeHook(int mode) { if (mode != TCL_SERVICE_ALL) { fatal("Tcl_ServiceModeHook called with unsupported mode", 0); } } int tclthreadmainloop(int zero) { int i; i = sockread(NULL, NULL, threaddata()->socklist, threaddata()->MAXSOCKS, 1); return (i == -5); } struct threaddata *td_main = 0; struct threaddata *threaddata() { static Tcl_ThreadDataKey tdkey; struct threaddata *td = Tcl_GetThreadData(&tdkey, sizeof(struct threaddata)); if (!(td->mainloopfunc) && td_main) /* python thread */ return td_main; return td; } void init_threaddata(int mainthread) { struct threaddata *td = threaddata(); /* Nested evaluation (vwait/update) of the event loop only * processes Tcl events (after/fileevent) for now. Using * eggdrops mainloop() requires caution regarding reentrance. * (check_tcl_* -> Tcl_Eval() -> mainloop() -> check_tcl_* etc.) */ /* td->mainloopfunc = mainthread ? mainloop : tclthreadmainloop; */ td->mainloopfunc = tclthreadmainloop; td->socklist = NULL; td->mainthread = mainthread; td->blocktime.tv_sec = 1; td->blocktime.tv_usec = 0; td->MAXSOCKS = 0; increase_socks_max(); if (mainthread) td_main = td; } /* workaround for Tcl that does not support unicode outside BMP (3 byte utf-8 characters) */ #ifdef TCL_WORKAROUND_UNICODESUP /* Based on https://github.com/skeeto/branchless-utf8 which is released into the public domain */ /* 0 means not utf-8, so the length is still 1 but we can distinguish that case, * that's why len = len + !len is used, to convert 0 to 1 and leave the rest as-is */ static const char utf8lengths[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; /* quick check if tricks are needed, only if 4-byte utf-8 characters are used */ int needs_unicodesup(const char *str) { while (*str) { int len = utf8lengths[((unsigned char)str[0]) >> 3] + !utf8lengths[((unsigned char)str[0]) >> 3]; if (len == 4) { return 1; } while (len--) { if (*str++ == '\0') { break; } } } return 0; } /* return a new Tcl_StringObj with 4-byte utf-8 characters replaced by surrogate pairs * - decode 4-byte utf-8 into unicode codepoint c * - calculate high and low surrogate unicode codepoints high/low * - encode high/low as 3-byte utf-8 strings * the length of the result could be len/4*6 bytes long, so * to avoid frequent reallocation/string appending, a temporary buffer is used * for long strings (>512 characters, which does not apply to IRC lines) chunks are assembled to a Tcl_DString * for short strings the 512/4*6 character buffer is enough */ Tcl_Obj *egg_string_unicodesup_surrogate(const char *oldstr, int len) { int stridx = 0, bufidx = 0, use_dstring = 0; char buf[768]; Tcl_DString ds; Tcl_Obj *result; /* chunked */ if (len > 512) { use_dstring = 1; Tcl_DStringInit(&ds); } while (stridx < len) { int charlen = utf8lengths[((unsigned char)oldstr[stridx]) >> 3] + !utf8lengths[((unsigned char)oldstr[stridx])>> 3]; if (charlen == 4 && stridx + 4 <= len) { uint32_t c; uint16_t high, low; /* decode 4-byte utf-8 into unicode codepoint */ c = (uint32_t)(oldstr[stridx++] & 0x07) << 18; c |= (uint32_t)(oldstr[stridx++] & 0x3f) << 12; c |= (uint32_t)(oldstr[stridx++] & 0x3f) << 6; c |= (uint32_t)(oldstr[stridx++] & 0x3f) << 0; /* calculate high and low surrogate unicode codepoints */ c -= 0x10000; high = 0xD800 + ((c & 0xffc00) >> 10); low = 0xDC00 + (c & 0x3ff); /* encode high surrogate as utf-8 */ buf[bufidx++] = 0xe0 | ((high >> 12) & 0xf); buf[bufidx++] = 0x80 | ((high >> 6) & 0x3f); buf[bufidx++] = 0x80 | ((high >> 0) & 0x3f); /* encode low surrogate as utf-8 */ buf[bufidx++] = 0xe0 | ((low >> 12) & 0xf); buf[bufidx++] = 0x80 | ((low >> 6) & 0x3f); buf[bufidx++] = 0x80 | ((low >> 0) & 0x3f); } else { /* copy everything else verbatim */ while (charlen-- && stridx < len) { buf[bufidx++] = oldstr[stridx++]; } } if (use_dstring && bufidx > sizeof buf - 6) { Tcl_DStringAppend(&ds, buf, bufidx); bufidx = 0; } } if (use_dstring && bufidx) { Tcl_DStringAppend(&ds, buf, bufidx); result = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); Tcl_DStringFree(&ds); } else { result = Tcl_NewStringObj(buf, bufidx); } return result; } /* decode 2 utf-8 sequences that are 3 bytes long and check if they are surrogate pairs */ int decode_surrogates(const char *str, uint32_t *high, uint32_t *low) { *high = (*str++ & 0xf) << 12; *high |= (*str++ & 0x3f) << 6; *high |= (*str++ & 0x3f) << 0; if (*high < 0xD800 || *high > 0xDBFF) { return 0; } *low = (*str++ & 0xf) << 12; *low |= (*str++ & 0x3f) << 6; *low |= (*str++ & 0x3f) << 0; if (*low < 0xDC00 || *low > 0xDFFF) { return 0; } return 1; } /* returns a new Tcl_StringObj by replacing surrogate pairs back into 4-byte utf-8 sequences * - for every 3-byte utf-8 sequence, check if it's a surrogate pair * - decode into high/low codepoints * - calculate original codepoint and write back a 4-byte utf-8 sequence instead of 3-byte surrogate pairs * the length of the result is guaranteed to be equal or shorter than the original, so malloc(len) is sufficient space */ Tcl_Obj *egg_string_unicodesup_desurrogate(const char *oldstr, int len) { int stridx = 0, bufidx = 0; char *buf = nmalloc(len); Tcl_Obj *o; while (stridx < len) { uint32_t low, high; int charlen = utf8lengths[((unsigned char)oldstr[stridx]) >> 3] + !utf8lengths[((unsigned char)oldstr[stridx]) >> 3]; if (charlen == 3 && stridx + 6 <= len && utf8lengths[((unsigned char)oldstr[stridx + 3]) >> 3] == 3 && decode_surrogates(oldstr + stridx, &high, &low)) { uint32_t c = 0x10000 + (high - 0xD800) * 0x400 + (low - 0xDC00); buf[bufidx++] = 0xF0 | ((c >> 18) & 0x07); buf[bufidx++] = 0x80 | ((c >> 12) & 0x3f); buf[bufidx++] = 0x80 | ((c >> 6) & 0x3f); buf[bufidx++] = 0x80 | ((c >> 0) & 0x3f); stridx += 6; } else { while (charlen-- && stridx < len) { buf[bufidx++] = oldstr[stridx++]; } } } o = Tcl_NewStringObj(buf, bufidx); nfree(buf); return o; } /* C function called for ::egg_tcl_tolower/toupper/totitle * context (original Tcl function and which conversion to do) is in cd */ int egg_string_unicodesup(void *cd, Tcl_Interp *interp, int objc, Tcl_Obj *const orig_objv[]) { struct tcl_unicodesup_info *info = cd; Tcl_Obj **new_objv; int i; int ret; /* impossible? */ if (objc == 0) { return Tcl_EvalObjv(interp, objc, orig_objv, 0); } /* new arguments to original Tcl string tolower/toupper/totitle */ new_objv = nmalloc(objc * sizeof *new_objv); for (i = 0; i < objc; i++) { if (i == 0) { /* overwrite command objv[0] with original Tcl command instead of this function */ new_objv[i] = info->cmd; } else if (i == 1 && needs_unicodesup(Tcl_GetString(orig_objv[1]))) { int len; const char *oldstr = Tcl_GetStringFromObj(orig_objv[1], &len); /* overwrite string argument by replacing 4-byet utf-8 sequences with surrogate pairs */ new_objv[1] = egg_string_unicodesup_surrogate(oldstr, len); } else { /* copy other arguments, e.g. string tolower test 1 2 */ new_objv[i] = orig_objv[i]; } /* ref count of new objects must be increased before eval and decreased after, orig_objv is read-only */ Tcl_IncrRefCount(new_objv[i]); } /* call original Tcl function */ ret = Tcl_EvalObjv(interp, objc, new_objv, 0); /* decrease ref count of new arguments */ for (i = 0; i < objc; i++) { Tcl_DecrRefCount(new_objv[i]); } nfree(new_objv); /* overwrite Tcl's result by replacing surrogates back to 4-byte utf-8 sequences*/ if (ret == TCL_OK) { int len; Tcl_Obj *resultobj = Tcl_GetObjResult(interp); const char *str = Tcl_GetStringFromObj(resultobj, &len); Tcl_SetObjResult(interp, egg_string_unicodesup_desurrogate(str, len)); } return ret; } /* register a single workaround command, making the namespace ensemble string call ::egg_string_ instead * original command names are ::tcl::string:: */ void init_unicodesup_cmd(Tcl_Obj *ensdict, const char *subcmd, int index) { Tcl_Obj *orig_cmd; static struct tcl_unicodesup_info info[3]; char buf[64]; if (Tcl_DictObjGet(interp, ensdict, Tcl_NewStringObj(subcmd, -1), &orig_cmd) != TCL_OK || !orig_cmd) { putlog(LOG_MISC, "*", "ERROR: Tcl non-BMP unicodesup could not find string %s subcommand", subcmd); return; } info[index].subcmd = subcmd; info[index].cmd = orig_cmd; Tcl_IncrRefCount(orig_cmd); snprintf(buf, sizeof buf, "::egg_string_%s", subcmd); Tcl_CreateObjCommand(interp, buf, egg_string_unicodesup, &info[index], NULL); if (Tcl_DictObjPut(interp, ensdict, Tcl_NewStringObj(subcmd, -1), Tcl_NewStringObj(buf, -1)) != TCL_OK) { putlog(LOG_MISC, "*", "ERROR: Tcl non-BMP unicodesup could not set dictionary redirect"); return; } } /* register all workaround functions */ void init_unicodesup(void) { Tcl_Obj *ensdict; Tcl_Command enscmd = Tcl_FindEnsemble(interp, Tcl_NewStringObj("string", -1), 0); if (!enscmd) { putlog(LOG_MISC, "*", "ERROR: Tcl non-BMP unicodesup could not find string command"); return; } if (!Tcl_IsEnsemble(enscmd)) { putlog(LOG_MISC, "*", "ERROR: Tcl non-BMP unicodesup is not a namespace ensemble"); return; } if (Tcl_GetEnsembleMappingDict(interp, enscmd, &ensdict) != TCL_OK || !ensdict) { putlog(LOG_MISC, "*", "ERROR: Tcl non-BMP unicodesup could not get namespace ensemble dictionary"); return; } init_unicodesup_cmd(ensdict, "tolower", 0); init_unicodesup_cmd(ensdict, "toupper", 1); init_unicodesup_cmd(ensdict, "totitle", 2); if (Tcl_SetEnsembleMappingDict(interp, enscmd, ensdict) != TCL_OK) { putlog(LOG_MISC, "*", "ERROR: Tcl non-BMP unicodesup could not set namespace ensemble dictionary"); return; } } #endif /* TCL_WORKAROUND_UNICODESUP */ void init_tcl0(int argc, char **argv) { Tcl_NotifierProcs notifierprocs; egg_bzero(¬ifierprocs, sizeof(notifierprocs)); notifierprocs.initNotifierProc = tickle_InitNotifier; notifierprocs.createFileHandlerProc = tickle_CreateFileHandler; notifierprocs.deleteFileHandlerProc = tickle_DeleteFileHandler; notifierprocs.setTimerProc = tickle_SetTimer; notifierprocs.waitForEventProc = tickle_WaitForEvent; notifierprocs.finalizeNotifierProc = tickle_FinalizeNotifier; notifierprocs.alertNotifierProc = tickle_AlertNotifier; notifierprocs.serviceModeHookProc = tickle_ServiceModeHook; Tcl_SetNotifier(¬ifierprocs); /* This must be done *BEFORE* Tcl_SetSystemEncoding(), * or Tcl_SetSystemEncoding() will cause a segfault. */ /* This is used for 'info nameofexecutable'. * The filename in argv[0] must exist in a directory listed in * the environment variable PATH for it to register anything. */ Tcl_FindExecutable(argv[0]); #if TCL_MAJOR_VERSION >= 9 Tcl_InitSubsystems(); #endif } /* Not going through Tcl's crazy main() system (what on earth was he * smoking?!) so we gotta initialize the Tcl interpreter */ void init_tcl1(int argc, char **argv) { const char *encoding; int i, j; char *langEnv, pver[1024] = ""; /* Initialize the interpreter */ interp = Tcl_CreateInterp(); #ifdef DEBUG_MEM /* Initialize Tcl's memory debugging if we want it */ Tcl_InitMemory(interp); #endif /* Set Tcl variable tcl_interactive to 0 */ Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); /* Setup script library facility */ Tcl_Init(interp); Tcl_SetServiceMode(TCL_SERVICE_ALL); /* Code based on Tcl's TclpSetInitialEncodings() */ /* Determine the current encoding from the LC_* or LANG environment * variables. */ langEnv = getenv("LC_ALL"); if (langEnv == NULL || langEnv[0] == '\0') { langEnv = getenv("LC_CTYPE"); } if (langEnv == NULL || langEnv[0] == '\0') { langEnv = getenv("LANG"); } if (langEnv == NULL || langEnv[0] == '\0') { langEnv = NULL; } encoding = NULL; if (langEnv != NULL) { for (i = 0; localeTable[i].lang != NULL; i++) if (strcmp(localeTable[i].lang, langEnv) == 0) { encoding = localeTable[i].encoding; break; } /* There was no mapping in the locale table. If there is an * encoding subfield, we can try to guess from that. */ if (encoding == NULL) { char *p; for (p = langEnv; *p != '\0'; p++) { if (*p == '.') { p++; break; } } if (*p != '\0') { Tcl_DString ds; Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, p, -1); encoding = Tcl_DStringValue(&ds); Tcl_UtfToLower(Tcl_DStringValue(&ds)); if (Tcl_SetSystemEncoding(NULL, encoding) == TCL_OK) { Tcl_DStringFree(&ds); goto resetPath; } Tcl_DStringFree(&ds); encoding = NULL; } } } if (encoding == NULL) { encoding = "iso8859-1"; } Tcl_SetSystemEncoding(NULL, encoding); resetPath: /* Initialize the C library's locale subsystem. */ setlocale(LC_CTYPE, ""); /* In case the initial locale is not "C", ensure that the numeric * processing is done in "C" locale regardless. */ setlocale(LC_NUMERIC, "C"); /* Keep the iso8859-1 encoding preloaded. The IO package uses it for * gets on a binary channel. */ Tcl_GetEncoding(NULL, "iso8859-1"); /* Add eggdrop to Tcl's package list */ for (j = 0; j <= strlen(egg_version); j++) { if ((egg_version[j] == ' ') || (egg_version[j] == '+')) break; pver[strlen(pver)] = egg_version[j]; } Tcl_PkgProvide(interp, "eggdrop", pver); #ifdef TCL_WORKAROUND_UNICODESUP init_unicodesup(); #endif /* Initialize binds and traces */ init_bind(); init_traces(); /* Add new commands */ add_tcl_commands(tcluser_cmds); add_tcl_commands(tcldcc_cmds); add_tcl_commands(tclmisc_cmds); add_tcl_commands(tcldns_cmds); #ifdef TLS add_tcl_commands(tcltls_cmds); #endif } void do_tcl(char *whatzit, char *script) { int code; char *result; Tcl_DString dstr; code = Tcl_Eval(interp, script); /* properly convert string to system encoding. */ Tcl_DStringInit(&dstr); Tcl_UtfToExternalDString(NULL, tcl_resultstring(), -1, &dstr); result = Tcl_DStringValue(&dstr); if (code != TCL_OK) { putlog(LOG_MISC, "*", "Tcl error in script for '%s':", whatzit); putlog(LOG_MISC, "*", "%s", result); Tcl_BackgroundError(interp); } Tcl_DStringFree(&dstr); } /* Interpret tcl file fname. * * returns: 1 - if everything was okay */ int readtclprog(char *fname) { int code; EGG_CONST char *result; Tcl_DString dstr; if (!file_readable(fname)) return 0; code = Tcl_EvalFile(interp, fname); result = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); /* properly convert string to system encoding. */ Tcl_DStringInit(&dstr); Tcl_UtfToExternalDString(NULL, result, -1, &dstr); result = Tcl_DStringValue(&dstr); if (code != TCL_OK) { putlog(LOG_MISC, "*", "Tcl error in file '%s':", fname); putlog(LOG_MISC, "*", "%s", result); Tcl_BackgroundError(interp); code = 0; /* JJM: refactored to remove premature return */ } else { /* Refresh internal variables */ code = 1; } Tcl_DStringFree(&dstr); return code; } void add_tcl_strings(tcl_strings *list) { int i; strinfo *st; int tmp; for (i = 0; list[i].name; i++) { st = nmalloc(sizeof *st); strtot += sizeof(strinfo); st->max = list[i].length - (list[i].flags & STR_DIR); if (list[i].flags & STR_PROTECT) st->max = -st->max; st->str = list[i].buf; st->flags = (list[i].flags & STR_DIR); tmp = protect_readonly; protect_readonly = 0; tcl_eggstr((ClientData) st, interp, list[i].name, NULL, TCL_TRACE_WRITES); protect_readonly = tmp; tcl_eggstr((ClientData) st, interp, list[i].name, NULL, TCL_TRACE_READS); Tcl_TraceVar(interp, list[i].name, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, tcl_eggstr, (ClientData) st); } } void rem_tcl_strings(tcl_strings *list) { int i, f; strinfo *st; f = TCL_GLOBAL_ONLY | TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS; for (i = 0; list[i].name; i++) { st = (strinfo *) Tcl_VarTraceInfo(interp, list[i].name, f, tcl_eggstr, NULL); Tcl_UntraceVar(interp, list[i].name, f, tcl_eggstr, st); if (st != NULL) { strtot -= sizeof(strinfo); nfree(st); } } } void add_tcl_ints(tcl_ints *list) { int i, tmp; intinfo *ii; for (i = 0; list[i].name; i++) { ii = nmalloc(sizeof *ii); strtot += sizeof(intinfo); ii->var = list[i].val; ii->ro = list[i].readonly; tmp = protect_readonly; protect_readonly = 0; tcl_eggint((ClientData) ii, interp, list[i].name, NULL, TCL_TRACE_WRITES); protect_readonly = tmp; tcl_eggint((ClientData) ii, interp, list[i].name, NULL, TCL_TRACE_READS); Tcl_TraceVar(interp, list[i].name, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, tcl_eggint, (ClientData) ii); } } void rem_tcl_ints(tcl_ints *list) { int i, f; intinfo *ii; f = TCL_GLOBAL_ONLY | TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS; for (i = 0; list[i].name; i++) { ii = (intinfo *) Tcl_VarTraceInfo(interp, list[i].name, f, tcl_eggint, NULL); Tcl_UntraceVar(interp, list[i].name, f, tcl_eggint, (ClientData) ii); if (ii) { strtot -= sizeof(intinfo); nfree(ii); } } } /* Allocate couplet space for tracing couplets */ void add_tcl_coups(tcl_coups *list) { coupletinfo *cp; int i; for (i = 0; list[i].name; i++) { cp = nmalloc(sizeof *cp); strtot += sizeof(coupletinfo); cp->left = list[i].lptr; cp->right = list[i].rptr; tcl_eggcouplet((ClientData) cp, interp, list[i].name, NULL, TCL_TRACE_WRITES); tcl_eggcouplet((ClientData) cp, interp, list[i].name, NULL, TCL_TRACE_READS); Tcl_TraceVar(interp, list[i].name, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, tcl_eggcouplet, (ClientData) cp); } } void rem_tcl_coups(tcl_coups *list) { int i, f; coupletinfo *cp; f = TCL_GLOBAL_ONLY | TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS; for (i = 0; list[i].name; i++) { cp = (coupletinfo *) Tcl_VarTraceInfo(interp, list[i].name, f, tcl_eggcouplet, NULL); strtot -= sizeof(coupletinfo); Tcl_UntraceVar(interp, list[i].name, f, tcl_eggcouplet, (ClientData) cp); nfree(cp); } } /* Check if the Tcl library supports threads */ int tcl_threaded() { if (Tcl_GetCurrentThread() != (Tcl_ThreadId)0) return 1; return 0; } /* Check if we need to fork before initializing Tcl */ int fork_before_tcl() { return 0; } time_t get_expire_time(Tcl_Interp * irp, const char *s) { char *endptr; long expire_foo = strtol(s, &endptr, 10); if (*endptr) { Tcl_SetResult(irp, "bogus expire time", TCL_STATIC); return -1; } if (expire_foo < 0) { Tcl_SetResult(irp, "expire time must be 0 (perm) or greater than 0 days", TCL_STATIC); return -1; } if (expire_foo == 0) return 0; if (expire_foo > (60 * 24 * 2000)) { Tcl_SetResult(irp, "expire time must be equal to or less than 2000 days", TCL_STATIC); return -1; } return now + 60 * expire_foo; } eggheads-eggdrop-9c00fe1/src/tcldcc.c000066400000000000000000001223621524730042700175200ustar00rootroot00000000000000/* * tcldcc.c -- handles: * Tcl stubs for the dcc commands */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" #include #include "tandem.h" #include "modules.h" #include #include extern Tcl_Interp *interp; extern tcl_timer_t *timer, *utimer; extern struct dcc_t *dcc; extern char botnetnick[], listen_ip[]; extern int dcc_total, backgrd, parties, make_userfile, remote_boots, max_dcc, conmask; #ifdef IPV6 extern int pref_af; #endif extern volatile sig_atomic_t do_restart; #ifdef TLS extern int tls_vfydcc; extern sock_list *socklist; #endif extern party_t *party; extern tand_t *tandbot; extern time_t now; extern unsigned long otraffic_irc, otraffic_irc_today, itraffic_irc, itraffic_irc_today, otraffic_bn, otraffic_bn_today, itraffic_bn, itraffic_bn_today, otraffic_dcc, otraffic_dcc_today, itraffic_dcc, itraffic_dcc_today, otraffic_trans, otraffic_trans_today, itraffic_trans, itraffic_trans_today, otraffic_unknown, itraffic_unknown, otraffic_unknown_today, itraffic_unknown_today; static struct portmap *root = NULL; int expmem_tcldcc(void) { int tot = 0; struct portmap *pmap; for (pmap = root; pmap; pmap = pmap->next) tot += sizeof(struct portmap); return tot; } static int tcl_putdcc STDVAR { int j; BADARGS(3, 4, " idx text ?options?"); if ((argc == 4) && strcasecmp(argv[3], "-raw")) { Tcl_AppendResult(irp, "unknown putdcc option: should be ", "-raw", NULL); return TCL_ERROR; } j = findidx(atoi(argv[1])); if (j < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (argc == 4) tputs(dcc[j].sock, argv[2], strlen(argv[2])); else dumplots(j, "", argv[2]); return TCL_OK; } /* Allows tcl scripts to send out raw data. Can be used for fast server write. * The server idx is 0. * * Usage: putdccraw * * Example: putdccraw 6 13 "eggdrop rulz\n" * * Added by . */ static int tcl_putdccraw STDVAR { #if 0 int i, j = 0, z; BADARGS(4, 4, " idx size text"); z = atoi(argv[1]); for (i = 0; i < dcc_total; i++) { if (!z && !strcmp(dcc[i].nick, "(server)")) { j = dcc[i].sock; break; } else if (dcc[i].sock == z) { j = dcc[i].sock; break; } } if (i == dcc_total) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } tputs(j, argv[3], atoi(argv[2])); return TCL_OK; #endif Tcl_AppendResult(irp, "putdccraw is deprecated. " "Please use putdcc/putnow instead.", NULL); return TCL_ERROR; } static int tcl_dccsimul STDVAR { int idx; BADARGS(3, 3, " idx command"); idx = findidx(atoi(argv[1])); if (idx >= 0 && (dcc[idx].type->flags & DCT_SIMUL)) { int l = strlen(argv[2]); if (l > 510) { l = 510; argv[2][510] = 0; /* Restrict length of cmd */ } if (dcc[idx].type && dcc[idx].type->activity) { dcc[idx].type->activity(idx, argv[2], l); return TCL_OK; } } else { Tcl_AppendResult(irp, "invalid idx", NULL); } return TCL_ERROR; } static int tcl_dccbroadcast STDVAR { char msg[401]; BADARGS(2, 2, " message"); strlcpy(msg, argv[1], sizeof msg); chatout("*** %s\n", msg); botnet_send_chat(-1, botnetnick, msg); check_tcl_bcst(botnetnick, -1, msg); return TCL_OK; } static int tcl_hand2idx STDVAR { int i; char s[11]; BADARGS(2, 2, " handle"); for (i = 0; i < dcc_total; i++) if ((dcc[i].type->flags & (DCT_SIMUL | DCT_BOT)) && !strcasecmp(argv[1], dcc[i].nick)) { egg_snprintf(s, sizeof s, "%ld", dcc[i].sock); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } Tcl_AppendResult(irp, "-1", NULL); return TCL_OK; } static int tcl_getchan STDVAR { char s[7]; int idx; BADARGS(2, 2, " idx"); idx = findidx(atoi(argv[1])); if (idx < 0 || (dcc[idx].type != &DCC_CHAT && dcc[idx].type != &DCC_SCRIPT)) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (dcc[idx].type == &DCC_SCRIPT) egg_snprintf(s, sizeof s, "%d", dcc[idx].u.script->u.chat->channel); else egg_snprintf(s, sizeof s, "%d", dcc[idx].u.chat->channel); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_setchan STDVAR { int idx, chan; module_entry *me; BADARGS(3, 3, " idx channel"); idx = findidx(atoi(argv[1])); if (idx < 0 || (dcc[idx].type != &DCC_CHAT && dcc[idx].type != &DCC_SCRIPT)) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (argv[2][0] < '0' || argv[2][0] > '9') { if (!strcmp(argv[2], "-1") || !strcasecmp(argv[2], "off")) chan = -1; else { Tcl_SetVar(irp, "_chan", argv[2], 0); if (Tcl_VarEval(irp, "assoc ", "$_chan", NULL) != TCL_OK || tcl_resultempty()) { Tcl_AppendResult(irp, "channel name is invalid", NULL); return TCL_ERROR; } chan = tcl_resultint(); } } else chan = atoi(argv[2]); if ((chan < -1) || (chan > 199999)) { Tcl_AppendResult(irp, "channel out of range; must be -1 through 199999", NULL); return TCL_ERROR; } if (dcc[idx].type == &DCC_SCRIPT) dcc[idx].u.script->u.chat->channel = chan; else { int oldchan = dcc[idx].u.chat->channel; if (dcc[idx].u.chat->channel >= 0) { if ((chan >= GLOBAL_CHANS) && (oldchan < GLOBAL_CHANS)) botnet_send_part_idx(idx, "*script*"); check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock, dcc[idx].u.chat->channel); } dcc[idx].u.chat->channel = chan; if (chan < GLOBAL_CHANS) botnet_send_join_idx(idx, oldchan); check_tcl_chjn(botnetnick, dcc[idx].nick, chan, geticon(idx), dcc[idx].sock, dcc[idx].host); } /* Console autosave. */ if ((me = module_find("console", 1, 1))) { Function *func = me->funcs; (func[CONSOLE_DOSTORE]) (idx); } return TCL_OK; } static int tcl_dccputchan STDVAR { int chan; char msg[401]; BADARGS(3, 3, " channel message"); chan = atoi(argv[1]); if ((chan < 0) || (chan > 199999)) { Tcl_AppendResult(irp, "channel out of range; must be 0 through 199999", NULL); return TCL_ERROR; } strlcpy(msg, argv[2], sizeof msg); chanout_but(-1, chan, "*** %s\n", argv[2]); botnet_send_chan(-1, botnetnick, NULL, chan, argv[2]); check_tcl_bcst(botnetnick, chan, argv[2]); return TCL_OK; } static int tcl_do_console(Tcl_Interp *irp, ClientData cd, int argc, char **argv, int reset) { int i, j, pls, arg; module_entry *me; i = findidx(atoi(argv[1])); if (i < 0 || dcc[i].type != &DCC_CHAT) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } pls = 1; for (arg = 2; arg < argc; arg++) { if (argv[arg][0] && !reset && ((strchr(CHANMETA, argv[arg][0]) != NULL) || (argv[arg][0] == '*'))) { if ((argv[arg][0] != '*') && (!findchan_by_dname(argv[arg]))) { /* If we don't find the channel, and it starts with a +, assume it * should be the console flags to set. */ if (argv[arg][0] == '+') goto do_console_flags; Tcl_AppendResult(irp, "invalid channel", NULL); return TCL_ERROR; } strlcpy(dcc[i].u.chat->con_chan, argv[arg], sizeof dcc[i].u.chat->con_chan); } else { if (!reset && (argv[arg][0] != '+') && (argv[arg][0] != '-')) dcc[i].u.chat->con_flags = 0; do_console_flags: if (reset) { dcc[i].u.chat->con_flags = (dcc[i].user && (dcc[i].user->flags & USER_MASTER) ? conmask : 0); } else { for (j = 0; j < strlen(argv[arg]); j++) { if (argv[arg][j] == '+') pls = 1; else if (argv[arg][j] == '-') pls = -1; else { char s[2]; s[0] = argv[arg][j]; s[1] = 0; if (pls == 1) dcc[i].u.chat->con_flags |= logmodes(s); else dcc[i].u.chat->con_flags &= ~logmodes(s); } } } } } Tcl_AppendElement(irp, dcc[i].u.chat->con_chan); Tcl_AppendElement(irp, masktype(dcc[i].u.chat->con_flags)); /* Console autosave. */ if (argc > 2 && (me = module_find("console", 1, 1))) { Function *func = me->funcs; (func[CONSOLE_DOSTORE]) (i); } return TCL_OK; } static int tcl_console STDVAR { BADARGS(2, 4, " idx ?channel? ?console-modes?"); return tcl_do_console(irp, cd, argc, argv, 0); } static int tcl_resetconsole STDVAR { BADARGS(2, 2, " idx"); return tcl_do_console(irp, cd, argc, argv, 1); } static int tcl_strip STDVAR { int i, j, pls, arg; module_entry *me; BADARGS(2, 4, " idx ?strip-flags?"); i = findidx(atoi(argv[1])); if (i < 0 || dcc[i].type != &DCC_CHAT) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } pls = 1; for (arg = 2; arg < argc; arg++) { if ((argv[arg][0] != '+') && (argv[arg][0] != '-')) dcc[i].u.chat->strip_flags = 0; for (j = 0; j < strlen(argv[arg]); j++) { if (argv[arg][j] == '+') pls = 1; else if (argv[arg][j] == '-') pls = -1; else { char s[2]; s[0] = argv[arg][j]; s[1] = 0; if (pls == 1) dcc[i].u.chat->strip_flags |= stripmodes(s); else dcc[i].u.chat->strip_flags &= ~stripmodes(s); } } } Tcl_AppendElement(irp, stripmasktype(dcc[i].u.chat->strip_flags)); /* Console autosave. */ if (argc > 2 && (me = module_find("console", 1, 1))) { Function *func = me->funcs; (func[CONSOLE_DOSTORE]) (i); } return TCL_OK; } static int tcl_echo STDVAR { int i; module_entry *me; BADARGS(2, 3, " idx ?status?"); i = findidx(atoi(argv[1])); if (i < 0 || dcc[i].type != &DCC_CHAT) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (argc == 3) { if (atoi(argv[2])) dcc[i].status |= STAT_ECHO; else dcc[i].status &= ~STAT_ECHO; } if (dcc[i].status & STAT_ECHO) Tcl_AppendResult(irp, "1", NULL); else Tcl_AppendResult(irp, "0", NULL); /* Console autosave. */ if (argc > 2 && (me = module_find("console", 1, 1))) { Function *func = me->funcs; (func[CONSOLE_DOSTORE]) (i); } return TCL_OK; } static int tcl_page STDVAR { int i; char x[20]; module_entry *me; BADARGS(2, 3, " idx ?status?"); i = findidx(atoi(argv[1])); if (i < 0 || dcc[i].type != &DCC_CHAT) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (argc == 3) { int l = atoi(argv[2]); if (!l) dcc[i].status &= ~STAT_PAGE; else { dcc[i].status |= STAT_PAGE; dcc[i].u.chat->max_line = l; } } if (dcc[i].status & STAT_PAGE) { egg_snprintf(x, sizeof x, "%d", dcc[i].u.chat->max_line); Tcl_AppendResult(irp, x, NULL); } else Tcl_AppendResult(irp, "0", NULL); /* Console autosave. */ if ((argc > 2) && (me = module_find("console", 1, 1))) { Function *func = me->funcs; (func[CONSOLE_DOSTORE]) (i); } return TCL_OK; } static int tcl_control STDVAR { int idx; void *hold; BADARGS(3, 3, " idx command"); idx = findidx(atoi(argv[1])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (dcc[idx].type->flags & DCT_CHAT) { if (dcc[idx].u.chat->channel >= 0) { chanout_but(idx, dcc[idx].u.chat->channel, "*** %s has gone.\n", dcc[idx].nick); check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock, dcc[idx].u.chat->channel); botnet_send_part_idx(idx, "gone"); } check_tcl_chof(dcc[idx].nick, dcc[idx].sock); } hold = dcc[idx].u.other; dcc[idx].u.script = get_data_ptr(sizeof(struct script_info)); dcc[idx].u.script->u.other = hold; dcc[idx].u.script->type = dcc[idx].type; dcc[idx].type = &DCC_SCRIPT; /* Do not buffer data anymore. All received and stored data is passed * over to the dcc functions from now on. */ sockoptions(dcc[idx].sock, EGG_OPTION_UNSET, SOCK_BUFFER); strlcpy(dcc[idx].u.script->command, argv[2], sizeof dcc[idx].u.script->command); return TCL_OK; } static int tcl_valididx STDVAR { int idx; BADARGS(2, 2, " idx"); idx = findidx(atoi(argv[1])); if (idx < 0 || !(dcc[idx].type->flags & DCT_VALIDIDX)) Tcl_AppendResult(irp, "0", NULL); else Tcl_AppendResult(irp, "1", NULL); return TCL_OK; } static int tcl_killdcc STDVAR { int idx; BADARGS(2, 3, " idx ?reason?"); idx = findidx(atoi(argv[1])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if ((dcc[idx].sock == STDOUT) && !backgrd) /* Don't kill terminal socket */ return TCL_OK; if (dcc[idx].type->flags & DCT_CHAT) { /* Make sure 'whom' info is updated */ chanout_but(idx, dcc[idx].u.chat->channel, "*** %s has left the %s%s%s\n", dcc[idx].nick, dcc[idx].u.chat ? "channel" : "partyline", argc == 3 ? ": " : "", argc == 3 ? argv[2] : ""); botnet_send_part_idx(idx, argc == 3 ? argv[2] : ""); if ((dcc[idx].u.chat->channel >= 0) && (dcc[idx].u.chat->channel < GLOBAL_CHANS)) check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock, dcc[idx].u.chat->channel); check_tcl_chof(dcc[idx].nick, dcc[idx].sock); } killsock(dcc[idx].sock); killtransfer(idx); lostdcc(idx); return TCL_OK; } static int tcl_putbot STDVAR { int i; char msg[401]; BADARGS(3, 3, " botnick message"); i = nextbot(argv[1]); if (i < 0) { Tcl_AppendResult(irp, "bot is not on the botnet", NULL); return TCL_ERROR; } strlcpy(msg, argv[2], sizeof msg); botnet_send_zapf(i, botnetnick, argv[1], msg); return TCL_OK; } static int tcl_putallbots STDVAR { char msg[401]; BADARGS(2, 2, " message"); strlcpy(msg, argv[1], sizeof msg); botnet_send_zapf_broad(-1, botnetnick, NULL, msg); return TCL_OK; } static int tcl_idx2hand STDVAR { int idx; BADARGS(2, 2, " idx"); idx = findidx(atoi(argv[1])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } Tcl_AppendResult(irp, dcc[idx].nick, NULL); return TCL_OK; } static int tcl_islinked STDVAR { int i; BADARGS(2, 2, " bot"); i = nextbot(argv[1]); if (i < 0) Tcl_AppendResult(irp, "0", NULL); else Tcl_AppendResult(irp, "1", NULL); return TCL_OK; } static int tcl_bots STDVAR { tand_t *bot; BADARGS(1, 1, ""); for (bot = tandbot; bot; bot = bot->next) Tcl_AppendElement(irp, bot->bot); return TCL_OK; } static int tcl_botlist STDVAR { char *p, sh[2], string[20]; EGG_CONST char *list[4]; tand_t *bot; BADARGS(1, 1, ""); sh[1] = 0; list[3] = sh; list[2] = string; for (bot = tandbot; bot; bot = bot->next) { list[0] = bot->bot; list[1] = (bot->uplink == (tand_t *) 1) ? botnetnick : bot->uplink->bot; strlcpy(string, int_to_base10(bot->ver), sizeof string); sh[0] = bot->share; p = Tcl_Merge(4, list); Tcl_AppendElement(irp, p); Tcl_Free((char *) p); } return TCL_OK; } static void build_dcc_list(Tcl_Interp *irp, char *idxstr, char *nick, char *host, char *portstring, char *type, char *other, char *timestamp) { char *p; EGG_CONST char *list[7]; list[0] = idxstr; list[1] = nick; list[2] = host; list[3] = portstring; list[4] = type; list[5] = other; list[6] = timestamp; p = Tcl_Merge(7, list); Tcl_AppendElement(irp, p); Tcl_Free((char *) p); } /* Build and return a list of lists of all sockets, in dict-readable format */ static void build_sock_list(Tcl_Interp *irp, Tcl_Obj *masterlist, char *idxstr, char *nick, char *host, char *ip, int port, int secure, char *type, char *other, char *timestamp) { EGG_CONST char *val[] = {"idx", "handle", "host", "ip", "port", "secure", "type", "info", "time"}; Tcl_Obj *thelist; char securestr[2], portstr[6]; egg_snprintf(securestr, sizeof securestr, "%d", secure); egg_snprintf(portstr, sizeof portstr, "%d", port); thelist = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[0], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(idxstr, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[1], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(nick, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[2], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(host, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[3], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(ip, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[4], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(portstr, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[5], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(securestr, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[6], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(type, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[7], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(other, -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[8], -1)); Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(timestamp, -1)); Tcl_ListObjAppendElement(irp, masterlist, thelist); Tcl_SetObjResult(irp, masterlist); } /* Gather information for dcclist or socklist */ static void dccsocklist(Tcl_Interp *irp, int argc, char *type, int src) { int i; char idxstr[10], timestamp[21], other[160]; char portstring[7]; /* ssl + portmax + NULL */ char s[EGG_INET_ADDRSTRLEN]; socklen_t namelen; struct sockaddr_storage ss; Tcl_Obj *masterlist = NULL; /* initialize to NULL to make old gcc versions * happy */ if (src) { masterlist = Tcl_NewListObj(0, NULL); } for (i = 0; i < dcc_total; i++) { if (argc == 1 || ((argc == 2) && (dcc[i].type && !strcasecmp(dcc[i].type->name, type)))) { egg_snprintf(idxstr, sizeof idxstr, "%ld", dcc[i].sock); snprintf(timestamp, sizeof timestamp, "%" PRId64, (int64_t) dcc[i].timeval); if (dcc[i].type && dcc[i].type->display) dcc[i].type->display(i, other); else { egg_snprintf(other, sizeof other, "?:%lX !! ERROR !!", (long) dcc[i].type); break; } #ifdef TLS egg_snprintf(portstring, sizeof portstring, "%s%d", dcc[i].ssl ? "+" : "", dcc[i].port); #else egg_snprintf(portstring, sizeof portstring, "%d", dcc[i].port); #endif /* If this came from dcclist... */ if (!src) { build_dcc_list(irp, idxstr, dcc[i].nick, (dcc[i].host[0] == '\0') ? iptostr(&dcc[i].sockname.addr.sa) : dcc[i].host, portstring, dcc[i].type ? dcc[i].type->name : "*UNKNOWN*", other, timestamp); /* If this came from socklist... */ } else { /* Update dcc table socket information, needed for getting local IP */ namelen = sizeof ss; getsockname(dcc[i].sock, (struct sockaddr *) &ss, &namelen); if (ss.ss_family == AF_INET) { struct sockaddr_in *saddr = (struct sockaddr_in *)&ss; inet_ntop(AF_INET, &(saddr->sin_addr), s, sizeof s); #ifdef IPV6 } else if (ss.ss_family == AF_INET6) { struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&ss; inet_ntop(AF_INET6, &(saddr->sin6_addr), s, sizeof s); #endif } build_sock_list(irp, masterlist, idxstr, dcc[i].nick, (dcc[i].host[0] == '\0') ? iptostr(&dcc[i].sockname.addr.sa) : dcc[i].host, s, dcc[i].port, #ifdef TLS dcc[i].ssl, #else 0, #endif dcc[i].type ? dcc[i].type->name : "*UNKNOWN*", other, timestamp); } } } } static int tcl_socklist STDVAR { BADARGS(1, 2, " ?type?"); dccsocklist(irp, argc, (argc == 2) ? argv[1] : NULL, 1); return TCL_OK; } static int tcl_dcclist STDVAR { BADARGS(1, 2, " ?type?"); dccsocklist(irp, argc, (argc == 2) ? argv[1] : NULL, 0); return TCL_OK; } static int tcl_whom STDVAR { int chan, i; char c[2], idle[21], work[20], *p; EGG_CONST char *list[7]; BADARGS(2, 2, " chan"); if (argv[1][0] == '*') chan = -1; else { if ((argv[1][0] < '0') || (argv[1][0] > '9')) { Tcl_SetVar(interp, "_chan", argv[1], 0); if ((Tcl_VarEval(interp, "assoc ", "$_chan", NULL) != TCL_OK) || tcl_resultempty()) { Tcl_AppendResult(irp, "channel name is invalid", NULL); return TCL_ERROR; } chan = tcl_resultint(); } else chan = atoi(argv[1]); if ((chan < 0) || (chan > 199999)) { Tcl_AppendResult(irp, "channel out of range; must be 0 through 199999", NULL); return TCL_ERROR; } } for (i = 0; i < dcc_total; i++) if (dcc[i].type == &DCC_CHAT) { if (dcc[i].u.chat->channel == chan || chan == -1) { c[0] = geticon(i); c[1] = 0; snprintf(idle, sizeof idle, "%" PRId64, (int64_t) ((now - dcc[i].timeval) / 60)); list[0] = dcc[i].nick; list[1] = botnetnick; list[2] = dcc[i].host; list[3] = c; list[4] = idle; list[5] = dcc[i].u.chat->away ? dcc[i].u.chat->away : ""; if (chan == -1) { egg_snprintf(work, sizeof work, "%d", dcc[i].u.chat->channel); list[6] = work; } p = Tcl_Merge((chan == -1) ? 7 : 6, list); Tcl_AppendElement(irp, p); Tcl_Free((char *) p); } } for (i = 0; i < parties; i++) { if (party[i].chan == chan || chan == -1) { c[0] = party[i].flag; c[1] = 0; if (party[i].timer == 0L) strcpy(idle, "0"); else snprintf(idle, sizeof idle, "%" PRId64, (int64_t) ((now - party[i].timer) / 60)); list[0] = party[i].nick; list[1] = party[i].bot; list[2] = party[i].from ? party[i].from : ""; list[3] = c; list[4] = idle; list[5] = party[i].status & PLSTAT_AWAY ? party[i].away : ""; if (chan == -1) { egg_snprintf(work, sizeof work, "%d", party[i].chan); list[6] = work; } p = Tcl_Merge((chan == -1) ? 7 : 6, list); Tcl_AppendElement(irp, p); Tcl_Free((char *) p); } } return TCL_OK; } static int tcl_dccused STDVAR { char s[20]; BADARGS(1, 1, ""); egg_snprintf(s, sizeof s, "%d", dcc_total); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_getdccidle STDVAR { int x, idx; char s[21]; BADARGS(2, 2, " idx"); idx = findidx(atoi(argv[1])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } x = (now - dcc[idx].timeval); egg_snprintf(s, sizeof s, "%d", x); Tcl_AppendElement(irp, s); return TCL_OK; } static int tcl_getdccaway STDVAR { int idx; BADARGS(2, 2, " idx"); idx = findidx(atol(argv[1])); if (idx < 0 || dcc[idx].type != &DCC_CHAT) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (dcc[idx].u.chat->away == NULL) return TCL_OK; Tcl_AppendResult(irp, dcc[idx].u.chat->away, NULL); return TCL_OK; } static int tcl_setdccaway STDVAR { int idx; BADARGS(3, 3, " idx message"); idx = findidx(atol(argv[1])); if (idx < 0 || dcc[idx].type != &DCC_CHAT) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (!argv[2][0]) { if (dcc[idx].u.chat->away != NULL) not_away(idx); return TCL_OK; } set_away(idx, argv[2]); return TCL_OK; } static int tcl_link STDVAR { int x, i; char bot[HANDLEN + 1], bot2[HANDLEN + 1]; BADARGS(2, 3, " ?via-bot? bot"); strlcpy(bot, argv[1], sizeof bot); if (argc == 3) { x = 1; strlcpy(bot2, argv[2], sizeof bot2); i = nextbot(bot); if (i < 0) x = 0; else botnet_send_link(i, botnetnick, bot, bot2); } else x = botlink("", -2, bot); egg_snprintf(bot, sizeof bot, "%d", x); Tcl_AppendResult(irp, bot, NULL); return TCL_OK; } static int tcl_unlink STDVAR { int i, x; char bot[HANDLEN + 1]; BADARGS(2, 3, " bot ?comment?"); strlcpy(bot, argv[1], sizeof bot); i = nextbot(bot); if (i < 0) x = 0; else { x = 1; if (!strcasecmp(bot, dcc[i].nick)) x = botunlink(-2, bot, argv[2], botnetnick); else botnet_send_unlink(i, botnetnick, lastbot(bot), bot, argv[2]); } egg_snprintf(bot, sizeof bot, "%d", x); Tcl_AppendResult(irp, bot, NULL); return TCL_OK; } static int tcl_connect STDVAR { int i, sock; char s[81]; BADARGS(3, 3, " hostname port"); if (dcc_total == max_dcc && increase_socks_max()) { Tcl_AppendResult(irp, "out of dcc table space", NULL); return TCL_ERROR; } i = new_dcc(&DCC_SOCKET, 0); if (i < 0) { Tcl_AppendResult(irp, "Could not allocate socket.", NULL); return TCL_ERROR; } sock = open_telnet(i, argv[1], atoi(argv[2])); if (sock < 0) { switch (sock) { case -3: Tcl_AppendResult(irp, MISC_NOFREESOCK, NULL); break; case -2: Tcl_AppendResult(irp, "DNS lookup failed", NULL); break; default: Tcl_AppendResult(irp, strerror(errno), NULL); } lostdcc(i); return TCL_ERROR; } #ifdef TLS if (*argv[2] == '+') { if (ssl_handshake(sock, TLS_CONNECT, 0, LOG_MISC, NULL, NULL)) { killsock(sock); lostdcc(i); strlcpy(s, "Failed to establish a TLS session", sizeof s); Tcl_AppendResult(irp, s, NULL); return TCL_ERROR; } else dcc[i].ssl = 1; } #endif strcpy(dcc[i].nick, "*"); strlcpy(dcc[i].host, argv[1], UHOSTMAX); egg_snprintf(s, sizeof s, "%d", sock); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int setlisten(Tcl_Interp *irp, char *ip, char *portp, char *type, char *maskproc, char *flag) { int i, idx = -1, port, realport, found=0, ipv4=1, error; char s[11], msg[256], newip[EGG_INET_ADDRSTRLEN]; struct portmap *pmap = NULL, *pold = NULL; sockname_t name; struct in_addr ipaddr4; struct addrinfo hint, *ipaddr = NULL; #ifdef IPV6 struct in6_addr ipaddr6; #endif memset(&hint, '\0', sizeof hint); hint.ai_family = PF_UNSPEC; hint.ai_flags = AI_NUMERICHOST; if (!ip[0]) { #ifdef IPV6 if (pref_af) { strlcpy(newip, "::", sizeof newip); } else { #endif strlcpy(newip, "0.0.0.0", sizeof newip); #ifdef IPV6 } #endif } else { strlcpy(newip, ip, sizeof newip); } /* Return addrinfo struct ipaddr containing family... */ error = getaddrinfo(newip, NULL, &hint, &ipaddr); if (!error) { /* Load network address to in(6)_addr struct for later byte comparisons */ if (ipaddr->ai_family == AF_INET) { inet_pton(AF_INET, newip, &ipaddr4); } #ifdef IPV6 else if (ipaddr->ai_family == AF_INET6) { inet_pton(AF_INET6, newip, &ipaddr6); ipv4 = 0; } #endif if (ipaddr) /* The behavior of freeadrinfo(NULL) is left unspecified by RFCs * 2553 and 3493. Avoid to be compatible with all OSes. */ freeaddrinfo(ipaddr); } else if (error == EAI_NONAME) /* currently setlisten() handles only ip not hostname */ putlog(LOG_MISC, "*", "tcldcc: setlisten(): getaddrinfo(): ip %s not known", newip); else putlog(LOG_MISC, "*", "tcldcc: setlisten(): getaddrinfo(): error = %s", gai_strerror(error)); port = realport = atoi(portp); for (pmap = root; pmap; pold = pmap, pmap = pmap->next) { if (pmap->realport == port) { port = pmap->mappedto; break; } } for (i = 0; i < dcc_total; i++) { if ((dcc[i].type == &DCC_TELNET) && (dcc[i].port == port)) { idx = i; found = 1; /* Check if this is an exact match and skip these checks (ie, rehash) */ if (ipv4) { if (ipaddr4.s_addr == dcc[idx].sockname.addr.s4.sin_addr.s_addr) { break; } } #ifdef IPV6 else if (ipaddr6.s6_addr == dcc[idx].sockname.addr.s6.sin6_addr.s6_addr) { break; } /* Check if the bound IP is IPvX, and the new IP is IPvY */ if (((ipv4) && (dcc[idx].sockname.addr.sa.sa_family != AF_INET)) || ((!ipv4) && (dcc[idx].sockname.addr.sa.sa_family != AF_INET6))) { found = 0; continue; } #endif /* Check if IP is specific, but the already-bound IP is all-interfaces */ if (ipv4) { if ((ipaddr4.s_addr != 0) && (dcc[idx].sockname.addr.s4.sin_addr.s_addr == 0)) { Tcl_AppendResult(irp, "this port is already bound to 0.0.0.0 on this " "machine, remove it before trying to bind to this IP", NULL); return TCL_ERROR; } } #ifdef IPV6 else if ((!IN6_IS_ADDR_UNSPECIFIED(&ipaddr6)) && (IN6_IS_ADDR_UNSPECIFIED(&dcc[idx].sockname.addr.s6.sin6_addr))) { Tcl_AppendResult(irp, "this port is already bound to :: on this " "machine, remove it before trying to bind to this IP", NULL); return TCL_ERROR; } #endif /* Check if IP is all-interfaces, but the already-bound IP is specific */ if (ipv4) { if ((ipaddr4.s_addr == 0) && (dcc[idx].sockname.addr.s4.sin_addr.s_addr != 0)) { Tcl_AppendResult(irp, "this port is already bound to a specific IP " "on this machine, remove it before trying to bind to all " "interfaces", NULL); return TCL_ERROR; } } #ifdef IPV6 else if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr6) && (!IN6_IS_ADDR_UNSPECIFIED(&dcc[idx].sockname.addr.s6.sin6_addr))) { Tcl_AppendResult(irp, "this port is already bound to a specific IP " "on this machine, remove it before trying to bind to this all " "interfaces", NULL); return TCL_ERROR; } #endif } } if (!strcasecmp(type, "off")) { if (pmap) { if (pold) pold->next = pmap->next; else root = pmap->next; nfree(pmap); } /* Remove */ if (idx < 0) { Tcl_AppendResult(irp, "no such listen port is open", NULL); return TCL_ERROR; } killsock(dcc[idx].sock); lostdcc(idx); return TCL_OK; } /* If there isn't already something listening on that port, or there is but * it is something that may allow us to try a different IP for that port */ if ((idx < 0) || (!found)) { /* Make new one */ if (dcc_total >= max_dcc && increase_socks_max()) { Tcl_AppendResult(irp, "No more DCC slots available.", NULL); return TCL_ERROR; } /* We used to try up to 20 ports here, but have scientifically concluded * that is just silly. */ /* If we didn't find a listening ip/port, or we did but it isn't all * interfaces */ if (strlen(newip)) { setsockname(&name, newip, port, 1); i = open_address_listen(&name); if (i < 0) { snprintf(msg, sizeof msg, "Couldn't listen on port %d on %s: %s. " "Please check that the port is not already in use", realport, newip, strerror(errno)); Tcl_AppendResult(irp, msg, NULL); return TCL_ERROR; } } else { i = open_listen(&port); if (i < 0) { snprintf(msg, sizeof msg, "Couldn't listen on port %d on the given " "address: %s. Please check that the port is not already in use", realport, strerror(errno)); Tcl_AppendResult(irp, msg, NULL); return TCL_ERROR; } } idx = new_dcc(&DCC_TELNET, 0); dcc[idx].sockname.addrlen = sizeof(dcc[idx].sockname.addr); getsockname(i, &dcc[idx].sockname.addr.sa, &dcc[idx].sockname.addrlen); dcc[idx].sockname.family = dcc[idx].sockname.addr.sa.sa_family; dcc[idx].port = port; dcc[idx].sock = i; dcc[idx].timeval = now; } #ifdef TLS if (portp[0] == '+') dcc[idx].ssl = 1; else dcc[idx].ssl = 0; #endif /* script? */ if (!strcmp(type, "script")) { strcpy(dcc[idx].nick, "(script)"); if (flag) { dcc[idx].status = LSTN_PUBLIC; } strlcpy(dcc[idx].host, maskproc, UHOSTMAX); egg_snprintf(s, sizeof s, "%d", port); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } /* bots/users/all */ if (!strcmp(type, "bots")) strcpy(dcc[idx].nick, "(bots)"); else if (!strcmp(type, "users")) strcpy(dcc[idx].nick, "(users)"); else if (!strcmp(type, "all")) strcpy(dcc[idx].nick, "(telnet)"); else if (!strcmp(type, "webui")) strcpy(dcc[idx].nick, "(webui)"); if (maskproc[0]) strlcpy(dcc[idx].host, maskproc, UHOSTMAX); else strcpy(dcc[idx].host, "*"); egg_snprintf(s, sizeof s, "%d", port); Tcl_AppendResult(irp, s, NULL); if (!pmap) { pmap = nmalloc(sizeof(struct portmap)); pmap->next = root; root = pmap; } pmap->realport = realport; pmap->mappedto = port; #ifdef TLS putlog(LOG_MISC, "*", "Listening for telnet connections on %s port %s%d (%s).", iptostr(&dcc[idx].sockname.addr.sa), dcc[idx].ssl ? "+" : "", port, type); #else putlog(LOG_MISC, "*", "Listening for telnet connections on %s port %d (%s).", iptostr(&dcc[idx].sockname.addr.sa), port, type); #endif return TCL_OK; } /* Create a new listening port (or destroy one) * * listen [ip] bots/all/users [mask] * listen [ip] script * listen [ip] off */ static int tcl_listen STDVAR { char ip[121], maskproc[UHOSTMAX] = ""; char port[7], type[7], flag[4], *endptr; unsigned char buf[sizeof(struct in6_addr)]; int i = 1; /* People like to add comments to this command for some reason, and it can cause * errors that are difficult to figure out. Let's instead throw a more helpful * error for this case to get around BADARGS, and handle other cases further * down in the code * * Check if extra args are config comments */ if (argc > 6) { if (argv[6][0] == '#') { fatal(DCC_BADLISTEN, 0); } } BADARGS(3, 6, " ?ip? port type ?mask?/?proc flag?"); /* default listen-addr if not specified */ strlcpy(ip, listen_ip, sizeof(ip)); /* Check if IP exists, set to NULL if not */ strtol(argv[1], &endptr, 10); if (*endptr != '\0') { if (inet_pton(AF_INET, argv[1], buf) #ifdef IPV6 || inet_pton(AF_INET6, argv[1], buf) #endif ) { strlcpy(ip, argv[1], sizeof(ip)); i++; } else { Tcl_AppendResult(irp, "invalid IP address argument", NULL); return TCL_ERROR; } } /* Check for port */ if ((atoi(argv[i]) > 65535) || (atoi(argv[i]) < 1)) { Tcl_AppendResult(irp, "invalid listen port", NULL); return TCL_ERROR; } strlcpy(port, argv[i], sizeof(port)); i++; /* Check for listen type */ if (!argv[i]) { Tcl_AppendResult(irp, "missing listen type", NULL); return TCL_ERROR; } if ((strcmp(argv[i], "bots")) && (strcmp(argv[i], "users")) && (strcmp(argv[i], "all")) && (strcmp(argv[i], "off")) && (strcmp(argv[i], "script")) && (strcmp(argv[i], "webui"))) { Tcl_AppendResult(irp, "invalid listen type: must be one of ", "bots, users, all, off, script, webui", NULL); return TCL_ERROR; } strlcpy(type, argv[i], sizeof(type)); /* Check if mask or proc exists */ if ((((argc>3) && !ip[0]) || ((argc >4) && ip[0])) && (argv[i+1][0] != '#')) { /* Ignore config comments! */ i++; strlcpy(maskproc, argv[i], sizeof(maskproc)); } /* If script, check for proc and flag */ if (!strcmp(type, "script")) { if (!maskproc[0]) { Tcl_AppendResult(irp, "a proc name must be specified for a script listen", NULL); return TCL_ERROR; } if ((!ip[0] && (argc==5)) || (argc == 6)) { i++; if (strcmp(argv[i], "pub")) { Tcl_AppendResult(irp, "unknown flag: ", argv[i], ". allowed flags: pub", NULL); return TCL_ERROR; } strlcpy(flag, argv[i], sizeof flag); } } return setlisten(irp, ip, port, type, maskproc, flag); } static int tcl_boot STDVAR { char who[NOTENAMELEN + 1]; int i, ok = 0; BADARGS(2, 3, " user@bot ?reason?"); strlcpy(who, argv[1], sizeof who); if (strchr(who, '@') != NULL) { char whonick[HANDLEN + 1]; splitcn(whonick, who, '@', sizeof whonick); if (!strcasecmp(who, botnetnick)) strlcpy(who, whonick, sizeof who); else if (remote_boots > 0) { i = nextbot(who); if (i < 0) return TCL_OK; botnet_send_reject(i, botnetnick, NULL, whonick, who, argc >= 3 && argv[2] ? argv[2] : ""); } else return TCL_OK; } for (i = 0; i < dcc_total; i++) if (!ok && (dcc[i].type->flags & DCT_CANBOOT) && !strcasecmp(dcc[i].nick, who)) { do_boot(i, botnetnick, argc >= 3 && argv[2] ? argv[2] : ""); ok = 1; } return TCL_OK; } static int tcl_rehash STDVAR { BADARGS(1, 1, ""); if (make_userfile) { putlog(LOG_MISC, "*", "%s", USERF_NONEEDNEW); make_userfile = 0; } write_userfile(-1); putlog(LOG_MISC, "*", "%s", USERF_REHASHING); do_restart = -2; return TCL_OK; } static int tcl_restart STDVAR { BADARGS(1, 1, ""); if (!backgrd) { Tcl_AppendResult(interp, "You can't restart a -n bot", NULL); return TCL_ERROR; } if (make_userfile) { putlog(LOG_MISC, "*", "%s", USERF_NONEEDNEW); make_userfile = 0; } write_userfile(-1); putlog(LOG_MISC, "*", "%s", MISC_RESTARTING); wipe_timers(interp, &utimer); wipe_timers(interp, &timer); do_restart = -1; return TCL_OK; } static int tcl_traffic STDVAR { char buf[1024]; unsigned long out_total_today, out_total; unsigned long in_total_today, in_total; /* IRC traffic */ sprintf(buf, "irc %lu %lu %lu %lu", itraffic_irc_today, itraffic_irc + itraffic_irc_today, otraffic_irc_today, otraffic_irc + otraffic_irc_today); Tcl_AppendElement(irp, buf); /* Botnet traffic */ sprintf(buf, "botnet %lu %lu %lu %lu", itraffic_bn_today, itraffic_bn + itraffic_bn_today, otraffic_bn_today, otraffic_bn + otraffic_bn_today); Tcl_AppendElement(irp, buf); /* Partyline */ sprintf(buf, "partyline %lu %lu %lu %lu", itraffic_dcc_today, itraffic_dcc + itraffic_dcc_today, otraffic_dcc_today, otraffic_dcc + otraffic_dcc_today); Tcl_AppendElement(irp, buf); /* Transfer */ sprintf(buf, "transfer %lu %lu %lu %lu", itraffic_trans_today, itraffic_trans + itraffic_trans_today, otraffic_trans_today, otraffic_trans + otraffic_trans_today); Tcl_AppendElement(irp, buf); /* Misc traffic */ sprintf(buf, "misc %lu %lu %lu %lu", itraffic_unknown_today, itraffic_unknown + itraffic_unknown_today, otraffic_unknown_today, otraffic_unknown + otraffic_unknown_today); Tcl_AppendElement(irp, buf); /* Totals */ in_total_today = itraffic_irc_today + itraffic_bn_today + itraffic_dcc_today + itraffic_trans_today + itraffic_unknown_today; in_total = in_total_today + itraffic_irc + itraffic_bn + itraffic_dcc + itraffic_trans + itraffic_unknown; out_total_today = otraffic_irc_today + otraffic_bn_today + otraffic_dcc_today + itraffic_trans_today + otraffic_unknown_today; out_total = out_total_today + otraffic_irc + otraffic_bn + otraffic_dcc + otraffic_trans + otraffic_unknown; sprintf(buf, "total %lu %lu %lu %lu", in_total_today, in_total, out_total_today, out_total); Tcl_AppendElement(irp, buf); return TCL_OK; } tcl_cmds tcldcc_cmds[] = { {"putdcc", tcl_putdcc}, {"putdccraw", tcl_putdccraw}, {"putidx", tcl_putdcc}, {"dccsimul", tcl_dccsimul}, {"dccbroadcast", tcl_dccbroadcast}, {"hand2idx", tcl_hand2idx}, {"getchan", tcl_getchan}, {"setchan", tcl_setchan}, {"dccputchan", tcl_dccputchan}, {"console", tcl_console}, {"resetconsole", tcl_resetconsole}, {"strip", tcl_strip}, {"echo", tcl_echo}, {"page", tcl_page}, {"control", tcl_control}, {"valididx", tcl_valididx}, {"killdcc", tcl_killdcc}, {"putbot", tcl_putbot}, {"putallbots", tcl_putallbots}, {"idx2hand", tcl_idx2hand}, {"bots", tcl_bots}, {"botlist", tcl_botlist}, {"dcclist", tcl_dcclist}, {"socklist", tcl_socklist}, {"whom", tcl_whom}, {"dccused", tcl_dccused}, {"getdccidle", tcl_getdccidle}, {"getdccaway", tcl_getdccaway}, {"setdccaway", tcl_setdccaway}, {"islinked", tcl_islinked}, {"link", tcl_link}, {"unlink", tcl_unlink}, {"connect", tcl_connect}, {"listen", tcl_listen}, {"boot", tcl_boot}, {"rehash", tcl_rehash}, {"restart", tcl_restart}, {"traffic", tcl_traffic}, {NULL, NULL} }; eggheads-eggdrop-9c00fe1/src/tclegg.h000066400000000000000000000166371524730042700175450ustar00rootroot00000000000000/* * tclegg.h * stuff used by tcl.c and tclhash.c */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_TCLEGG_H #define _EGG_TCLEGG_H #include "lush.h" #ifndef MAKING_MODS # include "proto.h" #endif /* * Wow, this is old...CMD_LEAVE goes back to before version 0.9. * This is for partyline and filesys 'quit'. */ #define CMD_LEAVE (Function)(-1) /* Match types for check_tcl_bind(). */ #define MATCH_PARTIAL 0 #define MATCH_EXACT 1 #define MATCH_MASK 2 #define MATCH_CASE 3 #define MATCH_MODE 4 #define MATCH_CRON 5 /* * Bitwise 'or' these: */ /* Check flags; make sure the user has the flags required. */ #define BIND_USE_ATTR 0x010 /* Bind is stackable; more than one bind can have the same name. */ #define BIND_STACKABLE 0x020 /* Additional flag checking; check for +d, +k, etc. * Currently used for dcc, fil, msg, and pub bind types. * Note that this just causes the flag checking to use flagrec_ok() * instead of flagrec_eq(). */ /* FIXME: Should this really be used for the dcc and fil types since * they are only available to the partyline/filesys (+p/+x)? * Eggdrop's revenge code does not add default flags when * adding a user record for +d or +k flags. */ /* FIXME: This type actually seems to be obsolete. This was originally * used to check built-in types in Eggdrop version 1.0. */ #define BIND_HAS_BUILTINS 0x040 /* Want return; we want to know if the proc returns 1. * Side effect: immediate return; don't do any further * processing of stacked binds. */ #define BIND_WANTRET 0x080 /* Alternate args; replace args with the return result from the Tcl proc. */ #define BIND_ALTER_ARGS 0x100 /* Stacked return; we want to know if any proc returns 1, * and also want to process all stacked binds. */ #define BIND_STACKRET 0x200 /* Return values. */ #define BIND_NOMATCH 0 #define BIND_AMBIGUOUS 1 #define BIND_MATCHED 2 /* But the proc couldn't be found */ #define BIND_EXECUTED 3 #define BIND_EXEC_LOG 4 /* Proc returned 1 -> wants to be logged */ #define BIND_QUIT 5 /* CMD_LEAVE 'quit' from partyline or filesys */ /* Extra commands are stored in Tcl hash tables (one hash table for each type * of command: msg, dcc, etc). */ typedef struct timer_str { struct timer_str *next; unsigned int mins; /* Time remaining */ unsigned int count; /* Number of repeats */ unsigned int interval; /* Time to elapse */ char *cmd; /* Command linked to */ unsigned long id; /* Used to remove timers */ char *name; /* User-specified name for timer */ } tcl_timer_t; /* Used for Tcl stub functions */ #define STDVAR (ClientData cd, Tcl_Interp *irp, int argc, char *argv[]) #define STDOBJVAR (ClientData cd, Tcl_Interp *irp, int objc, Tcl_Obj *const objv[]) #define BADARGS(nl, nh, example) do { \ if ((argc < (nl)) || ((argc > (nh)) && ((nh) != -1))) { \ Tcl_AppendResult(irp, "wrong # args: should be \"", \ argv[0], (example), "\"", NULL); \ return TCL_ERROR; \ } \ } while (0) #define BADOBJARGS(nl, nh, prefix, example) do { \ if ((objc < (nl)) || ((objc > (nh)) && ((nh) != -1))) { \ Tcl_WrongNumArgs(irp, prefix, objv, example); \ return TCL_ERROR; \ } \ } while (0) #define CHECKVALIDITY(a) do { \ if (!check_validity(argv[0], (a))) { \ Tcl_AppendResult(irp, "bad builtin command call!", \ NULL); \ return TCL_ERROR; \ } \ } while (0) char * add_timer(tcl_timer_t **, int, int, char *, char *, unsigned long); int find_timer(tcl_timer_t *, char *); int remove_timer(tcl_timer_t **, char *); void list_timers(Tcl_Interp *, tcl_timer_t *); void wipe_timers(Tcl_Interp *, tcl_timer_t **); void do_check_timers(tcl_timer_t **); typedef struct _tcl_strings { char *name; char *buf; int length; int flags; } tcl_strings; typedef struct _tcl_int { char *name; int *val; int readonly; } tcl_ints; typedef struct _tcl_coups { char *name; int *lptr; int *rptr; } tcl_coups; typedef struct _tcl_cmds { char *name; IntFunc func; } tcl_cmds; typedef struct _cd_tcl_cmd { char *name; IntFunc callback; void *cdata; } cd_tcl_cmd; void add_tcl_commands(tcl_cmds *); void add_tcl_objcommands(tcl_cmds *); void add_cd_tcl_cmds(cd_tcl_cmd *); void rem_tcl_commands(tcl_cmds *); void add_tcl_strings(tcl_strings *); void rem_tcl_strings(tcl_strings *); void add_tcl_coups(tcl_coups *); void rem_tcl_coups(tcl_coups *); void add_tcl_ints(tcl_ints *); void rem_tcl_ints(tcl_ints *); const char *tcl_resultstring(void); int tcl_resultint(void); int tcl_resultempty(void); int tcl_threaded(void); int fork_before_tcl(void); time_t get_expire_time(Tcl_Interp *, const char *); /* From Tcl's tclUnixInit.c */ /* The following table is used to map from Unix locale strings to * encoding files. */ typedef struct LocaleTable { const char *lang; const char *encoding; } LocaleTable; static const LocaleTable localeTable[] = { {"ja_JP.SJIS", "shiftjis"}, {"ja_JP.EUC", "euc-jp"}, {"ja_JP.JIS", "iso2022-jp"}, {"ja_JP.mscode", "shiftjis"}, {"ja_JP.ujis", "euc-jp"}, {"ja_JP", "euc-jp"}, {"Ja_JP", "shiftjis"}, {"Jp_JP", "shiftjis"}, {"japan", "euc-jp"}, #ifdef hpux {"japanese", "shiftjis"}, {"ja", "shiftjis"}, #else {"japanese", "euc-jp"}, {"ja", "euc-jp"}, #endif {"japanese.sjis", "shiftjis"}, {"japanese.euc", "euc-jp"}, {"japanese-sjis", "shiftjis"}, {"japanese-ujis", "euc-jp"}, {"ko", "euc-kr"}, {"ko_KR", "euc-kr"}, {"ko_KR.EUC", "euc-kr"}, {"ko_KR.euc", "euc-kr"}, {"ko_KR.eucKR", "euc-kr"}, {"korean", "euc-kr"}, {"ru", "iso8859-5"}, {"ru_RU", "iso8859-5"}, {"ru_SU", "iso8859-5"}, {"zh", "cp936"}, {NULL, NULL} }; #endif /* _EGG_TCLEGG_H */ eggheads-eggdrop-9c00fe1/src/tclhash.c000066400000000000000000001131351524730042700177100ustar00rootroot00000000000000/* * tclhash.c -- handles: * bind and unbind * checking and triggering the various in-bot bindings * listing current bindings * adding/removing new binding tables * (non-Tcl) procedure lookups for msg/dcc/file commands * (Tcl) binding internal procedures to msg/dcc/file commands */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" extern Tcl_Interp *interp; extern struct dcc_t *dcc; extern struct userrec *userlist; extern int dcc_total; extern time_t now; p_tcl_bind_list bind_table_list; p_tcl_bind_list H_chat, H_act, H_bcst, H_chon, H_chof, H_load, H_unld, H_link, H_disc, H_dcc, H_chjn, H_chpt, H_bot, H_time, H_nkch, H_away, H_note, H_filt, H_event, H_die, H_cron, H_log = NULL; #ifdef TLS p_tcl_bind_list H_tls = NULL; static int builtin_idx STDVAR; #endif static int builtin_2char STDVAR; static int builtin_3char STDVAR; static int builtin_5int STDVAR; static int builtin_cron STDVAR; static int builtin_char STDVAR; static int builtin_chpt STDVAR; static int builtin_chjn STDVAR; static int builtin_evnt STDVAR; static int builtin_idxchar STDVAR; static int builtin_charidx STDVAR; static int builtin_chat STDVAR; static int builtin_dcc STDVAR; static int builtin_log STDVAR; #ifdef DEBUG_CONTEXT char last_bind_called[512] = ""; #endif /* Allocate and initialise a chunk of memory. */ static void *n_malloc_null(int size, const char *file, int line) { #ifdef DEBUG_MEM # define nmalloc_null(size) n_malloc_null(size, __FILE__, __LINE__) void *ptr = n_malloc(size, file, line); #else # define nmalloc_null(size) n_malloc_null(size, NULL, 0) void *ptr = nmalloc(size); #endif egg_bzero(ptr, size); return ptr; } /* Delete trigger/command. */ static void tcl_cmd_delete(tcl_cmd_t *tc) { nfree(tc->func_name); nfree(tc); } /* Delete bind and its elements. */ static void tcl_bind_mask_delete(tcl_bind_mask_t *tm) { tcl_cmd_t *tc, *tc_next; for (tc = tm->first; tc; tc = tc_next) { tc_next = tc->next; tcl_cmd_delete(tc); } nfree(tm->mask); nfree(tm); } /* Delete bind list and its elements. */ static void tcl_bind_list_delete(tcl_bind_list_t *tl) { tcl_bind_mask_t *tm, *tm_next; for (tm = tl->first; tm; tm = tm_next) { tm_next = tm->next; tcl_bind_mask_delete(tm); } nfree(tl); } void garbage_collect_tclhash(void) { tcl_bind_list_t *tl, *tl_next, *tl_prev; tcl_bind_mask_t *tm, *tm_next, *tm_prev; tcl_cmd_t *tc, *tc_next, *tc_prev; for (tl = bind_table_list, tl_prev = NULL; tl; tl = tl_next) { tl_next = tl->next; if (tl->flags & HT_DELETED) { if (tl_prev) tl_prev->next = tl->next; else bind_table_list = tl->next; tcl_bind_list_delete(tl); } else { for (tm = tl->first, tm_prev = NULL; tm; tm = tm_next) { tm_next = tm->next; if (!(tm->flags & TBM_DELETED)) { for (tc = tm->first, tc_prev = NULL; tc; tc = tc_next) { tc_next = tc->next; if (tc->attributes & TC_DELETED) { if (tc_prev) tc_prev->next = tc->next; else tm->first = tc->next; tcl_cmd_delete(tc); } else tc_prev = tc; } } /* Delete the bind when it's marked as deleted or when it's empty. */ if ((tm->flags & TBM_DELETED) || tm->first == NULL) { if (tm_prev) tm_prev->next = tm->next; else tl->first = tm_next; tcl_bind_mask_delete(tm); } else tm_prev = tm; } tl_prev = tl; } } } static int tcl_cmd_expmem(tcl_cmd_t *tc) { int tot; tot = sizeof(*tc); if (tc->func_name) tot += strlen(tc->func_name) + 1; return tot; } static int tcl_bind_mask_expmem(tcl_bind_mask_t *tm) { int tot = 0; tcl_cmd_t *tc; for (tc = tm->first; tc; tc = tc->next) tot += tcl_cmd_expmem(tc); if (tm->mask) tot += strlen(tm->mask) + 1; tot += sizeof(*tm); return tot; } static int tcl_bind_list_expmem(tcl_bind_list_t *tl) { int tot = 0; tcl_bind_mask_t *tm; for (tm = tl->first; tm; tm = tm->next) tot += tcl_bind_mask_expmem(tm); tot += sizeof(*tl); return tot; } int expmem_tclhash(void) { int tot = 0; tcl_bind_list_t *tl; for (tl = bind_table_list; tl; tl = tl->next) tot += tcl_bind_list_expmem(tl); return tot; } extern cmd_t C_dcc[]; static int tcl_bind STDVAR; static cd_tcl_cmd cd_cmd_table[] = { {"bind", tcl_bind, (void *) 0}, {"unbind", tcl_bind, (void *) 1}, {0} }; void init_bind(void) { bind_table_list = NULL; add_cd_tcl_cmds(cd_cmd_table); H_unld = add_bind_table("unld", HT_STACKABLE, builtin_char); H_time = add_bind_table("time", HT_STACKABLE, builtin_5int); H_cron = add_bind_table("cron", HT_STACKABLE, builtin_cron); H_note = add_bind_table("note", 0, builtin_3char); H_nkch = add_bind_table("nkch", HT_STACKABLE, builtin_2char); H_load = add_bind_table("load", HT_STACKABLE, builtin_char); H_link = add_bind_table("link", HT_STACKABLE, builtin_2char); H_filt = add_bind_table("filt", HT_STACKABLE, builtin_idxchar); H_disc = add_bind_table("disc", HT_STACKABLE, builtin_char); H_dcc = add_bind_table("dcc", 0, builtin_dcc); H_chpt = add_bind_table("chpt", HT_STACKABLE, builtin_chpt); H_chon = add_bind_table("chon", HT_STACKABLE, builtin_charidx); H_chof = add_bind_table("chof", HT_STACKABLE, builtin_charidx); H_chjn = add_bind_table("chjn", HT_STACKABLE, builtin_chjn); H_chat = add_bind_table("chat", HT_STACKABLE, builtin_chat); H_bot = add_bind_table("bot", 0, builtin_3char); H_bcst = add_bind_table("bcst", HT_STACKABLE, builtin_chat); H_away = add_bind_table("away", HT_STACKABLE, builtin_chat); H_act = add_bind_table("act", HT_STACKABLE, builtin_chat); H_event = add_bind_table("evnt", HT_STACKABLE, builtin_evnt); H_die = add_bind_table("die", HT_STACKABLE, builtin_char); H_log = add_bind_table("log", HT_STACKABLE, builtin_log); #ifdef TLS H_tls = add_bind_table("tls", HT_STACKABLE, builtin_idx); #endif add_builtins(H_dcc, C_dcc); } void kill_bind(void) { tcl_bind_list_t *tl, *tl_next; rem_builtins(H_dcc, C_dcc); for (tl = bind_table_list; tl; tl = tl_next) { tl_next = tl->next; if (!(tl->flags |= HT_DELETED)) putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name); tcl_bind_list_delete(tl); } H_log = NULL; bind_table_list = NULL; } tcl_bind_list_t *add_bind_table(const char *nme, int flg, IntFunc func) { tcl_bind_list_t *tl, *tl_prev; int v; /* Do not allow coders to use bind table names longer than * 15 characters. */ Assert(strlen(nme) <= 15); for (tl = bind_table_list, tl_prev = NULL; tl; tl_prev = tl, tl = tl->next) { if (tl->flags & HT_DELETED) continue; v = strcasecmp(tl->name, nme); if (!v) return tl; /* Duplicate, just return old value. */ if (v > 0) break; /* New. Insert at start of list. */ } tl = nmalloc_null(sizeof *tl); strcpy(tl->name, nme); tl->flags = flg; tl->func = func; if (tl_prev) { tl->next = tl_prev->next; tl_prev->next = tl; } else { tl->next = bind_table_list; bind_table_list = tl; } putlog(LOG_DEBUG, "*", "Allocated bind table %s (flags %d)", nme, flg); return tl; } void del_bind_table(tcl_bind_list_t *tl_which) { tcl_bind_list_t *tl; for (tl = bind_table_list; tl; tl = tl->next) { if (tl->flags & HT_DELETED) continue; if (tl == tl_which) { tl->flags |= HT_DELETED; putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name); return; } } putlog(LOG_DEBUG, "*", "??? Tried to delete not listed bind table ???"); } tcl_bind_list_t *find_bind_table(const char *nme) { tcl_bind_list_t *tl; int v; for (tl = bind_table_list; tl; tl = tl->next) { if (tl->flags & HT_DELETED) continue; v = strcasecmp(tl->name, nme); if (!v) return tl; if (v > 0) return NULL; } return NULL; } static void dump_bind_tables(Tcl_Interp *irp) { tcl_bind_list_t *tl; uint8_t i; for (tl = bind_table_list, i = 0; tl; tl = tl->next) { if (tl->flags & HT_DELETED) continue; if (i) Tcl_AppendResult(irp, ", ", NULL); else i = 1; Tcl_AppendResult(irp, tl->name, NULL); } } int unbind_bind_entry(tcl_bind_list_t *tl, const char *flags, const char *cmd, const char *proc) { tcl_bind_mask_t *tm; /* Search for matching bind in bind list. */ for (tm = tl->first; tm; tm = tm->next) { if (tm->flags & TBM_DELETED) continue; if (!strcmp(cmd, tm->mask)) break; /* Found it! fall out! */ } if (tm) { tcl_cmd_t *tc; /* Search for matching proc in bind. */ for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; if (!strcasecmp(tc->func_name, proc)) { /* Erase proc regardless of flags. */ tc->attributes |= TC_DELETED; return 1; /* Match. */ } } } return 0; /* No match. */ } /* Add command (remove old one if necessary) */ int bind_bind_entry(tcl_bind_list_t *tl, const char *flags, const char *cmd, const char *proc) { tcl_cmd_t *tc; tcl_bind_mask_t *tm; /* Search for matching bind in bind list. */ for (tm = tl->first; tm; tm = tm->next) { if (tm->flags & TBM_DELETED) continue; if (!strcmp(cmd, tm->mask)) break; /* Found it! fall out! */ } /* Create bind if it doesn't exist yet. */ if (!tm) { tm = nmalloc_null(sizeof *tm); tm->mask = nmalloc(strlen(cmd) + 1); strcpy(tm->mask, cmd); /* Link into linked list of binds. */ tm->next = tl->first; tl->first = tm; } /* Proc already defined? If so, replace. */ for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; if (!strcmp(tc->func_name, proc)) { tc->flags.match = FR_GLOBAL | FR_CHAN; break_down_flags(flags, &(tc->flags), NULL); return 1; } } /* If this bind list is not stackable, remove the * old entry from this bind. */ if (!(tl->flags & HT_STACKABLE)) { for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; /* NOTE: We assume there's only one not-yet-deleted entry. */ tc->attributes |= TC_DELETED; break; } } tc = nmalloc_null(sizeof *tc); tc->flags.match = FR_GLOBAL | FR_CHAN; break_down_flags(flags, &(tc->flags), NULL); tc->func_name = nmalloc(strlen(proc) + 1); strcpy(tc->func_name, proc); /* Link into linked list of the bind's command list. */ tc->next = tm->first; tm->first = tc; return 1; } static int tcl_getbinds(tcl_bind_list_t *tl_kind, const char *name) { tcl_bind_mask_t *tm; for (tm = tl_kind->first; tm; tm = tm->next) { if (tm->flags & TBM_DELETED) continue; if (!strcasecmp(tm->mask, name)) { tcl_cmd_t *tc; for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; Tcl_AppendElement(interp, tc->func_name); } break; } } return TCL_OK; } static int tcl_bind STDVAR { tcl_bind_list_t *tl; /* cd defines what tcl_bind is supposed do: 0 = bind, 1 = unbind. */ if ((long int) cd == 1) BADARGS(5, 5, " type flags cmd/mask procname"); else BADARGS(4, 5, " type flags cmd/mask ?procname?"); tl = find_bind_table(argv[1]); if (!tl) { Tcl_AppendResult(irp, "bad type, should be one of: ", NULL); dump_bind_tables(irp); return TCL_ERROR; } if ((long int) cd == 1) { if (!unbind_bind_entry(tl, argv[2], argv[3], argv[4])) { /* Don't error if trying to re-unbind a builtin */ if (argv[4][0] != '*' || argv[4][4] != ':' || strcmp(argv[3], &argv[4][5]) || strncmp(argv[1], &argv[4][1], 3)) { Tcl_AppendResult(irp, "no such binding", NULL); return TCL_ERROR; } } } else { if (argc == 4) return tcl_getbinds(tl, argv[3]); bind_bind_entry(tl, argv[2], argv[3], argv[4]); } Tcl_AppendResult(irp, argv[3], NULL); return TCL_OK; } int check_validity(char *nme, IntFunc func) { char *p; tcl_bind_list_t *tl; if (*nme != '*') return 0; p = strchr(nme + 1, ':'); if (p == NULL) return 0; *p = 0; tl = find_bind_table(nme + 1); *p = ':'; if (!tl) return 0; if (tl->func != func) return 0; return 1; } static int builtin_3char STDVAR { Function F = (Function) cd; BADARGS(4, 4, " from to args"); CHECKVALIDITY(builtin_3char); F(argv[1], argv[2], argv[3]); return TCL_OK; } static int builtin_2char STDVAR { Function F = (Function) cd; BADARGS(3, 3, " nick msg"); CHECKVALIDITY(builtin_2char); F(argv[1], argv[2]); return TCL_OK; } static int builtin_5int STDVAR { Function F = (Function) cd; BADARGS(6, 6, " min hrs dom mon year"); CHECKVALIDITY(builtin_5int); F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5])); return TCL_OK; } static int builtin_cron STDVAR { Function F = (Function) cd; BADARGS(6, 6, " min hrs dom mon weekday"); CHECKVALIDITY(builtin_cron); F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5])); return TCL_OK; } static int builtin_char STDVAR { Function F = (Function) cd; BADARGS(2, 2, " handle"); CHECKVALIDITY(builtin_char); F(argv[1]); return TCL_OK; } static int builtin_chpt STDVAR { Function F = (Function) cd; BADARGS(3, 3, " bot nick sock"); CHECKVALIDITY(builtin_chpt); F(argv[1], argv[2], atoi(argv[3])); return TCL_OK; } static int builtin_chjn STDVAR { Function F = (Function) cd; BADARGS(6, 6, " bot nick chan# flag&sock host"); CHECKVALIDITY(builtin_chjn); F(argv[1], argv[2], atoi(argv[3]), argv[4][0], argv[4][0] ? atoi(argv[4] + 1) : 0, argv[5]); return TCL_OK; } static int builtin_evnt STDVAR { Function F = (Function) cd; BADARGS(2, 3, " event ?arg?"); CHECKVALIDITY(builtin_evnt); if (argc==2) { F(argv[1]); } else { F(argv[1], argv[2]); } return TCL_OK; } static int builtin_idxchar STDVAR { Function F = (Function) cd; int idx; char *r; BADARGS(3, 3, " idx args"); CHECKVALIDITY(builtin_idxchar); idx = findidx(atoi(argv[1])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } r = (((char *(*)()) F) (idx, argv[2])); Tcl_ResetResult(irp); Tcl_AppendResult(irp, r, NULL); return TCL_OK; } static int builtin_charidx STDVAR { Function F = (Function) cd; int idx; BADARGS(3, 3, " handle idx"); CHECKVALIDITY(builtin_charidx); idx = findanyidx(atoi(argv[2])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } Tcl_AppendResult(irp, int_to_base10(F(argv[1], idx)), NULL); return TCL_OK; } static int builtin_chat STDVAR { Function F = (Function) cd; int ch; BADARGS(4, 4, " handle idx text"); CHECKVALIDITY(builtin_chat); ch = atoi(argv[2]); F(argv[1], ch, argv[3]); return TCL_OK; } static int builtin_dcc STDVAR { int idx; Function F = (Function) cd; BADARGS(4, 4, " hand idx param"); CHECKVALIDITY(builtin_dcc); idx = findidx(atoi(argv[2])); if (idx < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } /* FIXME: This is an ugly hack. It is not documented as a * 'feature' because it will eventually go away. */ if (F == CMD_LEAVE) { Tcl_AppendResult(irp, "break", NULL); return TCL_OK; } /* Check if it's a password change, if so, don't show the password. We * don't need pretty formats here, as it's only for debugging purposes. */ debug4("tcl: builtin dcc call: %s %s %s %s", argv[0], argv[1], argv[2], (!strcmp(argv[0] + 5, "newpass") || !strcmp(argv[0] + 5, "chpass")) ? "[something]" : argv[3]); F(dcc[idx].user, idx, argv[3]); Tcl_ResetResult(irp); Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } static int builtin_log STDVAR { Function F = (Function) cd; BADARGS(3, 3, " lvl chan msg"); CHECKVALIDITY(builtin_log); F(argv[1], argv[2], argv[3]); return TCL_OK; } #ifdef TLS static int builtin_idx STDVAR { Function F = (Function) cd; BADARGS(2, 2, " idx"); CHECKVALIDITY(builtin_idx); F(atoi(argv[1])); return TCL_OK; } #endif /* Trigger (execute) a Tcl proc */ static int trigger_bind(const char *proc, const char *param, char *mask) { int x; struct rusage ru1, ru2; int r = 0; /* Set the lastbind variable before evaluating the proc so that the name * of the command that triggered the bind will be available to the proc. * This feature is used by scripts such as userinfo.tcl */ Tcl_SetVar(interp, "lastbind", mask, TCL_GLOBAL_ONLY); if(proc && proc[0] != '*') { /* proc[0] != '*' excludes internal binds */ #ifdef DEBUG_CONTEXT strlcpy(last_bind_called, proc, sizeof last_bind_called); #endif debug1("triggering bind %s", proc); r = getrusage(RUSAGE_SELF, &ru1); } x = Tcl_VarEval(interp, proc, param, NULL); if (proc && proc[0] != '*' && !r && !getrusage(RUSAGE_SELF, &ru2)) debug3("triggered bind %s, user %.3fms sys %.3fms", proc, (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 + (double) (ru2.ru_utime.tv_sec - ru1.ru_utime.tv_sec ) * 1000, (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 + (double) (ru2.ru_stime.tv_sec - ru1.ru_stime.tv_sec ) * 1000); if (x == TCL_ERROR) { /* FIXME: we really should be able to log longer errors */ putlog(LOG_MISC, "*", "Tcl error [%s]: %.*s", proc, 400, tcl_resultstring()); Tcl_BackgroundError(interp); return BIND_EXECUTED; } /* FIXME: This is an ugly hack. It is not documented as a * 'feature' because it will eventually go away. */ if (!strcmp(tcl_resultstring(), "break")) return BIND_QUIT; return (tcl_resultint() > 0) ? BIND_EXEC_LOG : BIND_EXECUTED; } /* Find out whether this bind matches the mask or provides the * requested attributes, depending on the specified requirements. */ static int check_bind_match(const char *match, char *mask, int match_type) { switch (match_type & 0x07) { case MATCH_PARTIAL: return (!strncasecmp(match, mask, strlen(match))); break; case MATCH_EXACT: return (!strcasecmp(match, mask)); break; case MATCH_CASE: return (!strcmp(match, mask)); break; case MATCH_MASK: return (wild_match_per(mask, match)); break; case MATCH_MODE: return (wild_match_partial_case(mask, match)); break; case MATCH_CRON: return (cron_match(mask, match)); break; default: /* Do nothing */ break; } return 0; } /* Check if the provided flags suffice for this command/trigger. */ static int check_bind_flags(struct flag_record *flags, struct flag_record *atr, int match_type) { if (match_type & BIND_USE_ATTR) { if (match_type & BIND_HAS_BUILTINS) return (flagrec_ok(flags, atr)); else return (flagrec_eq(flags, atr)); } return 1; } /* Check for and process Tcl binds */ int check_tcl_bind(tcl_bind_list_t *tl, const char *match, struct flag_record *atr, const char *param, int match_type) { int x, result = 0, cnt = 0, finish = 0; char *proc = NULL, *mask = NULL; tcl_bind_mask_t *tm, *tm_last = NULL, *tm_p = NULL; tcl_cmd_t *tc, *htc = NULL; char *str, *varName, *brkt; for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) { if (tm->flags & TBM_DELETED) continue; /* This bind mask was deleted */ if (!check_bind_match(match, tm->mask, match_type)) continue; /* This bind does not match. */ for (tc = tm->first; tc; tc = tc->next) { /* Search for valid entry. */ if (!(tc->attributes & TC_DELETED)) { /* Check if the provided flags suffice for this command. */ if (check_bind_flags(&tc->flags, atr, match_type)) { cnt++; tm_p = tm_last; /* Not stackable */ if (!(match_type & BIND_STACKABLE)) { /* Remember information about this bind. */ proc = tc->func_name; mask = tm->mask; htc = tc; /* Either this is a non-partial match, which means we * only want to execute _one_ bind ... */ if ((match_type & 0x07) != MATCH_PARTIAL || /* ... or this happens to be an exact match. */ !strcasecmp(match, tm->mask)) { cnt = 1; finish = 1; } /* We found a match so break out of the inner loop. */ break; } /* * Stackable; could be multiple commands/triggers. * Note: This code assumes BIND_ALTER_ARGS, BIND_WANTRET, and * BIND_STACKRET will only be used for stackable binds. */ /* We will only return if BIND_ALTER_ARGS or BIND_WANTRET was * specified because we want to trigger all binds in a stack. */ tc->hits++; x = trigger_bind(tc->func_name, param, tm->mask); if (match_type & BIND_ALTER_ARGS) { if (tcl_resultempty()) goto finally; } else if ((match_type & BIND_STACKRET) && x == BIND_EXEC_LOG) { /* If we have multiple commands/triggers, and if any of the * commands return 1, we store the result so we can return it * after processing all stacked binds. */ if (!result) result = x; continue; } else if ((match_type & BIND_WANTRET) && x == BIND_EXEC_LOG) /* Return immediately if any commands return 1 */ goto finally; } } } } if (!cnt) { x = BIND_NOMATCH; goto finally; } /* Do this before updating the preferred entries information, * since we don't want to change the order of stacked binds */ if (result) { /* BIND_STACKRET */ x = result; goto finally; } if ((match_type & 0x07) == MATCH_MASK || (match_type & 0x07) == MATCH_CASE) { x = BIND_EXECUTED; goto finally; } /* Hit counter */ if (htc) htc->hits++; if (cnt > 1) { x = BIND_AMBIGUOUS; goto finally; } /* Now that we have found exactly one bind, we can update the * preferred entries information. * Do this only for cnt == 1, * since we don't want to change the order of raw binds vs. builtin binds. * reason 1: order should be raw then builtin * reason 2: builtin could modify args */ if (tm_p && tm_p->next) { tm = tm_p->next; /* Move mask to front of bind's mask list. */ tm_p->next = tm->next; /* Unlink mask from list. */ tm->next = tl->first; /* Re-add mask to front of list. */ tl->first = tm; } x = trigger_bind(proc, param, mask); finally: str = nmalloc(strlen(param) + 1); strcpy(str, param); for (varName = strtok_r(str, " $:", &brkt); varName; varName = strtok_r(NULL, " $:", &brkt)) { Tcl_UnsetVar(interp, varName, 0); } nfree(str); return x; } /* Check for tcl-bound dcc command, return 1 if found * dcc: proc-name */ int check_tcl_dcc(const char *cmd, int idx, const char *args) { struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; int x; char s[11]; get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan); egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock); Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0); Tcl_SetVar(interp, "_dcc2", (char *) s, 0); Tcl_SetVar(interp, "_dcc3", (char *) args, 0); x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3", MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS); if (x == BIND_AMBIGUOUS) { dprintf(idx, "%s", MISC_AMBIGUOUS); return 0; } if (x == BIND_NOMATCH) { dprintf(idx, "%s", MISC_NOSUCHCMD); return 0; } /* We return 1 to leave the partyline */ if (x == BIND_QUIT) /* CMD_LEAVE, 'quit' */ return 1; if (x == BIND_EXEC_LOG) putlog(LOG_CMDS, "*", "#%s# %s %s", dcc[idx].nick, cmd, args); return 0; } void check_tcl_bot(const char *nick, const char *code, const char *param) { Tcl_SetVar(interp, "_bot1", (char *) nick, 0); Tcl_SetVar(interp, "_bot2", (char *) code, 0); Tcl_SetVar(interp, "_bot3", (char *) param, 0); check_tcl_bind(H_bot, code, 0, " $_bot1 $_bot2 $_bot3", MATCH_EXACT); } void check_tcl_chonof(char *hand, int sock, tcl_bind_list_t *tl) { struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; char s[11]; struct userrec *u; u = get_user_by_handle(userlist, hand); touch_laston(u, "partyline", now); get_user_flagrec(u, &fr, NULL); Tcl_SetVar(interp, "_chonof1", (char *) hand, 0); egg_snprintf(s, sizeof s, "%d", sock); Tcl_SetVar(interp, "_chonof2", (char *) s, 0); check_tcl_bind(tl, hand, &fr, " $_chonof1 $_chonof2", MATCH_MASK | BIND_USE_ATTR | BIND_STACKABLE | BIND_WANTRET); } void check_tcl_chatactbcst(const char *from, int chan, const char *text, tcl_bind_list_t *tl) { char s[11]; egg_snprintf(s, sizeof s, "%d", chan); Tcl_SetVar(interp, "_cab1", (char *) from, 0); Tcl_SetVar(interp, "_cab2", (char *) s, 0); Tcl_SetVar(interp, "_cab3", (char *) text, 0); check_tcl_bind(tl, text, 0, " $_cab1 $_cab2 $_cab3", MATCH_MASK | BIND_STACKABLE); } void check_tcl_nkch(const char *ohand, const char *nhand) { Tcl_SetVar(interp, "_nkch1", (char *) ohand, 0); Tcl_SetVar(interp, "_nkch2", (char *) nhand, 0); check_tcl_bind(H_nkch, ohand, 0, " $_nkch1 $_nkch2", MATCH_MASK | BIND_STACKABLE); } void check_tcl_link(const char *bot, const char *via) { Tcl_SetVar(interp, "_link1", (char *) bot, 0); Tcl_SetVar(interp, "_link2", (char *) via, 0); check_tcl_bind(H_link, bot, 0, " $_link1 $_link2", MATCH_MASK | BIND_STACKABLE); } void check_tcl_disc(const char *bot) { Tcl_SetVar(interp, "_disc1", (char *) bot, 0); check_tcl_bind(H_disc, bot, 0, " $_disc1", MATCH_MASK | BIND_STACKABLE); } void check_tcl_loadunld(const char *mod, tcl_bind_list_t *tl) { Tcl_SetVar(interp, "_lu1", (char *) mod, 0); check_tcl_bind(tl, mod, 0, " $_lu1", MATCH_MASK | BIND_STACKABLE); } const char *check_tcl_filt(int idx, const char *text) { char s[11]; int x; struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock); get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan); Tcl_SetVar(interp, "_filt1", (char *) s, 0); Tcl_SetVar(interp, "_filt2", (char *) text, 0); x = check_tcl_bind(H_filt, text, &fr, " $_filt1 $_filt2", MATCH_MASK | BIND_USE_ATTR | BIND_STACKABLE | BIND_WANTRET | BIND_ALTER_ARGS); if (x == BIND_EXECUTED || x == BIND_EXEC_LOG) { if (tcl_resultempty()) return ""; else return tcl_resultstring(); } else return text; } int check_tcl_note(const char *from, const char *to, const char *text) { int x; Tcl_SetVar(interp, "_note1", (char *) from, 0); Tcl_SetVar(interp, "_note2", (char *) to, 0); Tcl_SetVar(interp, "_note3", (char *) text, 0); x = check_tcl_bind(H_note, to, 0, " $_note1 $_note2 $_note3", MATCH_MASK | BIND_STACKABLE | BIND_WANTRET); return (x == BIND_EXEC_LOG); } void check_tcl_listen(const char *cmd, int idx) { char s[11]; int x; egg_snprintf(s, sizeof s, "%d", idx); Tcl_SetVar(interp, "_n", (char *) s, 0); x = Tcl_VarEval(interp, cmd, " $_n", NULL); if (x == TCL_ERROR) putlog(LOG_MISC, "*", "error on listen: %s", tcl_resultstring()); } void check_tcl_chjn(const char *bot, const char *nick, int chan, const char type, int sock, const char *host) { struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; char s[11], t[2], u[11]; t[0] = type; t[1] = 0; switch (type) { case '*': fr.global = USER_OWNER; break; case '+': fr.global = USER_MASTER; break; case '@': fr.global = USER_OP; break; case '^': fr.global = USER_HALFOP; break; case '%': fr.global = USER_BOTMAST; } egg_snprintf(s, sizeof s, "%d", chan); egg_snprintf(u, sizeof u, "%d", sock); Tcl_SetVar(interp, "_chjn1", (char *) bot, 0); Tcl_SetVar(interp, "_chjn2", (char *) nick, 0); Tcl_SetVar(interp, "_chjn3", (char *) s, 0); Tcl_SetVar(interp, "_chjn4", (char *) t, 0); Tcl_SetVar(interp, "_chjn5", (char *) u, 0); Tcl_SetVar(interp, "_chjn6", (char *) host, 0); check_tcl_bind(H_chjn, s, &fr, " $_chjn1 $_chjn2 $_chjn3 $_chjn4 $_chjn5 $_chjn6", MATCH_MASK | BIND_STACKABLE); } void check_tcl_chpt(const char *bot, const char *hand, int sock, int chan) { char u[11], v[11]; egg_snprintf(u, sizeof u, "%d", sock); egg_snprintf(v, sizeof v, "%d", chan); Tcl_SetVar(interp, "_chpt1", (char *) bot, 0); Tcl_SetVar(interp, "_chpt2", (char *) hand, 0); Tcl_SetVar(interp, "_chpt3", (char *) u, 0); Tcl_SetVar(interp, "_chpt4", (char *) v, 0); check_tcl_bind(H_chpt, v, 0, " $_chpt1 $_chpt2 $_chpt3 $_chpt4", MATCH_MASK | BIND_STACKABLE); } void check_tcl_away(const char *bot, int idx, const char *msg) { char u[11]; egg_snprintf(u, sizeof u, "%d", idx); Tcl_SetVar(interp, "_away1", (char *) bot, 0); Tcl_SetVar(interp, "_away2", (char *) u, 0); Tcl_SetVar(interp, "_away3", msg ? (char *) msg : "", 0); check_tcl_bind(H_away, bot, 0, " $_away1 $_away2 $_away3", MATCH_MASK | BIND_STACKABLE); } void check_tcl_time_and_cron(struct tm *tm) { /* Undersized due to sane assumption that struct tm is sane and at the same * time oversized to silence a gcc format-truncation warning */ char y[24]; egg_snprintf(y, sizeof y, "%02d", tm->tm_min); Tcl_SetVar(interp, "_time1", (char *) y, 0); Tcl_SetVar(interp, "_cron1", (char *) y, 0); egg_snprintf(y, sizeof y, "%02d", tm->tm_hour); Tcl_SetVar(interp, "_time2", (char *) y, 0); Tcl_SetVar(interp, "_cron2", (char *) y, 0); egg_snprintf(y, sizeof y, "%02d", tm->tm_mday); Tcl_SetVar(interp, "_time3", (char *) y, 0); Tcl_SetVar(interp, "_cron3", (char *) y, 0); egg_snprintf(y, sizeof y, "%02d", tm->tm_mon); Tcl_SetVar(interp, "_time4", (char *) y, 0); egg_snprintf(y, sizeof y, "%04d", tm->tm_year + 1900); Tcl_SetVar(interp, "_time5", (char *) y, 0); egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %04d", tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year + 1900); check_tcl_bind(H_time, y, 0, " $_time1 $_time2 $_time3 $_time4 $_time5", MATCH_MASK | BIND_STACKABLE); egg_snprintf(y, sizeof y, "%02d", tm->tm_mon + 1); Tcl_SetVar(interp, "_cron4", (char *) y, 0); egg_snprintf(y, sizeof y, "%02d", tm->tm_wday); Tcl_SetVar(interp, "_cron5", (char *) y, 0); egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %02d", tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon + 1, tm->tm_wday); check_tcl_bind(H_cron, y, 0, " $_cron1 $_cron2 $_cron3 $_cron4 $_cron5", MATCH_CRON | BIND_STACKABLE); } void check_tcl_event(const char *event) { Tcl_SetVar(interp, "_event1", (char *) event, TCL_GLOBAL_ONLY); check_tcl_bind(H_event, event, 0, " $::_event1", MATCH_EXACT | BIND_STACKABLE); } void check_tcl_event_arg(const char *event, const char *arg) { Tcl_SetVar(interp, "_event1", (char *) event, TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "_event2", (char *) arg, TCL_GLOBAL_ONLY); check_tcl_bind(H_event, event, 0, " $::_event1 $::_event2", MATCH_EXACT | BIND_STACKABLE); } int check_tcl_signal(const char *event) { int x; Tcl_SetVar(interp, "_event1", (char *) event, TCL_GLOBAL_ONLY); x = check_tcl_bind(H_event, event, 0, " $::_event1", MATCH_EXACT | BIND_STACKABLE | BIND_STACKRET); return (x == BIND_EXEC_LOG); } void check_tcl_die(char *reason) { Tcl_SetVar(interp, "_die1", reason, 0); check_tcl_bind(H_die, reason, 0, " $_die1", MATCH_MASK | BIND_STACKABLE); } void check_tcl_log(int lv, char *chan, char *msg) { char mask[512]; Tcl_Obj* prev_result; /* We have to store the old result, as check_tcl_bind may override it */ prev_result = Tcl_GetObjResult(interp); Tcl_IncrRefCount(prev_result); egg_snprintf(mask, sizeof mask, "%s %s", chan, msg); Tcl_SetVar(interp, "_log1", masktype(lv), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "_log2", chan, TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "_log3", msg, TCL_GLOBAL_ONLY); check_tcl_bind(H_log, mask, 0, " $::_log1 $::_log2 $::_log3", MATCH_MASK | BIND_STACKABLE); Tcl_SetObjResult(interp, prev_result); Tcl_DecrRefCount(prev_result); } #ifdef TLS int check_tcl_tls(int sock) { int x; char s[11]; egg_snprintf(s, sizeof s, "%d", sock); Tcl_SetVar(interp, "_tls", s, 0); x = check_tcl_bind(H_tls, s, 0, " $_tls", MATCH_MASK | BIND_STACKABLE | BIND_WANTRET); return (x == BIND_EXEC_LOG); } #endif void tell_binds(int idx, char *par) { tcl_bind_list_t *tl, *tl_kind; tcl_bind_mask_t *tm; int fnd = 0, showall = 0, patmatc = 0, maxname = 0; int ok = 0, showpy = 0, showtcl = 0; tcl_cmd_t *tc; char *name, *proc, *s, flg[100]; if (par[0]) name = newsplit(&par); else name = NULL; if (par[0]) s = newsplit(&par); else s = NULL; if (name) tl_kind = find_bind_table(name); else tl_kind = NULL; if ((name && name[0] && !strcasecmp(name, "all")) || (s && s[0] && !strcasecmp(s, "all"))) showall = 1; if ((name && name[0] && !strcasecmp(name, "tcl")) || (s && s[0] && !strcasecmp(s, "all"))) showtcl = 1; if ((name && name[0] && !strcasecmp(name, "python")) || (s && s[0] && !strcasecmp(s, "all"))) showpy = 1; if (tl_kind == NULL && !showpy && !showtcl && name && name[0] && strcasecmp(name, "all")) patmatc = 1; for (tl = tl_kind ? tl_kind : bind_table_list; tl; tl = tl_kind ? 0 : tl->next) { if (tl->flags & HT_DELETED) continue; for (tm = tl->first; tm; tm = tm->next) { if (tm->flags & TBM_DELETED) continue; for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; if (strlen(tl->name) > maxname) { maxname = strlen(tl->name); } } } } dprintf(idx, "%s", MISC_CMDBINDS); dprintf(idx, " %*s FLAGS COMMAND HITS BINDING (TCL)\n", maxname, "TYPE"); for (tl = tl_kind ? tl_kind : bind_table_list; tl; tl = tl_kind ? 0 : tl->next) { if (tl->flags & HT_DELETED) continue; for (tm = tl->first; tm; tm = tm->next) { if (tm->flags & TBM_DELETED) continue; for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; proc = tc->func_name; build_flags(flg, &(tc->flags), NULL); ok = 0; if (showall) { ok = 1; } else if (patmatc || showpy || showtcl) { if ((patmatc == 1) && (proc[0] != '*')) { if (wild_match_per(name, tl->name) || wild_match_per(name, tm->mask) || wild_match_per(name, tc->func_name)) { ok = 1; } } else if (showpy && !(strncasecmp(tc->func_name, "*python:", strlen("*python:")))) { ok = 1; } else if (showtcl && (strncasecmp(tc->func_name, "*", strlen("*")))) { ok = 1; } } else if (proc[0] != '*') { ok = 1; } if (ok) { dprintf(idx, " %*s %-8s %-20s %4d %s\n", maxname, tl->name, flg, tm->mask, tc->hits, tc->func_name); fnd = 1; } } } } if (!fnd) { if (patmatc) dprintf(idx, "No command bindings found that match %s\n", name); else if (tl_kind) dprintf(idx, "No command bindings for type: %s.\n", name); else dprintf(idx, "No command bindings exist.\n"); } } /* Bring the default msg/dcc/fil commands into the Tcl interpreter */ void add_builtins(tcl_bind_list_t *tl, cmd_t *cc) { int k, i; char p[1024], *l; cd_tcl_cmd table[2]; table[0].name = p; table[0].callback = tl->func; table[1].name = NULL; for (i = 0; cc[i].name; i++) { egg_snprintf(p, sizeof p, "*%s:%s", tl->name, cc[i].funcname ? cc[i].funcname : cc[i].name); l = nmalloc(Tcl_ScanElement(p, &k) + 1); Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES); table[0].cdata = (void *) cc[i].func; add_cd_tcl_cmds(table); bind_bind_entry(tl, cc[i].flags, cc[i].name, l); nfree(l); } } /* Remove the default msg/dcc/fil commands from the Tcl interpreter */ void rem_builtins(tcl_bind_list_t *table, cmd_t *cc) { int k, i; char p[1024], *l; for (i = 0; cc[i].name; i++) { egg_snprintf(p, sizeof p, "*%s:%s", table->name, cc[i].funcname ? cc[i].funcname : cc[i].name); l = nmalloc(Tcl_ScanElement(p, &k) + 1); Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES); Tcl_DeleteCommand(interp, p); unbind_bind_entry(table, cc[i].flags, cc[i].name, l); nfree(l); } } eggheads-eggdrop-9c00fe1/src/tclhash.h000066400000000000000000000121341524730042700177120ustar00rootroot00000000000000/* * tclhash.h */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_TCLHASH_H #define _EGG_TCLHASH_H #define TC_DELETED 0x0001 /* This command/trigger was deleted. */ typedef struct tcl_cmd_b { struct tcl_cmd_b *next; struct flag_record flags; char *func_name; /* Proc name. */ uint8_t attributes; /* Flags for this entry. TC_* */ unsigned int hits; /* Number of times this proc was triggered. */ } tcl_cmd_t; struct threaddata { int (*mainloopfunc)(int); /* main loop function replacing a single * tcl event loop iteration */ sock_list *socklist; /* tcl socket list for threads, else NULL */ struct timeval blocktime; /* maximum time to block in select() */ int mainthread; /* Is this the main thread? */ int MAXSOCKS; }; #define TBM_DELETED 0x0001 /* This mask was deleted. */ typedef struct tcl_bind_mask_b { struct tcl_bind_mask_b *next; char *mask; uint8_t flags; /* Flags for this entry. TBM_* */ tcl_cmd_t *first; /* List of commands registered for this bind. */ } tcl_bind_mask_t; #define HT_STACKABLE 0x0001 /* Triggers in this bind list may be stacked. */ #define HT_DELETED 0x0002 /* This bind list was already deleted. Do not * use it anymore. */ typedef struct tcl_bind_list_b { struct tcl_bind_list_b *next; char name[16]; /* Name of the bind. */ uint8_t flags; /* Flags for this element. HT_* */ IntFunc func; /* Function used as the Tcl calling interface * for procs actually representing C functions. */ tcl_bind_mask_t *first; /* Pointer to registered binds for this list. */ } tcl_bind_list_t, *p_tcl_bind_list; #ifndef MAKING_MODS void garbage_collect_tclhash(void); void init_bind(void); void kill_bind(void); int expmem_tclhash(void); tcl_bind_list_t *add_bind_table(const char *nme, int flg, IntFunc func); void del_bind_table(tcl_bind_list_t *tl_which); tcl_bind_list_t *find_bind_table(const char *nme); int bind_bind_entry(tcl_bind_list_t *tl, const char *flags, const char *cmd, const char *proc); int unbind_bind_entry(tcl_bind_list_t *tl, const char *flags, const char *cmd, const char *proc); int check_tcl_bind(tcl_bind_list_t *, const char *, struct flag_record *, const char *, int); int check_tcl_dcc(const char *, int, const char *); void check_tcl_chjn(const char *, const char *, int, char, int, const char *); void check_tcl_chpt(const char *, const char *, int, int); void check_tcl_bot(const char *, const char *, const char *); void check_tcl_link(const char *, const char *); void check_tcl_disc(const char *); const char *check_tcl_filt(int, const char *); int check_tcl_note(const char *, const char *, const char *); void check_tcl_listen(const char *, int); void check_tcl_time_and_cron(struct tm *); void tell_binds(int, char *); void check_tcl_nkch(const char *, const char *); void check_tcl_away(const char *, int, const char *); void check_tcl_chatactbcst(const char *, int, const char *, tcl_bind_list_t *); void check_tcl_event(const char *); void check_tcl_event_arg(const char *, const char *); int check_tcl_signal(const char *); void check_tcl_die(char *); void check_tcl_log(int, char *, char *); #ifdef TLS int check_tcl_tls(int); #endif #define check_tcl_chat(a, b, c) check_tcl_chatactbcst(a ,b, c, H_chat) #define check_tcl_act(a, b, c) check_tcl_chatactbcst(a, b, c, H_act) #define check_tcl_bcst(a, b, c) check_tcl_chatactbcst(a, b, c, H_bcst) void check_tcl_chonof(char *, int, tcl_bind_list_t *); #define check_tcl_chon(a, b) check_tcl_chonof(a, b, H_chon) #define check_tcl_chof(a, b) check_tcl_chonof(a, b, H_chof) void check_tcl_loadunld(const char *, tcl_bind_list_t *); #define check_tcl_load(a) check_tcl_loadunld(a, H_load) #define check_tcl_unld(a) check_tcl_loadunld(a, H_unld) void rem_builtins(tcl_bind_list_t *, cmd_t *); void add_builtins(tcl_bind_list_t *, cmd_t *); int check_validity(char *, IntFunc); extern p_tcl_bind_list H_chat, H_act, H_bcst, H_chon, H_chof; extern p_tcl_bind_list H_load, H_unld, H_dcc, H_bot, H_link; extern p_tcl_bind_list H_away, H_nkch, H_filt, H_disc, H_event, H_log; #endif /* MAKING_MODS */ #endif /* _EGG_TCLHASH_H */ eggheads-eggdrop-9c00fe1/src/tclmisc.c000066400000000000000000000477721524730042700177350ustar00rootroot00000000000000/* * tclmisc.c -- handles: * Tcl stubs for everything else */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "main.h" #include "modules.h" #include "md5/md5.h" #include #include extern p_tcl_bind_list bind_table_list; extern tcl_timer_t *timer, *utimer; extern struct dcc_t *dcc; extern char botnetnick[], quit_msg[]; extern struct userrec *userlist; extern time_t now; extern module_entry *module_list; extern int max_logs, cache_hit, cache_miss; extern log_t *logs; extern Tcl_Interp *interp; int expmem_tclmisc() { int i, tot = 0; for (i = 0; i < max_logs; i++) { if (logs[i].filename != NULL) { tot += strlen(logs[i].filename) + 1; tot += strlen(logs[i].chname) + 1; } } return tot; } /* * Logging */ /* logfile [ ] */ static int tcl_logfile STDVAR { int i; char s[256]; BADARGS(1, 4, " ?logModes channel logFile?"); if (argc == 1) { /* They just want a list of the logfiles and modes */ for (i = 0; i < max_logs; i++) if (logs[i].filename != NULL) { snprintf(s, sizeof s, "%s %s %s", masktype(logs[i].mask), logs[i].chname, logs[i].filename); Tcl_AppendElement(interp, s); } return TCL_OK; } BADARGS(4, 4, " ?logModes channel logFile?"); if (*argv[1] && !*argv[2]) { Tcl_AppendResult(interp, "log modes set, but no channel specified", NULL); return TCL_ERROR; } if (*argv[2] && !strchr(CHANMETA, *argv[2]) && strcmp(argv[2], "*")) { Tcl_AppendResult(interp, "invalid channel prefix", NULL); return TCL_ERROR; } if (*argv[2] && strchr(argv[2], ' ')) { Tcl_AppendResult(interp, "channel names cannot contain spaces", NULL); return TCL_ERROR; } for (i = 0; i < max_logs; i++) if ((logs[i].filename != NULL) && (!strcmp(logs[i].filename, argv[3]))) { logs[i].flags &= ~LF_EXPIRING; logs[i].mask = logmodes(argv[1]); nfree(logs[i].chname); logs[i].chname = NULL; if (!logs[i].mask) { /* ending logfile */ nfree(logs[i].filename); logs[i].filename = NULL; if (logs[i].f != NULL) { fclose(logs[i].f); logs[i].f = NULL; } logs[i].flags = 0; if (logs[i].szlast != NULL) { nfree(logs[i].szlast); logs[i].szlast = NULL; logs[i].szlast_len = 0; } } else { logs[i].chname = nmalloc(strlen(argv[2]) + 1); strcpy(logs[i].chname, argv[2]); } Tcl_AppendResult(interp, argv[3], NULL); return TCL_OK; } /* Do not add logfiles without any flags to log ++rtc */ if (!logmodes(argv[1])) { Tcl_AppendResult(interp, "can't remove \"", argv[3], "\" from list: no such logfile", NULL); return TCL_ERROR; } for (i = 0; i < max_logs; i++) if (logs[i].filename == NULL) { logs[i].flags = 0; logs[i].mask = logmodes(argv[1]); logs[i].filename = nmalloc(strlen(argv[3]) + 1); strcpy(logs[i].filename, argv[3]); logs[i].chname = nmalloc(strlen(argv[2]) + 1); strcpy(logs[i].chname, argv[2]); Tcl_AppendResult(interp, argv[3], NULL); return TCL_OK; } Tcl_AppendResult(interp, "reached max # of logfiles", NULL); return TCL_ERROR; } static int tcl_putlog STDVAR { char logtext[501]; BADARGS(2, 2, " text"); strlcpy(logtext, argv[1], sizeof logtext); putlog(LOG_MISC, "*", "%s", logtext); return TCL_OK; } static int tcl_putcmdlog STDVAR { char logtext[501]; BADARGS(2, 2, " text"); strlcpy(logtext, argv[1], sizeof logtext); putlog(LOG_CMDS, "*", "%s", logtext); return TCL_OK; } static int tcl_putxferlog STDVAR { char logtext[501]; BADARGS(2, 2, " text"); strlcpy(logtext, argv[1], sizeof logtext); putlog(LOG_FILES, "*", "%s", logtext); return TCL_OK; } static int tcl_putloglev STDVAR { int lev = 0; char logtext[501]; BADARGS(4, 4, " flag(s) channel text"); lev = logmodes(argv[1]); if (!lev) { Tcl_AppendResult(irp, "No valid log flag given", NULL); return TCL_ERROR; } strlcpy(logtext, argv[3], sizeof logtext); putlog(lev, argv[2], "%s", logtext); return TCL_OK; } static int tcl_binds STDVAR { int matching = 0; char *g, flg[100], hits[11]; EGG_CONST char *list[5]; tcl_bind_list_t *tl, *tl_kind; tcl_bind_mask_t *tm; tcl_cmd_t *tc; BADARGS(1, 2, " ?type/mask?"); if (argv[1]) tl_kind = find_bind_table(argv[1]); else tl_kind = NULL; if (!tl_kind && argv[1]) matching = 1; for (tl = tl_kind ? tl_kind : bind_table_list; tl; tl = tl_kind ? 0 : tl->next) { if (tl->flags & HT_DELETED) continue; for (tm = tl->first; tm; tm = tm->next) { if (tm->flags & TBM_DELETED) continue; for (tc = tm->first; tc; tc = tc->next) { if (tc->attributes & TC_DELETED) continue; if (matching && !wild_match_per(argv[1], tl->name) && !wild_match_per(argv[1], tm->mask) && !wild_match_per(argv[1], tc->func_name)) continue; build_flags(flg, &(tc->flags), NULL); egg_snprintf(hits, sizeof hits, "%i", (int) tc->hits); list[0] = tl->name; list[1] = flg; list[2] = tm->mask; list[3] = hits; list[4] = tc->func_name; g = Tcl_Merge(5, list); Tcl_AppendElement(irp, g); Tcl_Free((char *) g); } } } return TCL_OK; } int check_timer_syntax(Tcl_Interp *irp, int argc, char *argv[], tcl_timer_t *stack) { char *endptr; long val; if (atoi(argv[1]) < 0) { Tcl_AppendResult(irp, "time value must be positive", NULL); return 1; } if (argc >=4) { val = strtol(argv[3], &endptr, 0); if ((*endptr != '\0') || (val < 0)) { Tcl_AppendResult(irp, "count value must be >=0", NULL); return 1; } } if (argv[2][0] != '#') { if (argc == 5) { /* Prevent collisions with unnamed timers */ if (strncmp(argv[4], "timer", 5) == 0) { Tcl_AppendResult(irp, "timer name may not begin with \"timer\"", NULL); return 1; } /* Check for existing timers by same name */ if (find_timer(stack, argv[4])) { Tcl_AppendResult(irp, "timer already exists by that name", NULL); return 1; } } } return 0; } static int tcl_timer STDVAR { char *x; BADARGS(3, 5, " minutes command ?count ?name??"); if (check_timer_syntax(irp, argc, argv, timer)) { return TCL_ERROR; } x = add_timer(&timer, atoi(argv[1]), (argc >= 4 ? atoi(argv[3]) : 1), argv[2], (argc == 5 ? argv[4] : NULL), 0L); if (!x) { Tcl_AppendResult(irp, "Too many timers (wow, impressive). Timer not added", NULL); return TCL_ERROR; } Tcl_AppendResult(irp, x, NULL); return TCL_OK; } static int tcl_utimer STDVAR { char *x; BADARGS(3, 5, " seconds command ?count ?name??"); if (check_timer_syntax(irp, argc, argv, utimer)) { return TCL_ERROR; } x = add_timer(&utimer, atoi(argv[1]), (argc >= 4 ? atoi(argv[3]) : 1), argv[2], (argc == 5 ? argv[4] : '\0'), 0L); if (!x) { Tcl_AppendResult(irp, "Too many timers (wow, impressive). Timer not added", NULL); return TCL_ERROR; } Tcl_AppendResult(irp, x, NULL); return TCL_OK; } static int tcl_killtimer STDVAR { BADARGS(2, 2, " timerID"); if (remove_timer(&timer, argv[1])) return TCL_OK; Tcl_AppendResult(irp, "invalid timer name", NULL); return TCL_ERROR; } static int tcl_killutimer STDVAR { BADARGS(2, 2, " timerName"); if (remove_timer(&utimer, argv[1])) { return TCL_OK; } Tcl_AppendResult(irp, "invalid utimer name", NULL); return TCL_ERROR; } static int tcl_timers STDVAR { BADARGS(1, 1, ""); list_timers(irp, timer); return TCL_OK; } static int tcl_utimers STDVAR { BADARGS(1, 1, ""); list_timers(irp, utimer); return TCL_OK; } static int tcl_duration STDVAR { char s[70]; unsigned long sec, tmp; BADARGS(2, 2, " seconds"); if (atol(argv[1]) <= 0) { Tcl_AppendResult(irp, "0 seconds", NULL); return TCL_OK; } sec = atol(argv[1]); s[0] = 0; if (sec >= 31536000) { tmp = (sec / 31536000); sprintf(s, "%lu year%s ", tmp, (tmp == 1) ? "" : "s"); sec -= (tmp * 31536000); } if (sec >= 604800) { tmp = (sec / 604800); sprintf(&s[strlen(s)], "%lu week%s ", tmp, (tmp == 1) ? "" : "s"); sec -= (tmp * 604800); } if (sec >= 86400) { tmp = (sec / 86400); sprintf(&s[strlen(s)], "%lu day%s ", tmp, (tmp == 1) ? "" : "s"); sec -= (tmp * 86400); } if (sec >= 3600) { tmp = (sec / 3600); sprintf(&s[strlen(s)], "%lu hour%s ", tmp, (tmp == 1) ? "" : "s"); sec -= (tmp * 3600); } if (sec >= 60) { tmp = (sec / 60); sprintf(&s[strlen(s)], "%lu minute%s ", tmp, (tmp == 1) ? "" : "s"); sec -= (tmp * 60); } if (sec > 0) { tmp = (sec); sprintf(&s[strlen(s)], "%lu second%s", tmp, (tmp == 1) ? "" : "s"); } if (strlen(s) > 0 && s[strlen(s) - 1] == ' ') s[strlen(s) - 1] = 0; Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_unixtime STDVAR { char s[21]; time_t now2 = time(NULL); BADARGS(1, 1, ""); snprintf(s, sizeof s, "%" PRId64, (int64_t) now2); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_ctime STDVAR { time_t tt; char s[26]; BADARGS(2, 2, " unixtime"); tt = (time_t) atol(argv[1]); ctime_r(&tt, s); s[24] = 0; Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_strftime STDVAR { char buf[512]; struct tm *tm1; time_t t; BADARGS(2, 3, " format ?time?"); if (argc == 3) t = atol(argv[2]); else t = now; tm1 = localtime(&t); if (!tm1) { Tcl_AppendResult(irp, "tcl_strftime(): localtime(): error = ", strerror(errno), NULL); return TCL_ERROR; } if (strftime(buf, sizeof(buf) - 1, argv[1], tm1)) { Tcl_AppendResult(irp, buf, NULL); return TCL_OK; } Tcl_AppendResult(irp, "tcl_strftime(): strftime(): error", NULL); return TCL_ERROR; } static int tcl_myip STDVAR { char s[EGG_INET_ADDRSTRLEN]; BADARGS(1, 1, ""); getdccaddr(NULL, s, sizeof s); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_rand STDVAR { long i; unsigned long x; char s[11]; BADARGS(2, 2, " limit"); i = atol(argv[1]); if (i <= 0) { Tcl_AppendResult(irp, "random limit must be greater than 0", NULL); return TCL_ERROR; } else if (i > RANDOM_MAX) { Tcl_AppendResult(irp, "random limit must be equal to or less than ", int_to_base10(RANDOM_MAX), NULL); return TCL_ERROR; } x = randint(i); egg_snprintf(s, sizeof s, "%lu", x); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_sendnote STDVAR { char s[5], from[NOTENAMELEN + 1], to[NOTENAMELEN + 1], msg[451]; BADARGS(4, 4, " from to message"); strlcpy(from, argv[1], sizeof from); strlcpy(to, argv[2], sizeof to); strlcpy(msg, argv[3], sizeof msg); egg_snprintf(s, sizeof s, "%d", add_note(to, from, msg, -1, 0)); Tcl_AppendResult(irp, s, NULL); return TCL_OK; } static int tcl_dumpfile STDVAR { char nick[NICKLEN]; struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; BADARGS(3, 3, " nickname filename"); strlcpy(nick, argv[1], sizeof nick); get_user_flagrec(get_user_by_nick(nick), &fr, NULL); showhelp(argv[1], argv[2], &fr, HELP_TEXT); return TCL_OK; } static int tcl_dccdumpfile STDVAR { int idx, i; struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 }; BADARGS(3, 3, " idx filename"); i = atoi(argv[1]); idx = findidx(i); if (idx < 0) { Tcl_AppendResult(irp, "illegal idx", NULL); return TCL_ERROR; } get_user_flagrec(get_user_by_handle(userlist, dcc[idx].nick), &fr, NULL); tellhelp(idx, argv[2], &fr, HELP_TEXT); return TCL_OK; } static int tcl_backup STDVAR { BADARGS(1, 1, ""); call_hook(HOOK_BACKUP); return TCL_OK; } static int tcl_die STDVAR { char s[1024]; BADARGS(1, 2, " ?reason?"); if (argc == 2) { egg_snprintf(s, sizeof s, "BOT SHUTDOWN (%s)", argv[1]); strlcpy(quit_msg, argv[1], 1024); } else { strlcpy(s, "BOT SHUTDOWN (No reason)", sizeof s); quit_msg[0] = 0; } kill_bot(s, quit_msg[0] ? quit_msg : "EXIT"); return TCL_OK; } static int tcl_loadmodule STDVAR { const char *p; BADARGS(2, 2, " module-name"); p = module_load(argv[1]); if (p && strcmp(p, MOD_ALREADYLOAD) && !strcmp(argv[0], "loadmodule")) putlog(LOG_MISC, "*", "%s %s: %s", MOD_CANTLOADMOD, argv[1], p); Tcl_AppendResult(irp, p, NULL); return TCL_OK; } static int tcl_unloadmodule STDVAR { BADARGS(2, 2, " module-name"); Tcl_AppendResult(irp, module_unload(argv[1], botnetnick), NULL); return TCL_OK; } static int tcl_unames STDVAR { Tcl_AppendResult(irp, egg_uname(), NULL); return TCL_OK; } static int tcl_modules STDVAR { int i; char *p, s[24], s2[24]; EGG_CONST char *list[100], *list2[2]; dependancy *dep; module_entry *current; BADARGS(1, 1, ""); for (current = module_list; current; current = current->next) { list[0] = current->name; egg_snprintf(s, sizeof s, "%d.%d", current->major, current->minor); list[1] = s; i = 2; for (dep = dependancy_list; dep && (i < 100); dep = dep->next) { if (dep->needing == current) { list2[0] = dep->needed->name; egg_snprintf(s2, sizeof s2, "%d.%d", dep->major, dep->minor); list2[1] = s2; list[i] = Tcl_Merge(2, list2); i++; } } p = Tcl_Merge(i, list); Tcl_AppendElement(irp, p); Tcl_Free((char *) p); while (i > 2) { i--; Tcl_Free((char *) list[i]); } } return TCL_OK; } static int tcl_loadhelp STDVAR { BADARGS(2, 2, " helpfile-name"); add_help_reference(argv[1]); return TCL_OK; } static int tcl_unloadhelp STDVAR { BADARGS(2, 2, " helpfile-name"); rem_help_reference(argv[1]); return TCL_OK; } static int tcl_reloadhelp STDVAR { BADARGS(1, 1, ""); reload_help_data(); return TCL_OK; } static int tcl_callevent STDVAR { BADARGS(2, 3, " event ?arg?"); if (argc == 2) { check_tcl_event(argv[1]); } else { check_tcl_event_arg(argv[1], argv[2]); } return TCL_OK; } static int tcl_stripcodes STDVAR { int flags = 0; char *p; BADARGS(3, 3, " strip-flags string"); for (p = argv[1]; *p; p++) switch (*p) { case 'c': flags |= STRIP_COLOR; break; case 'b': flags |= STRIP_BOLD; break; case 'r': flags |= STRIP_REVERSE; break; case 'u': flags |= STRIP_UNDERLINE; break; case 'a': flags |= STRIP_ANSI; break; case 'g': flags |= STRIP_BELLS; break; case 'o': flags |= STRIP_ORDINARY; break; case 'i': flags |= STRIP_ITALICS; break; case '*': flags |= STRIP_ALL; break; default: Tcl_AppendResult(irp, "Invalid strip-flags: ", argv[1], NULL); return TCL_ERROR; } p = Tcl_Alloc(strlen(argv[2]) + 1); strcpy(p, argv[2]); strip_mirc_codes(flags, p); Tcl_SetResult(irp, p, TCL_DYNAMIC); return TCL_OK; } static int tcl_md5 STDVAR { char digest_string[33]; unsigned char digest[16]; int i; BADARGS(2, 2, " string"); #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && defined(HAVE_EVP_MD5) EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); const EVP_MD *md = EVP_md5(); unsigned int md_len; EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, argv[1], strlen(argv[1])); EVP_DigestFinal_ex(mdctx, digest, &md_len); EVP_MD_CTX_free(mdctx); #else MD5_CTX md5context; MD5_Init(&md5context); MD5_Update(&md5context, (unsigned char *) argv[1], strlen(argv[1])); MD5_Final(digest, &md5context); #endif for (i = 0; i < 16; i++) sprintf(digest_string + (i * 2), "%.2x", digest[i]); Tcl_AppendResult(irp, digest_string, NULL); return TCL_OK; } static int tcl_matchaddr STDVAR { BADARGS(3, 3, " mask address"); if (match_addr(argv[1], argv[2])) Tcl_AppendResult(irp, "1", NULL); else Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } static int tcl_matchcidr STDVAR { BADARGS(4, 4, " block address prefix"); if (cidr_match(argv[1], argv[2], atoi(argv[3]))) Tcl_AppendResult(irp, "1", NULL); else Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } static int tcl_matchstr STDVAR { BADARGS(3, 3, " pattern string"); if (wild_match(argv[1], argv[2])) Tcl_AppendResult(irp, "1", NULL); else Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } static int tcl_status STDVAR { char s[15]; BADARGS(1, 2, " ?type?"); if ((argc < 2) || !strcmp(argv[1], "cpu")) { Tcl_AppendElement(irp, "cputime"); egg_snprintf(s, sizeof s, "%f", getcputime()); Tcl_AppendElement(irp, s); } if ((argc < 2) || !strcmp(argv[1], "mem")) { Tcl_AppendElement(irp, "expmem"); egg_snprintf(s, sizeof s, "%d", expected_memory()); Tcl_AppendElement(irp, s); } if ((argc < 2) || !strcmp(argv[1], "ipv6")) { Tcl_AppendElement(irp, "ipv6"); #ifdef IPV6 Tcl_AppendElement(irp, "enabled"); #else Tcl_AppendElement(irp, "disabled"); #endif } if ((argc < 2) || !strcmp(argv[1], "tls")) { Tcl_AppendElement(irp, "tls"); #ifdef TLS #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ Tcl_AppendElement(irp, OpenSSL_version(OPENSSL_VERSION)); #else Tcl_AppendElement(irp, SSLeay_version(SSLEAY_VERSION)); #endif #else Tcl_AppendElement(irp, "disabled"); #endif } if ((argc < 2) || !strcmp(argv[1], "cache")) { Tcl_AppendElement(irp, "usercache"); egg_snprintf(s, sizeof s, "%4.1f", 100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss))); Tcl_AppendElement(irp, s); } return TCL_OK; } static int tcl_rfcequal STDVAR { BADARGS(3, 3, " string1 string2"); Tcl_AppendResult(irp, !rfc_casecmp(argv[1], argv[2]) ? "1" : "0", NULL); return TCL_OK; } tcl_cmds tclmisc_cmds[] = { {"logfile", tcl_logfile}, {"putlog", tcl_putlog}, {"putcmdlog", tcl_putcmdlog}, {"putxferlog", tcl_putxferlog}, {"putloglev", tcl_putloglev}, {"timer", tcl_timer}, {"utimer", tcl_utimer}, {"killtimer", tcl_killtimer}, {"killutimer", tcl_killutimer}, {"timers", tcl_timers}, {"utimers", tcl_utimers}, {"unixtime", tcl_unixtime}, {"strftime", tcl_strftime}, {"ctime", tcl_ctime}, {"myip", tcl_myip}, {"rand", tcl_rand}, {"sendnote", tcl_sendnote}, {"dumpfile", tcl_dumpfile}, {"dccdumpfile", tcl_dccdumpfile}, {"backup", tcl_backup}, {"exit", tcl_die}, {"die", tcl_die}, {"unames", tcl_unames}, {"unloadmodule", tcl_unloadmodule}, {"loadmodule", tcl_loadmodule}, {"checkmodule", tcl_loadmodule}, {"modules", tcl_modules}, {"loadhelp", tcl_loadhelp}, {"unloadhelp", tcl_unloadhelp}, {"reloadhelp", tcl_reloadhelp}, {"duration", tcl_duration}, {"binds", tcl_binds}, {"callevent", tcl_callevent}, {"stripcodes", tcl_stripcodes}, {"matchaddr", tcl_matchaddr}, {"matchcidr", tcl_matchcidr}, {"matchstr", tcl_matchstr}, {"status", tcl_status}, {"rfcequal", tcl_rfcequal}, {"md5", tcl_md5}, {NULL, NULL} }; eggheads-eggdrop-9c00fe1/src/tcluser.c000066400000000000000000000467431524730042700177550ustar00rootroot00000000000000/* * tcluser.c -- handles: * Tcl stubs for the user-record-oriented commands */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" #include "modules.h" extern Tcl_Interp *interp; extern struct userrec *userlist; extern int default_flags, dcc_total, ignore_time; extern struct dcc_t *dcc; extern char botnetnick[]; extern time_t now; extern struct user_entry_type *entry_type_list; static int tcl_countusers STDVAR { BADARGS(1, 1, ""); Tcl_AppendResult(irp, int_to_base10(count_users(userlist)), NULL); return TCL_OK; } static int tcl_validuser STDVAR { BADARGS(2, 2, " handle"); Tcl_AppendResult(irp, get_user_by_handle(userlist, argv[1]) ? "1" : "0", NULL); return TCL_OK; } static int tcl_finduser STDVAR { struct userrec *u; BADARGS(2, 3, " ?-account? searchString"); if (argc == 3) { if (!strcasecmp(argv[1], "-account")) { u = get_user_by_account(argv[2]); } else { Tcl_AppendResult(irp, "invalid option, must be -account", NULL); return TCL_ERROR; } } else { u = get_user_by_host(argv[1]); } Tcl_AppendResult(irp, u ? u->handle : "*", NULL); return TCL_OK; } static int tcl_passwdOk STDVAR { struct userrec *u; BADARGS(3, 3, " handle passwd"); Tcl_AppendResult(irp, ((u = get_user_by_handle(userlist, argv[1])) && u_pass_match(u, argv[2])) ? "1" : "0", NULL); return TCL_OK; } static int tcl_chattr STDVAR { int of, ocf = 0; char *chan, *chg, work[100]; struct flag_record pls, mns, user; struct userrec *u; BADARGS(2, 4, " handle ?changes? ?channel?"); if ((argv[1][0] == '*') || !(u = get_user_by_handle(userlist, argv[1]))) { Tcl_AppendResult(irp, "*", NULL); return TCL_OK; } if (argc == 4) { user.match = FR_GLOBAL | FR_CHAN; chan = argv[3]; chg = argv[2]; } else if (argc == 3 && argv[2][0]) { int ischan = (findchan_by_dname(argv[2]) != NULL); if (strchr(CHANMETA, argv[2][0]) && !ischan && argv[2][0] != '+' && argv[2][0] != '-') { Tcl_AppendResult(irp, "no such channel", NULL); return TCL_ERROR; } else if (ischan) { /* Channel exists */ user.match = FR_GLOBAL | FR_CHAN; chan = argv[2]; chg = NULL; } else { /* 3rd possibility... channel doesnt exist, does start with a +. * In this case we assume the string is flags. */ user.match = FR_GLOBAL; chan = NULL; chg = argv[2]; } } else { user.match = FR_GLOBAL; chan = NULL; chg = NULL; } if (chan && !findchan_by_dname(chan)) { Tcl_AppendResult(irp, "no such channel", NULL); return TCL_ERROR; } /* Retrieve current flags */ get_user_flagrec(u, &user, chan); /* Make changes */ if (chg) { of = user.global; pls.match = user.match; break_down_flags(chg, &pls, &mns); /* No-one can change these flags on-the-fly */ pls.global &=~(USER_BOT); mns.global &=~(USER_BOT); if (chan) { pls.chan &= ~(BOT_AGGRESSIVE); mns.chan &= ~(BOT_AGGRESSIVE); } user_sanity_check(&(user.global), pls.global, mns.global); user.udef_global = (user.udef_global | pls.udef_global) & ~mns.udef_global; if (chan) { ocf = user.chan; chan_sanity_check(&(user.chan), pls.chan, mns.chan, user.global); user.udef_chan = (user.udef_chan | pls.udef_chan) & ~mns.udef_chan; } set_user_flagrec(u, &user, chan); check_dcc_attrs(u, of); if (chan) check_dcc_chanattrs(u, chan, user.chan, ocf); } user.chan &= ~BOT_AGGRESSIVE; /* actually not a user flag, hide it */ /* Build flag string */ build_flags(work, &user, NULL); Tcl_AppendResult(irp, work, NULL); return TCL_OK; } static int tcl_botattr STDVAR { char *chan, *chg, work[100]; struct flag_record pls, mns, user; struct userrec *u; BADARGS(2, 4, " bot-handle ?changes? ?channel?"); u = get_user_by_handle(userlist, argv[1]); if ((argv[1][0] == '*') || !u || !(u->flags & USER_BOT)) { Tcl_AppendResult(irp, "*", NULL); return TCL_OK; } if (argc == 4) { user.match = FR_BOT | FR_CHAN; chan = argv[3]; chg = argv[2]; } else if (argc == 3 && argv[2][0] && strchr(CHANMETA, argv[2][0]) != NULL) { /* We need todo extra checking here to stop us mixing up +channel's * with flags. */ if (!findchan_by_dname(argv[2]) && argv[2][0] != '+') { /* Channel doesnt exist, and it cant possibly be flags as there * is no + at the start of the string. */ Tcl_AppendResult(irp, "no such channel", NULL); return TCL_ERROR; } else if (findchan_by_dname(argv[2])) { /* Channel exists */ user.match = FR_BOT | FR_CHAN; chan = argv[2]; chg = NULL; } else { /* 3rd possibility... channel doesnt exist, does start with a +. * In this case we assume the string is flags. */ user.match = FR_BOT; chan = NULL; chg = argv[2]; } } else { user.match = FR_BOT; chan = NULL; if (argc < 3) chg = NULL; else chg = argv[2]; } if (chan && !findchan_by_dname(chan)) { Tcl_AppendResult(irp, "no such channel", NULL); return TCL_ERROR; } /* Retrieve current flags */ get_user_flagrec(u, &user, chan); /* Make changes */ if (chg) { pls.match = user.match; break_down_flags(chg, &pls, &mns); /* No-one can change these flags on-the-fly */ if (chan) { pls.chan &= BOT_AGGRESSIVE; mns.chan &= BOT_AGGRESSIVE; } /* this merges user.bot with pls.bot and mns.bot in user.bot * squelch the msgids as this is the tcl version of botattr */ bot_sanity_check(&(user.bot), pls.bot, mns.bot); if (chan) { user.chan = (user.chan | pls.chan) & ~mns.chan; user.udef_chan = (user.udef_chan | pls.udef_chan) & ~mns.udef_chan; } set_user_flagrec(u, &user, chan); } /* Only user flags can be set per channel, not bot ones, so BOT_AGGRESSIVE is a hack to allow botattr |+s */ user.chan &= BOT_AGGRESSIVE; user.udef_chan = 0; /* User definable bot flags are global only, anything here is a regular flag, so hide it. */ /* Build flag string */ build_flags(work, &user, NULL); Tcl_AppendResult(irp, work, NULL); return TCL_OK; } static int tcl_matchattr STDVAR { struct userrec *u; struct flag_record plus = {0}, minus = {0}, user = {0}; int ok = 0, nom = 0; BADARGS(3, 4, " handle flags ?channel?"); if ((u = get_user_by_handle(userlist, argv[1]))) { user.match = FR_GLOBAL | (argc == 4 ? FR_CHAN : 0) | FR_BOT; get_user_flagrec(u, &user, argv[3]); plus.match = user.match; break_down_flags(argv[2], &plus, &minus); minus.match = plus.match ^ (FR_AND | FR_OR); if (!minus.global && !minus.udef_global && !minus.chan && !minus.udef_chan && !minus.bot) { nom = 1; if (!plus.global && !plus.udef_global && !plus.chan && !plus.udef_chan && !plus.bot) { /* No flags (e.g. "-" or "+" or "-|-" matches anyone */ Tcl_AppendResult(irp, "1", NULL); return TCL_OK; } } if (flagrec_eq(&plus, &user)) { if (nom || !flagrec_eq(&minus, &user)) { ok = 1; } } } Tcl_AppendResult(irp, ok ? "1" : "0", NULL); return TCL_OK; } static int tcl_adduser STDVAR { unsigned char *p; BADARGS(2, 3, " handle ?hostmask?"); if (strlen(argv[1]) > HANDLEN) argv[1][HANDLEN] = 0; for (p = (unsigned char *) argv[1]; *p; p++) if (*p <= 32 || *p == '@') *p = '?'; if ((argv[1][0] == '*') || strchr(BADHANDCHARS, argv[1][0]) || get_user_by_handle(userlist, argv[1])) Tcl_AppendResult(irp, "0", NULL); else { userlist = adduser(userlist, argv[1], argv[2], "-", default_flags); Tcl_AppendResult(irp, "1", NULL); } return TCL_OK; } static int tcl_addbot STDVAR { struct bot_addr *bi; /* Max addr len is 60 ? (see cmd_pls_bot in cmds.c) + brackets + delims + ports + 0 */ char *p, *q, addr[75], hand[HANDLEN + 1]; int i, colon=0, braced = 0, ipv6 = 0, count = 0; BADARGS(3, 5, " handle address ?telnet-port ?relay-port??"); /* Copy to adjustable char*'s */ strlcpy(hand, argv[1], sizeof hand); strlcpy(addr, argv[2], sizeof addr); for (p = hand; *p; p++) if ((unsigned char) *p <= 32 || *p == '@') { Tcl_AppendResult(irp, "Invalid character in handle", NULL); return TCL_ERROR; } if ((argv[1][0] == '*') || strchr(BADHANDCHARS, argv[1][0]) || get_user_by_handle(userlist, hand)) Tcl_AppendResult(irp, "0", NULL); else { for (i=0; addr[i]; i++) { if (addr[i] == ':') { count++; colon=i; } if (addr[i] == ']') { braced = i; } } if (count > 1) { ipv6 = 1; } if ((!ipv6 && braced) #ifndef IPV6 || (ipv6) #endif ) { Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } /* Check that the char following the / is not null */ if ((q = strrchr(addr, '/'))) { if (!q[1]) { *q = 0; q = NULL; } } /* Find ports, still allowing them in address for backward compat */ if (!ipv6) { q = strchr(addr, ':'); } else if (braced && (colon > braced)) { q = strrchr(addr, ':'); } else { /* IPv6 address with no braces or braces without port(s) after */ q = NULL; } if (q) { /* Split and count, ignore any following args */ *q++ = 0; p = strchr(q, '/'); if (p) *p++ = 0; } else { /* No port in address field, check next args */ p = NULL; if (argc == 4 || argc == 5) { q = argv[3]; } if (argc == 5) { p = argv[4]; } } /* Verify ports */ #ifndef TLS /* Check TLS */ if ((q && *q == '+') || (p && *p == '+')) { Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } #endif /* Verify */ /* check_int_range returns 0 if q or p is NULL */ if (q && !check_int_range(q, 0, 65536)) { Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } if (p && !check_int_range(p, 0, 65536)) { Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } userlist = adduser(userlist, hand, "none", "-", USER_BOT); bi = user_malloc(sizeof(struct bot_addr)); #ifdef TLS bi->ssl = 0; #endif if ((count = strlen(addr)) > 60) { count = 60; addr[count] = 0; } /* Trim IPv6 []s out if present */ if (braced) { --count; addr[count] = 0; memmove(addr, addr + 1, count); } if (!q) { bi->address = user_malloc(count + 1); strcpy(bi->address, addr); bi->telnet_port = 3333; bi->relay_port = 3333; } else { bi->address = user_malloc(count + 1); strcpy(bi->address, addr); bi->telnet_port = atoi(q); #ifdef TLS if (*q == '+') bi->ssl = TLS_BOT; #endif if (!p) { bi->relay_port = bi->telnet_port; #ifdef TLS bi->ssl *= TLS_BOT + TLS_RELAY; #endif } else { bi->relay_port = atoi(p); #ifdef TLS if (*p == '+') bi->ssl |= TLS_RELAY; #endif } } set_user(&USERENTRY_BOTADDR, get_user_by_handle(userlist, hand), bi); Tcl_AppendResult(irp, "1", NULL); } return TCL_OK; } static int tcl_deluser STDVAR { BADARGS(2, 2, " handle"); Tcl_AppendResult(irp, (argv[1][0] == '*') ? "0" : int_to_base10(deluser(argv[1])), NULL); return TCL_OK; } static int tcl_delhost STDVAR { BADARGS(3, 3, " handle hostmask"); if ((!get_user_by_handle(userlist, argv[1])) || (argv[1][0] == '*')) { Tcl_AppendResult(irp, "non-existent user", NULL); return TCL_ERROR; } Tcl_AppendResult(irp, delhost_by_handle(argv[1], argv[2]) ? "1" : "0", NULL); return TCL_OK; } static int tcl_userlist STDVAR { struct userrec *u; struct flag_record user, plus, minus; int ok = 1, f = 0; BADARGS(1, 3, " ?flags ?channel??"); if (argc == 3 && !findchan_by_dname(argv[2])) { Tcl_AppendResult(irp, "Invalid channel: ", argv[2], NULL); return TCL_ERROR; } if (argc >= 2) { plus.match = FR_GLOBAL | FR_CHAN | FR_BOT; break_down_flags(argv[1], &plus, &minus); f = (minus.global ||minus.udef_global || minus.chan || minus.udef_chan || minus.bot); minus.match = plus.match ^ (FR_AND | FR_OR); } for (u = userlist; u; u = u->next) { if (argc >= 2) { user.match = FR_GLOBAL | FR_CHAN | FR_BOT | (argc == 3 ? 0 : FR_ANYWH); if (argc == 3) get_user_flagrec(u, &user, argv[2]); else get_user_flagrec(u, &user, NULL); if (flagrec_eq(&plus, &user) && !(f && flagrec_eq(&minus, &user))) ok = 1; else ok = 0; } if (ok) Tcl_AppendElement(interp, u->handle); } return TCL_OK; } static int tcl_save STDVAR { write_userfile(-1); return TCL_OK; } static int tcl_reload STDVAR { reload(); return TCL_OK; } static int tcl_chhandle STDVAR { struct userrec *u; char newhand[HANDLEN + 1]; int x = 1, i; BADARGS(3, 3, " oldnick newnick"); u = get_user_by_handle(userlist, argv[1]); if (!u) x = 0; else { strlcpy(newhand, argv[2], sizeof newhand); for (i = 0; i < strlen(newhand); i++) if (((unsigned char) newhand[i] <= 32) || (newhand[i] == '@')) newhand[i] = '?'; if (strchr(BADHANDCHARS, newhand[0]) != NULL) x = 0; else if (strlen(newhand) < 1) x = 0; else if (get_user_by_handle(userlist, newhand)) x = 0; else if (!strcasecmp(botnetnick, newhand) && (!(u->flags & USER_BOT) || nextbot(argv[1]) != -1)) x = 0; else if (newhand[0] == '*') x = 0; } if (x) x = change_handle(u, newhand); Tcl_AppendResult(irp, x ? "1" : "0", NULL); return TCL_OK; } static int tcl_getting_users STDVAR { int i; BADARGS(1, 1, ""); for (i = 0; i < dcc_total; i++) { if (dcc[i].type == &DCC_BOT && dcc[i].status & STAT_GETTING) { Tcl_AppendResult(irp, "1", NULL); return TCL_OK; } } Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } static int tcl_isignore STDVAR { BADARGS(2, 2, " nick!user@host"); Tcl_AppendResult(irp, match_ignore(argv[1]) ? "1" : "0", NULL); return TCL_OK; } static int tcl_newignore STDVAR { time_t expire_time; char ign[UHOSTLEN], cmt[66], from[HANDLEN + 1]; BADARGS(4, 5, " hostmask creator comment ?lifetime?"); strlcpy(ign, argv[1], sizeof ign); strlcpy(from, argv[2], sizeof from); strlcpy(cmt, argv[3], sizeof cmt); if (argc == 4) expire_time = now + 60 * ignore_time; else if ((expire_time = get_expire_time(irp, argv[4])) == -1) return TCL_ERROR; addignore(ign, from, cmt, expire_time); return TCL_OK; } static int tcl_killignore STDVAR { BADARGS(2, 2, " hostmask"); Tcl_AppendResult(irp, delignore(argv[1]) ? "1" : "0", NULL); return TCL_OK; } static int tcl_ignorelist STDVAR { char expire[21], added[21], *p; EGG_CONST char *list[5]; struct igrec *i; BADARGS(1, 1, ""); for (i = global_ign; i; i = i->next) { list[0] = i->igmask; list[1] = i->msg; snprintf(expire, sizeof expire, "%" PRId64, (int64_t) i->expire); list[2] = expire; snprintf(added, sizeof added, "%" PRId64, (int64_t) i->added); list[3] = added; list[4] = i->user; p = Tcl_Merge(5, list); Tcl_AppendElement(irp, p); Tcl_Free((char *) p); } return TCL_OK; } static int tcl_getuser STDVAR { struct user_entry_type *et = NULL; struct userrec *u; struct user_entry *e; BADARGS(2, -1, " handle ?type?"); if (!(u = get_user_by_handle(userlist, argv[1]))) { if (argv[1][0] != '*') { Tcl_AppendResult(irp, "No such user.", NULL); return TCL_ERROR; } else return TCL_OK; /* silently ignore user */ } if (argc >= 3) { if (!(et = find_entry_type(argv[2])) && strcasecmp(argv[2], "HANDLE")) { Tcl_AppendResult(irp, "No such info type: ", argv[2], NULL); return TCL_ERROR; } if (!strcasecmp(argv[2], "HANDLE")) Tcl_AppendResult(irp, u->handle, NULL); else { e = find_user_entry(et, u); if (e) return et->tcl_get(irp, u, e, argc, argv); } } else { for (et = entry_type_list; et; et = et->next) { if (!et->tcl_append) continue; e = find_user_entry(et, u); if (e && e->name) { Tcl_AppendElement(irp, e->name); } else if (et->name) { Tcl_AppendElement(irp, et->name); } else { continue; } if (e) { et->tcl_append(irp, u, e); } else { Tcl_AppendElement(irp, ""); } } } return TCL_OK; } static int tcl_setuser STDVAR { struct user_entry_type *et; struct userrec *u; struct user_entry *e; int r; module_entry *me; BADARGS(3, -1, " handle type ?setting....?"); if (!(et = find_entry_type(argv[2]))) { Tcl_AppendResult(irp, "No such info type: ", argv[2], NULL); return TCL_ERROR; } if (!(u = get_user_by_handle(userlist, argv[1]))) { if (argv[1][0] != '*') { Tcl_AppendResult(irp, "No such user.", NULL); return TCL_ERROR; } else return TCL_OK; /* Silently ignore user * */ } me = module_find("irc", 0, 0); if (me && !strcasecmp(argv[2], "hosts") && argc == 3) { Function *func = me->funcs; (func[IRC_CHECK_THIS_USER]) (argv[1], 1, NULL); } if (!(e = find_user_entry(et, u))) { e = user_malloc(sizeof(struct user_entry)); e->type = et; e->name = NULL; e->u.list = NULL; list_insert((&(u->entries)), e); } r = et->tcl_set(irp, u, e, argc, argv); /* Yeah... e is freed, and we read it... (tcl: setuser hand HOSTS none) */ if ((!e->u.list) && (egg_list_delete((struct list_type **) &(u->entries), (struct list_type *) e))) nfree(e); /* else maybe already freed... (entry_type==HOSTS) */ if (me && !strcasecmp(argv[2], "hosts") && argc == 4) { Function *func = me->funcs; (func[IRC_CHECK_THIS_USER]) (argv[1], 0, NULL); } return r; } tcl_cmds tcluser_cmds[] = { {"countusers", tcl_countusers}, {"validuser", tcl_validuser}, {"finduser", tcl_finduser}, {"passwdok", tcl_passwdOk}, {"chattr", tcl_chattr}, {"botattr", tcl_botattr}, {"matchattr", tcl_matchattr}, {"matchchanattr", tcl_matchattr}, {"adduser", tcl_adduser}, {"addbot", tcl_addbot}, {"deluser", tcl_deluser}, {"delhost", tcl_delhost}, {"userlist", tcl_userlist}, {"save", tcl_save}, {"reload", tcl_reload}, {"chhandle", tcl_chhandle}, {"chnick", tcl_chhandle}, {"getting-users", tcl_getting_users}, {"isignore", tcl_isignore}, {"newignore", tcl_newignore}, {"killignore", tcl_killignore}, {"ignorelist", tcl_ignorelist}, {"getuser", tcl_getuser}, {"setuser", tcl_setuser}, {NULL, NULL} }; eggheads-eggdrop-9c00fe1/src/tls.c000066400000000000000000001254201524730042700170640ustar00rootroot00000000000000/* * tls.c -- handles: * TLS support functions * Certificate handling * OpenSSL initialization and shutdown */ /* * Written by Rumen Stoyanov * * Copyright (C) 2010 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" #ifdef TLS #include #include #include #include #include "version.h" extern int dcc_total, stealth_telnets, tls_vfydcc; extern struct dcc_t *dcc; int tls_maxdepth = 9; /* Max certificate chain verification depth */ int ssl_files_loaded = 0; /* Check for loaded SSL key/cert files */ SSL_CTX *ssl_ctx = NULL; /* SSL context object */ char *tls_randfile = NULL; /* Random seed file for SSL */ char tls_capath[121] = ""; /* Path to trusted CA certificates */ char tls_cafile[121] = ""; /* File containing trusted CA certificates */ char tls_certfile[121] = ""; /* Our own digital certificate ;) */ char tls_keyfile[121] = ""; /* Private key for use with eggdrop */ char tls_protocols[61] = "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3" ; /* A list of protocols for SSL to use */ char tls_dhparam[121] = ""; /* dhparam for SSL to use */ char tls_ciphers[2049] = ""; /* A list of ciphers for SSL to use */ /* Count allocated memory for SSL. This excludes memory allocated by OpenSSL's * family of malloc functions. */ int expmem_tls() { int i, tot; struct threaddata *td = threaddata(); /* currently it's only the appdata structs allocated by ssl_handshake() */ for (i = 0, tot = 0; i < td->MAXSOCKS; i++) if (!(td->socklist[i].flags & (SOCK_UNUSED | SOCK_TCL))) if (td->socklist[i].ssl && SSL_get_app_data(td->socklist[i].ssl)) tot += sizeof(ssl_appdata); return tot; } /* Seeds the PRNG * * Only does something if the system doesn't have enough entropy. * If there is no random file, one will be created either at * $RANDFILE if set or at $HOME/.rnd * * Return value: 0 on success, !=0 on failure. */ static int ssl_seed(void) { char stackdata[1024]; static char rand_file[120]; FILE *fh; if (RAND_status()) return 0; /* Status OK */ /* If '/dev/urandom' is present, OpenSSL will use it by default. * Otherwise we'll have to generate pseudorandom data ourselves, * using system time, our process ID and some uninitialized static * storage. */ putlog(LOG_MISC, "*", "WARNING: TLS: PRNG has not been sufficiently seeded. Seeding now."); if ((fh = fopen("/dev/urandom", "r"))) { fclose(fh); return 0; } if (RAND_file_name(rand_file, sizeof(rand_file))) tls_randfile = rand_file; else return 1; if (!RAND_load_file(rand_file, -1)) { /* generate some pseudo random data */ unsigned int c; c = time(NULL); RAND_seed(&c, sizeof(c)); c = getpid(); RAND_seed(&c, sizeof(c)); RAND_seed(stackdata, sizeof(stackdata)); } if (!RAND_status()) return 2; /* pseudo random data still not enough */ return 0; } /* Get the certificate, corresponding to the connection identified by sock. * * Return value: pointer to a X509 certificate or NULL if we couldn't look up * the certificate. */ static X509 *ssl_getcert(int sock) { int i; struct threaddata *td = threaddata(); i = findsock(sock); if (i == -1 || !td->socklist[i].ssl) return NULL; #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* 3.0.0 */ return SSL_get0_peer_certificate(td->socklist[i].ssl); #else return SSL_get_peer_certificate(td->socklist[i].ssl); #endif } /* Get the certificate fingerprint of the connection corresponding to the * socket. * * Return value: ptr to the hexadecimal representation of the fingerprint or * NULL in case of error. */ static char *ssl_getfp_from_cert(X509 *cert, const EVP_MD *type) { char *p; unsigned int i; static char fp[EVP_MAX_MD_SIZE * 3]; unsigned char md[EVP_MAX_MD_SIZE]; if (!X509_digest(cert, type, md, &i)) { putlog(LOG_MISC, "*", "ERROR: TLS: ssl_getfp_from_cert(): X509_digest()"); return NULL; } if (!(p = OPENSSL_buf2hexstr(md, i))) { putlog(LOG_MISC, "*", "ERROR: TLS: ssl_getfp_from_cert(): OPENSSL_buf2hexstr()"); return NULL; } strlcpy(fp, p, sizeof fp); OPENSSL_free(p); return fp; } /* Get the certificate fingerprint of the connection corresponding * to the socket. * * Return value: ptr to the hexadecimal representation of the fingerprint * or NULL if there's no certificate associated with the connection or in case * of error. */ char *ssl_getfp(int sock) { char *fp; X509 *cert; if (!(cert = ssl_getcert(sock))) return NULL; fp = ssl_getfp_from_cert(cert, EVP_sha1()); #if OPENSSL_VERSION_NUMBER < 0x30000000L /* 3.0.0 */ X509_free(cert); #endif return fp; } // FIXME: Assumption is fingerprint stays the same if path doesn't change void verify_cert_expiry(int idx) { X509 *x509; static char last_tls_certfile[sizeof tls_certfile]; #if OPENSSL_VERSION_NUMBER >= 0x10002000L /* 1.0.2 */ x509 = SSL_CTX_get0_certificate(ssl_ctx); /* The returned pointer must not be freed by the caller. */ #else BIO *bio = BIO_new_file(tls_certfile, "r"); if (!bio) return; x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); #endif if (x509) { if (strcmp(tls_certfile, last_tls_certfile)) { const char *fp = ssl_getfp_from_cert(x509, EVP_sha256()); putlog(LOG_MISC, "*", "Certificate loaded: %s (sha256 fingerprint %s)", tls_certfile, fp ? fp : "(error getting fp)"); strlcpy(last_tls_certfile, tls_certfile, sizeof last_tls_certfile); } #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ int e; if (!X509_check_certificate_times(NULL, x509, &e) && (e == X509_V_ERR_CERT_HAS_EXPIRED)) { #else if (X509_cmp_current_time(X509_get_notAfter(x509)) < 0) { #endif if (idx) { dprintf(idx, "WARNING: SSL/TLS certificate %s expired\n", tls_certfile); dprintf(idx, "You can generate new certificates by running 'make sslcert' from the source directory\n\n"); } else { putlog(LOG_MISC, "*", "\nWARNING: SSL/TLS certificate %s expired", tls_certfile); putlog(LOG_MISC, "*", "You can generate new certificates by running 'make sslcert' from the source directory\n"); } } #if OPENSSL_VERSION_NUMBER < 0x10002000L /* 1.0.2 */ X509_free(x509); BIO_free(bio); #endif } } /* Prepares and initializes SSL stuff * * Creates a context object, supporting SSLv2/v3 & TLSv1 protocols; * Seeds the Pseudo Random Number Generator; * Optionally loads a SSL certificate and a private key. * Tell OpenSSL the location of certificate authority certs * * Return value: 0 on successful initialization, !=0 on failure */ int ssl_init() { /* OpenSSL library initialization * If you are using 1.1.0 or above then you don't need to take any further steps. */ #if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif if (ssl_seed()) { putlog(LOG_MISC, "*", "ERROR: TLS: unable to seed PRNG. Disabling SSL"); #if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ ERR_free_strings(); #endif return -2; } /* A TLS/SSL connection established with this method will understand all supported protocols (SSLv2, SSLv3, and TLSv1) */ if (!(ssl_ctx = SSL_CTX_new(SSLv23_method()))) { putlog(LOG_MISC, "*", "%s", ERR_error_string(ERR_get_error(), NULL)); putlog(LOG_MISC, "*", "ERROR: TLS: unable to create context. Disabling SSL."); #if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ ERR_free_strings(); #endif return -1; } ssl_files_loaded = 0; if ((tls_certfile[0] == '\0') != (tls_keyfile[0] == '\0')) { /* Both need to be set or unset */ putlog(LOG_MISC, "*", "ERROR: TLS: %s set but %s unset. Both must be set " "to use a certificate, or unset both to disable.", tls_certfile[0] ? "ssl-certificate" : "ssl-privatekey", tls_certfile[0] ? "ssl-privatekey" : "ssl-certificate"); fatal("ssl-privatekey and ssl-certificate must both be set or unset.", 0); } if (tls_certfile[0] && tls_keyfile[0]) { /* Load our own certificate and private key. Mandatory for acting as server, because we don't support anonymous ciphers by default. */ if (SSL_CTX_use_certificate_chain_file(ssl_ctx, tls_certfile) != 1) { putlog(LOG_MISC, "*", "ERROR: TLS: unable to load own certificate from %s: %s", tls_certfile, ERR_error_string(ERR_get_error(), NULL)); fatal("Unable to load TLS certificate (ssl-certificate config setting)!", 0); } verify_cert_expiry(0); if (SSL_CTX_use_PrivateKey_file(ssl_ctx, tls_keyfile, SSL_FILETYPE_PEM) != 1) { putlog(LOG_MISC, "*", "ERROR: TLS: unable to load private key from %s: %s", tls_keyfile, ERR_error_string(ERR_get_error(), NULL)); fatal("Unable to load TLS private key (ssl-privatekey config setting)!", 0); } ssl_files_loaded = 1; } if ((tls_capath[0] || tls_cafile[0]) && !SSL_CTX_load_verify_locations(ssl_ctx, tls_cafile[0] ? tls_cafile : NULL, tls_capath[0] ? tls_capath : NULL)) { putlog(LOG_MISC, "*", "ERROR: TLS: unable to set CA certificates location: %s", ERR_error_string(ERR_get_error(), NULL)); #if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ ERR_free_strings(); #endif } /* Let advanced users specify the list of allowed ssl protocols */ #define EGG_SSLv2 (1 << 0) #define EGG_SSLv3 (1 << 1) #define EGG_TLSv1 (1 << 2) #define EGG_TLSv1_1 (1 << 3) #define EGG_TLSv1_2 (1 << 4) #define EGG_TLSv1_3 (1 << 5) if (tls_protocols[0]) { char s[sizeof tls_protocols]; char *sep = " "; char *word; unsigned int protocols = 0; strcpy(s, tls_protocols); for (word = strtok(s, sep); word; word = strtok(NULL, sep)) { if (!strcmp(word, "SSLv2")) protocols |= EGG_SSLv2; if (!strcmp(word, "SSLv3")) protocols |= EGG_SSLv3; if (!strcmp(word, "TLSv1")) protocols |= EGG_TLSv1; if (!strcmp(word, "TLSv1.1")) protocols |= EGG_TLSv1_1; if (!strcmp(word, "TLSv1.2")) protocols |= EGG_TLSv1_2; if (!strcmp(word, "TLSv1.3")) protocols |= EGG_TLSv1_3; } if (!(protocols & EGG_SSLv2)) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2); } if (!(protocols & EGG_SSLv3)) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3); } if (!(protocols & EGG_TLSv1)) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1); } #ifdef SSL_OP_NO_TLSv1_1 if (!(protocols & EGG_TLSv1_1)) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); } #endif #ifdef SSL_OP_NO_TLSv1_2 if (!(protocols & EGG_TLSv1_2)) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); } #endif #ifdef SSL_OP_NO_TLSv1_3 if (!(protocols & EGG_TLSv1_3)) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); } #endif } #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_COMPRESSION); #endif /* Let advanced users specify dhparam */ if (tls_dhparam[0]) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* 3.0.0 */ BIO *pbio = BIO_new_file(tls_dhparam, "r"); if (pbio) { EVP_PKEY *param = PEM_read_bio_Parameters(pbio, NULL); BIO_free(pbio); if (param) { if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, param) == 1) debug1("TLS: setting ssl dhparam %s successful", tls_dhparam); else { EVP_PKEY_free(param); putlog(LOG_MISC, "*", "ERROR: TLS: SSL_CTX_set0_tmp_dh_pkey(%s): %s", tls_dhparam, ERR_error_string(ERR_get_error(), NULL)); } } else putlog(LOG_MISC, "*", "ERROR: TLS: PEM_read_bio_Parameters(%s): %s", tls_dhparam, ERR_error_string(ERR_get_error(), NULL)); } else putlog(LOG_MISC, "*", "ERROR: TLS: BIO_new_file(%s): %s", tls_dhparam, ERR_error_string(ERR_get_error(), NULL)); #else DH *dh; FILE *paramfile = fopen(tls_dhparam, "r"); if (paramfile) { dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL); fclose(paramfile); if (dh) { if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) == 1) debug1("TLS: setting ssl dhparam %s successful", tls_dhparam); else putlog(LOG_MISC, "*", "ERROR: TLS: SSL_CTX_set_tmp_dh(%s): %s", tls_dhparam, ERR_error_string(ERR_get_error(), NULL)); DH_free(dh); } else putlog(LOG_MISC, "*", "ERROR: TLS: PEM_read_DHparams(%s): %s", tls_dhparam, ERR_error_string(ERR_get_error(), NULL)); } else putlog(LOG_MISC, "*", "ERROR: TLS: unable to open %s: %s", tls_dhparam, strerror(errno)); #endif } /* Let advanced users specify the list of allowed ssl ciphers */ if (tls_ciphers[0] && !SSL_CTX_set_cipher_list(ssl_ctx, tls_ciphers)) { /* this replaces any preset ciphers so an invalid list is fatal */ putlog(LOG_MISC, "*", "ERROR: TLS: no valid ciphers found. Disabling SSL."); #if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ ERR_free_strings(); #endif SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; return -3; } const unsigned char sid_ctx[] = "0"; /* anything will do */ SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, (sizeof sid_ctx) - 1); return 0; } /* Free the SSL CTX, clean up the mess */ void ssl_cleanup() { if (ssl_ctx) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } if (tls_randfile) RAND_write_file(tls_randfile); #if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ ERR_free_strings(); #endif } char *ssl_fpconv(char *in, char *out) { long len; char *fp; unsigned char *sha1; if (!in) return NULL; if ((sha1 = OPENSSL_hexstr2buf(in, &len))) { fp = OPENSSL_buf2hexstr(sha1, len); if (fp) { out = user_realloc(out, strlen(fp) + 1); strcpy(out, fp); OPENSSL_free(sha1); OPENSSL_free(fp); return out; } OPENSSL_free(sha1); } return NULL; } /* Get the UID field from the certificate subject name. * The certificate is looked up using the socket of the connection. * * Return value: Pointer to the uid string or NULL if not found */ const char *ssl_getuid(int sock) { int idx; X509 *cert; #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ const #endif X509_NAME *subj; #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ const #endif ASN1_STRING *name; if (!(cert = ssl_getcert(sock))) return NULL; /* Get the subject name */ if (!(subj = X509_get_subject_name(cert))) { #if OPENSSL_VERSION_NUMBER < 0x30000000L /* 3.0.0 */ X509_free(cert); #endif return NULL; } /* Get the first UID */ idx = X509_NAME_get_index_by_NID(subj, NID_userId, -1); if (idx == -1) { #if OPENSSL_VERSION_NUMBER < 0x30000000L /* 3.0.0 */ X509_free(cert); #endif return NULL; } name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, idx)); /* Extract the contents, assuming null-terminated ASCII string */ /* For openssl < 3.0.0 we leak cert here, but we cant free cert here * because we return an internal pointer of certificate * also this function is only triggered in dcc_telnet_id() * and only for ssl-cert-auth set to 2 */ return (const char *) egg_ASN1_string_data(name); } /* Compare the peer's host with their Common Name or dnsName found in * it's certificate. Only the first domain component of cn is allowed to * be a wildcard '*'. The non-wildcard characters are compared ignoring * case. * * Return value: 1 if cn matches host, 0 otherwise. */ static int ssl_hostmatch(const char *cn, const char *host) { const char *p, *q, *r; if ((r = strchr(cn + 1, '.')) && r[-1] == '*' && strchr(r, '.')) { for (p = cn, q = host; *p != '*'; p++, q++) if (toupper((const unsigned char)*p) != toupper((const unsigned char)*q)) return 0; if (!(p = strchr(host, '.')) || strcasecmp(p, r)) return 0; return 1; } /* First domain component is not a wildcard and they aren't allowed elsewhere, so just compare the strings. */ return strcasecmp(cn, host) ? 0 : 1; } /* Confirm the peer identity, by checking if the certificate subject * matches the peer's DNS name or IP address. Matching is performed in * accordance with RFC 2818: * * If the certificate has a subjectAltName extension, all names of type * IPAddress or dnsName present there, will be compared to data->host, * depending on it's contents. * In case there's no subjectAltName extension, commonName (CN) parts * of the certificate subject field will be used instead of IPAddress * and dnsName entries. For IP addresses, common names must contain IPs * in presentation format (1.2.3.4 or 2001:DB8:15:dead::) * Finally, if no subjectAltName or common names are present, the * certificate is considered to not match the peer. * * The structure of X509 certificates and all fields referenced above * are described in RFC 5280. * * The certificate must be pointed by cert and the peer's host must be * placed in data->host. The format is a regular DNS name or an IP in * presentation format (see above). * * Return value: 1 if the certificate matches the peer, 0 otherwise. */ static int ssl_verifycn(X509 *cert, ssl_appdata *data) { const char *cn; int crit = 0, match = 0; ASN1_OCTET_STRING *ip; GENERAL_NAMES *altname; /* SubjectAltName ::= GeneralNames */ ip = a2i_IPADDRESS(data->host); /* check if it's an IP or a hostname */ if ((altname = X509_get_ext_d2i(cert, NID_subject_alt_name, &crit, NULL))) { GENERAL_NAME *gn; /* Loop through the general names in altname and pick these of type ip address or dns name */ while (!match && (gn = sk_GENERAL_NAME_pop(altname))) { /* if the peer's host is an IP, we're only interested in matching against iPAddress general names, otherwise we'll only look for dnsName's */ if (ip) { if (gn->type == GEN_IPADD) match = !ASN1_STRING_cmp(gn->d.ip, ip); } else if (gn->type == GEN_DNS) { /* IA5string holds ASCII data */ cn = (const char *) egg_ASN1_string_data(gn->d.ia5); match = ssl_hostmatch(cn, data->host); } } sk_GENERAL_NAME_free(altname); } else { /* no subjectAltName, try to match against the subject CNs */ #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ const #endif X509_NAME *subj; /* certificate subject */ /* the following is just for information */ switch (crit) { case 0: debug0("TLS: X509 subjectAltName cannot be decoded"); break; case -1: debug0("TLS: X509 has no subjectAltName extension"); break; case -2: debug0("TLS: X509 has multiple subjectAltName extensions"); } /* no subject name either? A completely broken certificate :) */ if (!(subj = X509_get_subject_name(cert))) { putlog(data->loglevel, "*", "TLS: peer certificate has no subject: %s", data->host); match = 0; } else { /* we have a subject name, look at it */ int pos = -1; #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ const #endif ASN1_STRING *name; /* Look for commonName attributes in the subject name */ pos = X509_NAME_get_index_by_NID(subj, NID_commonName, pos); if (pos == -1) /* sorry */ putlog(data->loglevel, "*", "TLS: Peer has no common names and " "no subjectAltName extension. Verification failed."); /* Loop through all common names which may be present in the subject name until we find a match. */ while (!match && pos != -1) { name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, pos)); cn = (const char *) egg_ASN1_string_data(name); if (ip) match = a2i_IPADDRESS(cn) ? (ASN1_STRING_cmp(ip, a2i_IPADDRESS(cn)) ? 0 : 1) : 0; else match = ssl_hostmatch(cn, data->host); pos = X509_NAME_get_index_by_NID(subj, NID_commonName, pos); } } } if (ip) ASN1_OCTET_STRING_free(ip); return match; } /* Extract a human readable version of a X509_NAME and put the result * into a nmalloc'd buffer. * The X509_NAME structure is used for example in certificate subject * and issuer names. * * You need to nfree() the returned pointer. */ #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ static char *ssl_printname(const X509_NAME *name) #else static char *ssl_printname(X509_NAME *name) #endif { long len; char *data, *buf; BIO *bio = BIO_new(BIO_s_mem()); /* 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) { buf = nmalloc(len + 1); memcpy(buf, data, len); /* don't strlcpy() for it would read data[len] */ buf[len] = 0; } else { debug0("TLS: ssl_printname(): BIO_get_mem_data(): error"); buf = nmalloc(1); *buf = 0; } } else { debug0("TLS: ssl_printname(): X509_NAME_print_ex(): error"); buf = nmalloc(1); *buf = 0; } BIO_free(bio); return buf; } /* Print the time from a ASN1_UTCTIME object in standard format i.e. * Nov 21 23:59:00 1996 GMT and store it in a nmalloc'd buffer. * The ASN1_UTCTIME structure is what's used for example with * certificate validity dates. * * You need to nfree() the returned pointer. */ #if OPENSSL_VERSION_NUMBER >= 0x10000000L /* 1.0.0 */ static char *ssl_printtime(const ASN1_UTCTIME *t) #else static char *ssl_printtime(ASN1_UTCTIME *t) #endif { long len; 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) { buf = nmalloc(len + 1); memcpy(buf, data, len); /* don't strlcpy() for it would read data[len] */ buf[len] = 0; } else { debug0("TLS: ssl_printtime(): BIO_get_mem_data(): error"); buf = nmalloc(1); *buf = 0; } BIO_free(bio); return buf; } /* Print the value of an ASN1_INTEGER in hexadecimal format. * A typical use for this is to display certificate serial numbers. * As usual, we use a memory BIO. * * You need to nfree() the returned pointer. */ static char *ssl_printnum(ASN1_INTEGER *i) { long len; 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) { buf = nmalloc(len + 1); memcpy(buf, data, len); /* don't strlcpy() for it would read data[len] */ buf[len] = 0; } else { debug0("TLS: ssl_printnum(): BIO_get_mem_data(): error"); buf = nmalloc(1); *buf = 0; } BIO_free(bio); return buf; } /* Show the user all relevant information about a certificate: subject, * issuer, validity dates and fingerprints. */ static void ssl_showcert(X509 *cert, const int loglev) { char *buf, *from, *to; #if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */ const #endif X509_NAME *name; unsigned int len; unsigned char md[EVP_MAX_MD_SIZE]; /* Subject and issuer names */ if ((name = X509_get_subject_name(cert))) { buf = ssl_printname(name); putlog(loglev, "*", "TLS: certificate subject: %s", buf); nfree(buf); } else putlog(loglev, "*", "TLS: cannot get subject name from certificate!"); if ((name = X509_get_issuer_name(cert))) { buf = ssl_printname(name); putlog(loglev, "*", "TLS: certificate issuer: %s", buf); nfree(buf); } else putlog(loglev, "*", "TLS: cannot get issuer name from certificate!"); /* Fingerprints */ if (X509_digest(cert, EVP_sha1(), md, &len)) { buf = OPENSSL_buf2hexstr(md, len); putlog(loglev, "*", "TLS: certificate SHA1 Fingerprint: %s", buf); OPENSSL_free(buf); } if (X509_digest(cert, EVP_sha256(), md, &len)) { buf = OPENSSL_buf2hexstr(md, len); putlog(loglev, "*", "TLS: certificate SHA-256 Fingerprint: %s", buf); OPENSSL_free(buf); } /* Validity time */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ from = ssl_printtime(X509_get0_notBefore(cert)); to = ssl_printtime(X509_get0_notAfter(cert)); #else from = ssl_printtime(X509_get_notBefore(cert)); to = ssl_printtime(X509_get_notAfter(cert)); #endif putlog(loglev, "*", "TLS: certificate valid from %s to %s", from, to); nfree(from); nfree(to); } /* Certificate validation callback * * Check if the certificate given is valid with respect to the * ssl-verify config variable. This makes it possible to allow * self-signed certificates and is also a convenient place to * extract a certificate summary. * * Return value: 1 - validation passed, 0 - invalid cert */ int ssl_verify(int ok, X509_STORE_CTX *ctx) { SSL *ssl; X509 *cert; ssl_appdata *data; int err, depth; /* get cert, callbacks, error codes, etc. */ depth = X509_STORE_CTX_get_error_depth(ctx); cert = X509_STORE_CTX_get_current_cert(ctx); ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); data = (ssl_appdata *) SSL_get_app_data(ssl); err = X509_STORE_CTX_get_error(ctx); /* OpenSSL won't explicitly generate this error; instead it will * report missing certificates. Refer to SSL_CTX_set_verify(3) * manual for details */ if (depth > tls_maxdepth) { ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; /* depth 0 is actually the peer certificate. We do all custom * verification here and leave the rest of the certificate chain * to OpenSSL's built in procedures. */ } else if (!depth) { /* OpenSSL doesn't perform subject name verification. We need to do * it ourselves. We check here for validity even if it's not requested * in order to be able to warn the user. */ if (!(data->flags & TLS_DEPTH0) && (data->verify & TLS_VERIFYCN) && !ssl_verifycn(cert, data)) { putlog(data->loglevel, "*", "TLS: certificate validation failed. " "Certificate subject does not match peer."); return 0; } data->flags |= TLS_DEPTH0; /* Allow exceptions for certain common verification errors, if the * caller requested so. A lot of servers provide completely invalid * certificates useless for any authentication. */ if (!ok || data->verify) if (((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) && !(data->verify & TLS_VERIFYISSUER)) || ((err == X509_V_ERR_CERT_REVOKED) && !(data->verify & TLS_VERIFYREV)) || ((err == X509_V_ERR_CERT_NOT_YET_VALID) && !(data->verify & TLS_VERIFYFROM)) || ((err == X509_V_ERR_CERT_HAS_EXPIRED) && !(data->verify & TLS_VERIFYTO))) { putlog(data->loglevel, "*", "TLS: peer certificate warning: %s", X509_verify_cert_error_string(err)); ok = 1; } } if (ok || !data->verify) return 1; putlog(data->loglevel, "*", "TLS: certificate validation failed at depth %d: %s", depth, X509_verify_cert_error_string(err)); return 0; } /* SSL info callback, this is used to trace engine state changes * and to check when the handshake is finished, so we can display * some cipher and session information and process callbacks. */ static void ssl_info(const SSL *ssl, int where, int ret) { int sock; X509 *cert; char buf[256]; ssl_appdata *data; #if OPENSSL_VERSION_NUMBER >= 0x009080d1L /* 0.9.8m-beta1 */ const #endif SSL_CIPHER *cipher; int secret, processed, i; if (!(data = (ssl_appdata *) SSL_get_app_data(ssl))) return; /* We're doing non-blocking IO, so we check here if the handshake has finished */ if (where & SSL_CB_HANDSHAKE_DONE) { /* Callback for completed handshake. Cheaper and more convenient than using H_tls */ sock = SSL_get_fd(ssl); if (data->cb) data->cb(sock); /* Call TLS binds. We allow scripts to take over or disable displaying of certificate information. */ if (check_tcl_tls(sock)) return; putlog(data->loglevel, "*", "TLS: handshake successful. Secure connection " "established."); #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* 3.0.0 */ if ((cert = SSL_get0_peer_certificate(ssl))) { ssl_showcert(cert, LOG_DEBUG); #else if ((cert = SSL_get_peer_certificate(ssl))) { ssl_showcert(cert, LOG_DEBUG); X509_free(cert); #endif } else putlog(data->loglevel, "*", "TLS: peer did not present a certificate"); /* Display cipher information */ cipher = SSL_get_current_cipher(ssl); processed = SSL_CIPHER_get_bits(cipher, &secret); putlog(LOG_DEBUG, "*", "TLS: cipher used: %s, %d of %d secret bits used for cipher, %s", SSL_CIPHER_get_name(cipher), processed, secret, SSL_get_version(ssl)); /* secret are the actually secret bits. If processed and secret differ, the rest of the bits are fixed, i.e. for limited export ciphers */ /* More verbose information, for debugging only */ SSL_CIPHER_description(cipher, buf, sizeof buf); i = strlen(buf); if ((i > 0) && (buf[i - 1]) == '\n') buf[i - 1] = 0; debug1("TLS: cipher details: %s", buf); #if OPENSSL_VERSION_NUMBER >= 0x10002000L /* 1.0.2 */ EVP_PKEY *key; if (SSL_get_server_tmp_key((SSL *) ssl, &key)) { putlog(LOG_DEBUG, "*", "TLS: diffie–hellman ephemeral key used: %s, bits %d", OBJ_nid2sn(EVP_PKEY_id(key)), EVP_PKEY_bits(key)); EVP_PKEY_free(key); } #endif } else if (where & SSL_CB_ALERT) { if (strcmp(SSL_alert_type_string(ret), "W") || strcmp(SSL_alert_desc_string(ret), "CN")) { putlog(data->loglevel, "*", "TLS: alert during %s: %s (%s).", (where & SSL_CB_READ) ? "read" : "write", SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); if (!strcmp(SSL_alert_type_string(ret), "F") && !strcmp(SSL_alert_desc_string(ret), "RO")) putlog(LOG_MISC, "*", "TLS: Long TLSCiphertext field received, connection failed. Is this really a TLS port?"); } else { /* Ignore close notify warnings and stop writing to sock */ sock = SSL_get_fd(ssl); debug2("TLS: Received close notify during %s sock %i", (where & SSL_CB_READ) ? "read" : "write", sock); if (where & SSL_CB_WRITE) { int i = findsock(sock); if (i >= 0 && (threaddata()->socklist[i].flags & SOCK_WEBUI)) { int idx = findidx(sock); lostdcc_deferred(idx); } } } } else if (where & SSL_CB_EXIT) { /* SSL_CB_EXIT may point to soft error for non-blocking! */ if (ret == 0) { /* According to manpage, only 0 indicates a real error */ putlog(data->loglevel, "*", "TLS: failed in: %s.", SSL_state_string_long(ssl)); } else if (ret < 0) { int err = SSL_get_error(ssl, ret); /* However we still check <0 as man example does so too */ if (err & (SSL_ERROR_WANT_READ | SSL_ERROR_WANT_WRITE)) { /* Errors to be ignored for non-blocking */ debug1("TLS: awaiting more %s", (err & SSL_ERROR_WANT_READ) ? "reads" : "writes"); } else { putlog(data->loglevel, "*", "TLS: error in: %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 * * Creates a SSL data structure for the connection; * Sets up callbacks and initiates a SSL handshake with the peer; * Reports error conditions and performs cleanup upon failure. * * flags: ssl flags, i.e connect or listen * verify: peer certificate verification flags * loglevel: is the level to output information about the connection * and certificates. * host: contains the dns name or ip address of the peer. Used for * verification. * cb: optional callback, this function will be called after the * handshake completes. * * Return value: 0 on success, !=0 on failure. */ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host, IntFunc cb) { int i, err, ret; ssl_appdata *data; struct threaddata *td = threaddata(); debug0("TLS: attempting SSL negotiation..."); if (!ssl_ctx && ssl_init()) { debug0("TLS: Failed. OpenSSL not initialized properly."); return -1; } if ((flags & TLS_LISTEN) && !ssl_files_loaded) { putlog(LOG_MISC, "*", "TLS: Failed. Certificate/Key not loaded, cannot support SSL/TLS for client (see doc/TLS)."); return -4; } /* find the socket in the list */ i = findsock(sock); if (i == -1) { debug0("TLS: socket not in socklist"); return -2; } if (td->socklist[i].ssl) { debug0("TLS: handshake not required - SSL session already established"); return 0; } td->socklist[i].ssl = SSL_new(ssl_ctx); if (!td->socklist[i].ssl || !SSL_set_fd(td->socklist[i].ssl, td->socklist[i].sock)) { debug1("TLS: cannot initiate SSL session - %s", ERR_error_string(ERR_get_error(), 0)); return -3; } /* Prepare a ssl appdata struct for the verify callback */ data = nmalloc(sizeof(ssl_appdata)); egg_bzero(data, sizeof(ssl_appdata)); data->flags = flags & (TLS_LISTEN | TLS_CONNECT); data->verify = verify; /* Invert these flags as their corresponding configuration values express * exceptions */ if (data->verify) data->verify ^= (TLS_VERIFYISSUER | TLS_VERIFYCN | TLS_VERIFYFROM | TLS_VERIFYTO | TLS_VERIFYREV); data->loglevel = loglevel; data->cb = cb; strlcpy(data->host, host ? host : "", sizeof(data->host)); SSL_set_app_data(td->socklist[i].ssl, data); SSL_set_info_callback(td->socklist[i].ssl, ssl_info); /* We set this +1 to be able to report extra long chains properly. * Otherwise, OpenSSL will break the verification reporting about * missing certificates instead. The rest of the fix is in * ssl_verify() */ SSL_set_verify_depth(td->socklist[i].ssl, tls_maxdepth + 1); SSL_set_mode(td->socklist[i].ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); if (data->flags & TLS_CONNECT) { 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) if (!SSL_set_tlsext_host_name(td->socklist[i].ssl, data->host)) debug1("TLS: setting the server name indication (SNI) to %s failed", data->host); else debug1("TLS: setting the server name indication (SNI) to %s successful", data->host); else debug0("TLS: not setting the server name indication (SNI) because host is an empty string"); #else debug0("TLS: setting the server name indication (SNI) not supported by ssl " "lib, probably < openssl 0.9.8f"); #endif ret = SSL_connect(td->socklist[i].ssl); if (!ret) debug0("TLS: connect handshake failed."); } else { if (data->verify & TLS_VERIFYPEER) SSL_set_verify(td->socklist[i].ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify); else SSL_set_verify(td->socklist[i].ssl, SSL_VERIFY_PEER, ssl_verify); ret = SSL_accept(td->socklist[i].ssl); if (!ret) debug0("TLS: accept handshake failed"); } err = SSL_get_error(td->socklist[i].ssl, ret); /* Normal condition for async I/O, similar to EAGAIN */ if (ret > 0 || err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { debug0("TLS: handshake in progress"); return 0; } if ((err = ERR_peek_error())) { if ((td->socklist[i].flags & SOCK_WEBUI) && ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL && ERR_GET_REASON(err) == SSL_R_HTTP_REQUEST) { /* We dont have access to real port, host or dcc information here */ putlog(LOG_MISC, "*", "TLS: error: HTTP request received on an SSL port"); int j; char *response; char *body = "Error: HTTP request received on an SSL port, please try HTTPS"; j = snprintf(NULL, 0, "HTTP/1.1 400 \r\n" /* textual phrase is OPTIONAL */ "Content-Length: %zu\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Server: %s\r\n" "\r\n%s", strlen(body), #ifdef EGG_PATCH stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER "+" EGG_PATCH, #else stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER, #endif body); response = nmalloc(j + 1); sprintf(response, "HTTP/1.1 400 \r\n" /* textual phrase is OPTIONAL */ "Content-Length: %zu\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Server: %s\r\n" "\r\n%s", strlen(body), #ifdef EGG_PATCH stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER "+" EGG_PATCH, #else stealth_telnets ? "nginx/1.28.0" : "Eggdrop/" EGG_STRINGVER, #endif body); if (write(sock, response, j) < 0) /* tputs() cannot be used here */ putlog(LOG_MISC, "*", "TLS: error: write(sock %i): %s", sock, strerror(errno)); nfree(response); } else { putlog(data->loglevel, "*", "TLS: handshake failed due to the following error: %s", ERR_reason_error_string(err)); debug0("TLS: handshake failed due to the following errors: "); while ((err = ERR_get_error())) debug1("TLS: %s", ERR_error_string(err, NULL)); } } /* Attempt failed, cleanup and abort */ SSL_shutdown(td->socklist[i].ssl); SSL_free(td->socklist[i].ssl); td->socklist[i].ssl = NULL; nfree(data); return -4; } /* Tcl functions */ /* Is the connection secure? */ static int tcl_istls STDVAR { int j; BADARGS(2, 2, " idx"); j = findidx(atoi(argv[1])); if (j < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (dcc[j].ssl) Tcl_AppendResult(irp, "1", NULL); else Tcl_AppendResult(irp, "0", NULL); return TCL_OK; } /* Perform a SSL handshake over an existing plain text * connection. */ static int tcl_starttls STDVAR { int j; struct threaddata *td = threaddata(); BADARGS(2, 2, " idx"); j = findidx(atoi(argv[1])); if (j < 0 || (dcc[j].type != &DCC_SCRIPT)) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } if (dcc[j].ssl) { Tcl_AppendResult(irp, "already started", NULL); return TCL_ERROR; } /* Determine if we're playing a client or a server */ j = findsock(dcc[j].sock); if (ssl_handshake(dcc[j].sock, (td->socklist[j].flags & SOCK_CONNECT) ? TLS_CONNECT : TLS_LISTEN, tls_vfydcc, LOG_MISC, NULL, NULL)) Tcl_AppendResult(irp, "0", NULL); else Tcl_AppendResult(irp, "1", NULL); return TCL_OK; } /* Get all relevant information about an established ssl connection. * This includes certificate subject and issuer, serial number, * expiry date, protocol version and cipher information. * All data is presented as a flat list consisting of name-value pairs. */ static int tcl_tlsstatus STDVAR { char *p; int i, j; X509 *cert; const SSL_CIPHER *cipher; struct threaddata *td = threaddata(); Tcl_DString ds; BADARGS(2, 2, " idx"); /* Allow it to be used for any connection, not just scripted * ones. This makes it possible for a script to display the * server certificate. */ i = findanyidx(atoi(argv[1])); if (i < 0) { Tcl_AppendResult(irp, "invalid idx", NULL); return TCL_ERROR; } j = findsock(dcc[i].sock); if (!j || !dcc[i].ssl || !td->socklist[j].ssl) { Tcl_AppendResult(irp, "not a TLS connection", NULL); return TCL_ERROR; } Tcl_DStringInit(&ds); /* Try to get a cert, clients aren't required to send a * certificate, so this is optional */ #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* 3.0.0 */ cert = SSL_get0_peer_certificate(td->socklist[j].ssl); #else cert = SSL_get_peer_certificate(td->socklist[j].ssl); #endif /* The following information is certificate dependent */ if (cert) { p = ssl_printname(X509_get_subject_name(cert)); Tcl_DStringAppendElement(&ds, "subject"); Tcl_DStringAppendElement(&ds, p); nfree(p); p = ssl_printname(X509_get_issuer_name(cert)); Tcl_DStringAppendElement(&ds, "issuer"); Tcl_DStringAppendElement(&ds, p); nfree(p); #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ p = ssl_printtime(X509_get0_notBefore(cert)); #else p = ssl_printtime(X509_get_notBefore(cert)); #endif Tcl_DStringAppendElement(&ds, "notBefore"); Tcl_DStringAppendElement(&ds, p); nfree(p); #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ p = ssl_printtime(X509_get0_notAfter(cert)); #else p = ssl_printtime(X509_get_notAfter(cert)); #endif Tcl_DStringAppendElement(&ds, "notAfter"); Tcl_DStringAppendElement(&ds, p); nfree(p); p = ssl_printnum(X509_get_serialNumber(cert)); Tcl_DStringAppendElement(&ds, "serial"); Tcl_DStringAppendElement(&ds, p); nfree(p); #if OPENSSL_VERSION_NUMBER < 0x30000000L /* 3.0.0 */ X509_free(cert); #endif } /* We should always have a cipher, but who knows? */ cipher = SSL_get_current_cipher(td->socklist[j].ssl); if (cipher) { /* don't bother if there's none */ Tcl_DStringAppendElement(&ds, "protocol"); Tcl_DStringAppendElement(&ds, SSL_CIPHER_get_version(cipher)); Tcl_DStringAppendElement(&ds, "cipher"); Tcl_DStringAppendElement(&ds, SSL_CIPHER_get_name(cipher)); } /* Done, get a Tcl list from this and return it to the caller */ Tcl_AppendResult(irp, Tcl_DStringValue(&ds), NULL); Tcl_DStringFree(&ds); return TCL_OK; } /* These will be added by tcl.c which is the established practice */ tcl_cmds tcltls_cmds[] = { {"istls", tcl_istls}, {"starttls", tcl_starttls}, {"tlsstatus", tcl_tlsstatus}, {NULL, NULL} }; #endif /* TLS */ eggheads-eggdrop-9c00fe1/src/userent.c000066400000000000000000001160401524730042700177450ustar00rootroot00000000000000/* * userent.c -- handles: * user-entry handling, new style more versatile. */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" #include "users.h" extern int noshare; extern struct userrec *userlist; extern struct dcc_t *dcc; extern Tcl_Interp *interp; extern char whois_fields[]; int share_greet = 0; /* Share greeting info */ int remove_pass = 0; /* create and keep encryption mod passwords */ struct user_entry_type *entry_type_list; void init_userent() { entry_type_list = 0; add_entry_type(&USERENTRY_COMMENT); add_entry_type(&USERENTRY_XTRA); add_entry_type(&USERENTRY_INFO); add_entry_type(&USERENTRY_LASTON); add_entry_type(&USERENTRY_BOTADDR); add_entry_type(&USERENTRY_PASS); add_entry_type(&USERENTRY_PASS2); add_entry_type(&USERENTRY_HOSTS); add_entry_type(&USERENTRY_BOTFL); add_entry_type(&USERENTRY_ACCOUNT); #ifdef TLS add_entry_type(&USERENTRY_FPRINT); #endif } void list_type_kill(struct list_type *t) { struct list_type *u; while (t) { u = t->next; if (t->extra) nfree(t->extra); nfree(t); t = u; } } int list_type_expmem(struct list_type *t) { int tot = 0; for (; t; t = t->next) tot += sizeof(struct list_type) + strlen(t->extra) + 1; return tot; } int def_unpack(struct userrec *u, struct user_entry *e) { char *tmp; tmp = e->u.list->extra; e->u.list->extra = NULL; list_type_kill(e->u.list); e->u.string = tmp; return 1; } int def_pack(struct userrec *u, struct user_entry *e) { char *tmp; tmp = e->u.string; e->u.list = user_malloc(sizeof(struct list_type)); e->u.list->next = NULL; e->u.list->extra = tmp; return 1; } int def_kill(struct user_entry *e) { nfree(e->u.string); nfree(e); return 1; } int def_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { if (fprintf(f, "--%s %s\n", e->type->name, e->u.string) == EOF) return 0; return 1; } void *def_get(struct userrec *u, struct user_entry *e) { return e->u.string; } int def_set(struct userrec *u, struct user_entry *e, void *buf) { char *string = (char *) buf; if (string && !string[0]) string = NULL; if (!string && !e->u.string) return 1; if (string) { int l = strlen(string); char *i; e->u.string = user_realloc(e->u.string, l + 1); strlcpy(e->u.string, string, l + 1); for (i = e->u.string; *i; i++) /* Allow bold, inverse, underline, color text here... * But never add cr or lf!! --rtc */ if ((unsigned int) *i < 32 && !strchr("\002\003\026\037", *i)) *i = '?'; } else { nfree(e->u.string); e->u.string = NULL; } if (!noshare && !(u->flags & (USER_BOT | USER_UNSHARED))) { if (e->type != &USERENTRY_INFO || share_greet) shareout(NULL, "c %s %s %s\n", e->type->name, u->handle, e->u.string ? e->u.string : ""); } return 1; } int def_gotshare(struct userrec *u, struct user_entry *e, char *data, int idx) { putlog(LOG_CMDS, "*", "%s: change %s %s", dcc[idx].nick, e->type->name, u->handle); return e->type->set(u, e, data); } int def_tcl_get(Tcl_Interp * interp, struct userrec *u, struct user_entry *e, int argc, char **argv) { Tcl_AppendResult(interp, e->u.string, NULL); return TCL_OK; } int def_tcl_append(Tcl_Interp * interp, struct userrec *u, struct user_entry *e) { Tcl_AppendElement(interp, e->u.string); return TCL_OK; } int def_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { BADARGS(4, 4, " handle type setting"); e->type->set(u, e, argv[3]); return TCL_OK; } int def_expmem(struct user_entry *e) { return strlen(e->u.string) + 1; } void def_display(int idx, struct user_entry *e) { dprintf(idx, " %s: %s\n", e->type->name, e->u.string); } int def_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e) { return set_user(e->type, new, e->u.string); } static void comment_display(int idx, struct user_entry *e) { if (dcc[idx].user && (dcc[idx].user->flags & USER_MASTER)) dprintf(idx, " COMMENT: %.70s\n", e->u.string); } struct user_entry_type USERENTRY_COMMENT = { 0, /* always 0 ;) */ def_gotshare, def_dupuser, def_unpack, def_pack, def_write_userfile, def_kill, def_get, def_set, def_tcl_get, def_tcl_set, def_expmem, comment_display, "COMMENT", def_tcl_append }; struct user_entry_type USERENTRY_INFO = { 0, /* always 0 ;) */ def_gotshare, def_dupuser, def_unpack, def_pack, def_write_userfile, def_kill, def_get, def_set, def_tcl_get, def_tcl_set, def_expmem, def_display, "INFO", def_tcl_append }; int pass2_set(struct userrec *u, struct user_entry *e, void *new) { if (e->u.extra) { explicit_bzero(e->u.extra, strlen(e->u.extra)); nfree(e->u.extra); } if (new) { /* set PASS2 */ e->u.extra = user_malloc(strlen(new) + 1); strcpy(e->u.extra, new); } else /* remove PASS2 */ e->u.extra = NULL; return 0; } static int def_tcl_null(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { Tcl_AppendResult(irp, "Please use PASS instead.", NULL); return TCL_ERROR; } struct user_entry_type USERENTRY_PASS2 = { 0, 0, 0, def_unpack, def_pack, def_write_userfile, def_kill, def_get, pass2_set, def_tcl_null, def_tcl_null, def_expmem, 0, "PASS2", def_tcl_append }; int pass_set(struct userrec *u, struct user_entry *e, void *buf) { char *pass = buf; unsigned char *p; char new[PASSWORDLEN]; char *new2 = 0; /* encrypt_pass means encryption module is loaded * encrypt_pass2 means encryption2 module is loaded */ if (encrypt_pass && e->u.extra) { explicit_bzero(e->u.extra, strlen(e->u.extra)); nfree(e->u.extra); } if (!pass || !pass[0] || (pass[0] == '-')) { /* empty string or '-' means remove passwords */ if (encrypt_pass) /* remove PASS */ e->u.extra = NULL; if (encrypt_pass2) /* remove PASS2 */ set_user(&USERENTRY_PASS2, u, NULL); } else { /* sanitize password */ if (strlen(pass) > PASSWORDMAX) pass[PASSWORDMAX] = 0; p = (unsigned char *) pass; while (*p) { if ((*p <= 32) || (*p == 127)) *p = '?'; p++; } /* load new with new PASS * load new2 with new PASS2 */ if (u->flags & USER_BOT) { /* set PASS and PASS2 cleartext password */ strlcpy(new, pass, sizeof new); if (encrypt_pass2) new2 = new; } else if (pass[0] == '+') { /* '+' means pass is already encrypted, set PASS = pass */ strlcpy(new, pass, sizeof new); /* due to module api encrypted pass2 cannot be available here * caller must do set_user(&USERENTRY_PASS2, u, password); * probably only share.c:dup_userlist() */ } else { /* encrypt password into new and/or new2 depending on the encryption * modules loaded and the value of remove-pass */ if (encrypt_pass && (!encrypt_pass2 || !remove_pass)) encrypt_pass(pass, new); if (encrypt_pass2) new2 = encrypt_pass2(pass); } /* set PASS to new and PASS2 to new2 depending on the encryption modules * loaded and the value of remove-pass */ if (encrypt_pass && (!encrypt_pass2 || !remove_pass)) { /* set PASS */ e->u.extra = user_malloc(strlen(new) + 1); strcpy(e->u.extra, new); } if (new2) { /* implicit encrypt_pass2 && */ /* set PASS2 */ set_user(&USERENTRY_PASS2, u, new2); if (encrypt_pass && remove_pass && e->u.extra) e->u.extra = NULL; /* remove PASS, e->u.extra already freed */ } explicit_bzero(new, sizeof new); if (new2 && new2 != new) explicit_bzero(new2, strlen(new2)); } if (!noshare && !(u->flags & (USER_BOT | USER_UNSHARED))) shareout(NULL, "c PASS %s %s\n", u->handle, pass ? pass : ""); return 1; } static int pass_tcl_get(Tcl_Interp * interp, struct userrec *u, struct user_entry *e, int argc, char **argv) { char *pass = 0; if (encrypt_pass2) pass = get_user(&USERENTRY_PASS2, u); if (!pass) pass = e->u.string; Tcl_AppendResult(interp, pass, NULL); return TCL_OK; } static int pass_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { BADARGS(3, 4, " handle PASS ?newpass?"); pass_set(u, e, argc == 3 ? NULL : argv[3]); return TCL_OK; } struct user_entry_type USERENTRY_PASS = { 0, def_gotshare, 0, def_unpack, def_pack, def_write_userfile, def_kill, def_get, pass_set, pass_tcl_get, pass_tcl_set, def_expmem, 0, "PASS", def_tcl_append }; static int laston_unpack(struct userrec *u, struct user_entry *e) { char *par, *arg; struct laston_info *li; par = e->u.list->extra; arg = newsplit(&par); if (!par[0]) par = "???"; li = user_malloc(sizeof(struct laston_info)); li->lastonplace = user_malloc(strlen(par) + 1); li->laston = atoi(arg); strcpy(li->lastonplace, par); list_type_kill(e->u.list); e->u.extra = li; return 1; } static int laston_pack(struct userrec *u, struct user_entry *e) { char work[1024]; struct laston_info *li; int l; li = (struct laston_info *) e->u.extra; l = sprintf(work, "%" PRId64 " %s", (int64_t) li->laston, li->lastonplace); e->u.list = user_malloc(sizeof(struct list_type)); e->u.list->next = NULL; e->u.list->extra = user_malloc(l + 1); strcpy(e->u.list->extra, work); nfree(li->lastonplace); nfree(li); return 1; } static int laston_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { struct laston_info *li = (struct laston_info *) e->u.extra; if (fprintf(f, "--LASTON %" PRId64 " %s\n", (int64_t) li->laston, li->lastonplace ? li->lastonplace : "") == EOF) return 0; return 1; } static int laston_kill(struct user_entry *e) { if (((struct laston_info *) (e->u.extra))->lastonplace) nfree(((struct laston_info *) (e->u.extra))->lastonplace); nfree(e->u.extra); nfree(e); return 1; } static int laston_set(struct userrec *u, struct user_entry *e, void *buf) { struct laston_info *li = (struct laston_info *) e->u.extra; if (li != buf) { if (li) { nfree(li->lastonplace); nfree(li); } e->u.extra = buf; } /* donut share laston info */ return 1; } static int laston_tcl_get(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct laston_info *li = (struct laston_info *) e->u.extra; char number[22]; struct chanuserrec *cr; BADARGS(3, 4, " handle LASTON ?channel?"); if (argc == 4) { for (cr = u->chanrec; cr; cr = cr->next) { if (!rfc_casecmp(cr->channel, argv[3])) { Tcl_AppendResult(irp, int_to_base10(cr->laston), NULL); break; } } if (!cr) Tcl_AppendResult(irp, "0", NULL); } else { snprintf(number, sizeof number, "%" PRId64 " ", (int64_t) li->laston); Tcl_AppendResult(irp, number, li->lastonplace, NULL); } return TCL_OK; } static int laston_tcl_append(Tcl_Interp *irp, struct userrec *u, struct user_entry *e) { Tcl_DString ds; struct chanuserrec *cr; Tcl_DStringInit(&ds); for (cr = u->chanrec; cr; cr = cr->next) { Tcl_DStringAppendElement(&ds, cr->channel); Tcl_DStringAppendElement(&ds, int_to_base10(cr->laston)); } Tcl_AppendElement(irp, Tcl_DStringValue(&ds)); Tcl_DStringFree(&ds); return TCL_OK; } static int laston_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct laston_info *li; struct chanuserrec *cr; BADARGS(4, 5, " handle LASTON time ?place?"); if ((argc == 5) && argv[4][0] && strchr(CHANMETA, argv[4][0])) { /* Search for matching channel */ for (cr = u->chanrec; cr; cr = cr->next) if (!rfc_casecmp(cr->channel, argv[4])) { cr->laston = atoi(argv[3]); break; } } /* Save globally */ li = user_malloc(sizeof(struct laston_info)); if (argc == 5) { li->lastonplace = user_malloc(strlen(argv[4]) + 1); strcpy(li->lastonplace, argv[4]); } else { li->lastonplace = user_malloc(1); li->lastonplace[0] = 0; } li->laston = atoi(argv[3]); set_user(&USERENTRY_LASTON, u, li); return TCL_OK; } static int laston_expmem(struct user_entry *e) { return sizeof(struct laston_info) + strlen(((struct laston_info *) (e->u.extra))->lastonplace) + 1; } static int laston_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e) { struct laston_info *li = e->u.extra, *li2; if (li) { li2 = user_malloc(sizeof(struct laston_info)); li2->laston = li->laston; li2->lastonplace = user_malloc(strlen(li->lastonplace) + 1); strcpy(li2->lastonplace, li->lastonplace); return set_user(&USERENTRY_LASTON, new, li2); } return 0; } struct user_entry_type USERENTRY_LASTON = { 0, /* always 0 ;) */ 0, laston_dupuser, laston_unpack, laston_pack, laston_write_userfile, laston_kill, def_get, laston_set, laston_tcl_get, laston_tcl_set, laston_expmem, 0, "LASTON", laston_tcl_append }; static int botaddr_unpack(struct userrec *u, struct user_entry *e) { char *p, *q; struct bot_addr *bi = user_malloc(sizeof(struct bot_addr)); egg_bzero(bi, sizeof(struct bot_addr)); if (!(q = strchr((p = e->u.list->extra), ':'))) { bi->address = user_malloc(strlen(p) + 1); strcpy(bi->address, p); } else { bi->address = user_malloc((q - p) + 1); strncpy(bi->address, p, q - p); bi->address[q - p] = 0; q++; #ifdef TLS if (*q == '+') bi->ssl |= TLS_BOT; #endif bi->telnet_port = atoi(q); if ((q = strchr(q, '/'))) #ifdef TLS { if (q[1] == '+') bi->ssl |= TLS_RELAY; bi->relay_port = atoi(q + 1); } #else bi->relay_port = atoi(q + 1); #endif } #ifdef IPV6 for (p = bi->address; *p; p++) if (*p == ';') *p = ':'; #endif if (!bi->telnet_port) bi->telnet_port = 3333; if (!bi->relay_port) bi->relay_port = bi->telnet_port; list_type_kill(e->u.list); e->u.extra = bi; return 1; } static int botaddr_pack(struct userrec *u, struct user_entry *e) { char work[1024], *p, *q = work; struct bot_addr *bi; int l; bi = (struct bot_addr *) e->u.extra; for (p = bi->address; *p; p++) if (*p == ':') *q++ = ';'; else *q++ = *p; #ifdef TLS l = simple_sprintf(q, ":%s%u/%s%u", (bi->ssl & TLS_BOT) ? "+" : "", bi->telnet_port, (bi->ssl & TLS_RELAY) ? "+" : "", bi->relay_port); #else l = simple_sprintf(q, ":%u/%u", bi->telnet_port, bi->relay_port); #endif e->u.list = user_malloc(sizeof(struct list_type)); e->u.list->next = NULL; e->u.list->extra = user_malloc(l + 1); strcpy(e->u.list->extra, work); nfree(bi->address); nfree(bi); return 1; } static int botaddr_kill(struct user_entry *e) { nfree(((struct bot_addr *) (e->u.extra))->address); nfree(e->u.extra); nfree(e); return 1; } static int botaddr_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { int ret = 1; char *p, *q, *addr; struct bot_addr *bi = (struct bot_addr *) e->u.extra; p = bi->address; addr = user_malloc(strlen(bi->address) + 1); for (q = addr; *p; p++) if (*p == ':') *q++ = ';'; else *q++ = *p; *q = 0; #ifdef TLS if (fprintf(f, "--%s %s:%s%u/%s%u\n", e->type->name, addr, (bi->ssl & TLS_BOT) ? "+" : "", bi->telnet_port, (bi->ssl & TLS_RELAY) ? "+" : "", bi->relay_port) == EOF) #else if (fprintf(f, "--%s %s:%u/%u\n", e->type->name, addr, bi->telnet_port, bi->relay_port) == EOF) #endif ret = 0; nfree(addr); return ret; } static int botaddr_set(struct userrec *u, struct user_entry *e, void *buf) { struct bot_addr *bi = (struct bot_addr *) e->u.extra; if (!bi && !buf) return 1; if (bi != buf) { if (bi) { nfree(bi->address); nfree(bi); } bi = e->u.extra = buf; } if (bi && !noshare && !(u->flags & USER_UNSHARED)) { #ifdef TLS shareout(NULL, "c BOTADDR %s %s %s%d %s%d\n", u->handle, bi->address, (bi->ssl & TLS_BOT) ? "+" : "", bi->telnet_port, (bi->ssl & TLS_RELAY) ? "+" : "", bi->relay_port); #else shareout(NULL, "c BOTADDR %s %s %d %d\n", u->handle, bi->address, bi->telnet_port, bi->relay_port); #endif } return 1; } static int botaddr_tcl_dstring(Tcl_DString *ds, struct user_entry *e) { struct bot_addr *bi = (struct bot_addr *)e->u.extra; Tcl_DStringAppendElement(ds, bi->address); /* This is safe because there are no special characters in the port */ Tcl_DStringAppend(ds, " ", 1); #ifdef TLS if (bi->ssl & TLS_BOT) Tcl_DStringAppend(ds, "+", 1); #endif Tcl_DStringAppend(ds, int_to_base10(bi->telnet_port), -1); Tcl_DStringAppend(ds, " ", 1); #ifdef TLS if (bi->ssl & TLS_RELAY) Tcl_DStringAppend(ds, "+", 1); #endif Tcl_DStringAppend(ds, int_to_base10(bi->relay_port), -1); return TCL_OK; } static int botaddr_tcl_get(Tcl_Interp * interp, struct userrec *u, struct user_entry *e, int argc, char **argv) { Tcl_DString ds; Tcl_DStringInit(&ds); botaddr_tcl_dstring(&ds, e); Tcl_AppendResult(interp, Tcl_DStringValue(&ds), NULL); Tcl_DStringFree(&ds); return TCL_OK; } static int botaddr_tcl_append(Tcl_Interp * interp, struct userrec *u, struct user_entry *e) { Tcl_DString ds; Tcl_DStringInit(&ds); botaddr_tcl_dstring(&ds, e); Tcl_AppendElement(interp, Tcl_DStringValue(&ds)); Tcl_DStringFree(&ds); return TCL_OK; } static int botaddr_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct bot_addr *bi = (struct bot_addr *) e->u.extra; BADARGS(4, 6, " handle type address ?telnetport ?relayport??"); if (u->flags & USER_BOT) { /* Silently ignore for users */ if (!bi) { bi = user_malloc(sizeof(struct bot_addr)); egg_bzero(bi, sizeof(struct bot_addr)); } else nfree(bi->address); bi->address = user_malloc(strlen(argv[3]) + 1); strcpy(bi->address, argv[3]); if (argc > 4) { #ifdef TLS /* If no user port set, use bot port for both entries */ if (*argv[4] == '+') { bi->ssl |= TLS_BOT; if (argc == 5) { bi->ssl |= TLS_RELAY; } } else { bi->ssl &= ~TLS_BOT; if (argc == 5) { bi->ssl &= ~TLS_RELAY; } } #endif bi->telnet_port = atoi(argv[4]); if (argc == 5) { bi->relay_port = atoi(argv[4]); } } if (argc > 5) { #ifdef TLS if (*argv[5] == '+') { bi->ssl |= TLS_RELAY; } else { bi->ssl &= ~TLS_RELAY; } #endif bi->relay_port = atoi(argv[5]); } if (!bi->telnet_port) bi->telnet_port = 3333; if (!bi->relay_port) bi->relay_port = bi->telnet_port; botaddr_set(u, e, bi); } return TCL_OK; } static int botaddr_expmem(struct user_entry *e) { struct bot_addr *bi = (struct bot_addr *) e->u.extra; return strlen(bi->address) + 1 + sizeof(struct bot_addr); } static void botaddr_display(int idx, struct user_entry *e) { struct bot_addr *bi = (struct bot_addr *) e->u.extra; dprintf(idx, " ADDRESS: %.70s\n", bi->address); #ifdef TLS dprintf(idx, " users: %s%d, bots: %s%d\n", (bi->ssl & TLS_RELAY) ? "+" : "", bi->relay_port, (bi->ssl & TLS_BOT) ? "+" : "", bi->telnet_port); #else dprintf(idx, " users: %d, bots: %d\n", bi->relay_port, bi->telnet_port); #endif } static int botaddr_gotshare(struct userrec *u, struct user_entry *e, char *buf, int idx) { struct bot_addr *bi = user_malloc(sizeof(struct bot_addr)); char *arg; egg_bzero(bi, sizeof(struct bot_addr)); arg = newsplit(&buf); bi->address = user_malloc(strlen(arg) + 1); strcpy(bi->address, arg); arg = newsplit(&buf); #ifdef TLS if (*arg == '+') bi->ssl |= TLS_BOT; if (*buf == '+') bi->ssl |= TLS_RELAY; #endif bi->telnet_port = atoi(arg); bi->relay_port = atoi(buf); if (!bi->telnet_port) bi->telnet_port = 3333; if (!bi->relay_port) bi->relay_port = bi->telnet_port; if (!(dcc[idx].status & STAT_GETTING)) putlog(LOG_CMDS, "*", "%s: change botaddr %s", dcc[idx].nick, u->handle); return botaddr_set(u, e, bi); } static int botaddr_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e) { if (old->flags & USER_BOT) { struct bot_addr *bi = e->u.extra, *bi2; if (bi) { bi2 = user_malloc(sizeof(struct bot_addr)); bi2->telnet_port = bi->telnet_port; bi2->relay_port = bi->relay_port; #ifdef TLS bi2->ssl = bi->ssl; #endif bi2->address = user_malloc(strlen(bi->address) + 1); strcpy(bi2->address, bi->address); return set_user(&USERENTRY_BOTADDR, new, bi2); } } return 0; } struct user_entry_type USERENTRY_BOTADDR = { 0, /* always 0 ;) */ botaddr_gotshare, botaddr_dupuser, botaddr_unpack, botaddr_pack, botaddr_write_userfile, botaddr_kill, def_get, botaddr_set, botaddr_tcl_get, botaddr_tcl_set, botaddr_expmem, botaddr_display, "BOTADDR", botaddr_tcl_append }; int xtra_set(struct userrec *u, struct user_entry *e, void *buf) { struct xtra_key *curr, *old = NULL, *new = buf; for (curr = e->u.extra; curr; curr = curr->next) { if (curr->key && !strcasecmp(curr->key, new->key)) { old = curr; break; } } if (!old && (!new->data || !new->data[0])) { /* Delete non-existent entry -- doh ++rtc */ nfree(new->key); if (new->data) nfree(new->data); nfree(new); return TCL_OK; } /* We will possibly free new below, so let's send the information * to the botnet now */ if (!noshare && !(u->flags & (USER_BOT | USER_UNSHARED))) shareout(NULL, "c XTRA %s %s %s\n", u->handle, new->key, new->data ? new->data : ""); if ((old && old != new) || !new->data || !new->data[0]) { egg_list_delete(&e->u.list, (struct list_type *) old); nfree(old->key); nfree(old->data); if (old == e->u.extra) e->u.extra = NULL; nfree(old); old = NULL; } /* don't do anything when old == new */ if (old != new) { if (new->data && new->data[0]) list_insert((&e->u.extra), new) /* do not add a ';' here */ else { if (new->data) nfree(new->data); nfree(new->key); nfree(new); } } return TCL_OK; } static int xtra_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct xtra_key *xk; int l; BADARGS(4, 5, " handle type key ?value?"); xk = user_malloc(sizeof(struct xtra_key)); l = strlen(argv[3]); egg_bzero(xk, sizeof(struct xtra_key)); if (l > 500) l = 500; xk->key = user_malloc(l + 1); strlcpy(xk->key, argv[3], l + 1); if (argc == 5) { int k = strlen(argv[4]); if (k > 500 - l) k = 500 - l; xk->data = user_malloc(k + 1); strlcpy(xk->data, argv[4], k + 1); } xtra_set(u, e, xk); return TCL_OK; } int xtra_unpack(struct userrec *u, struct user_entry *e) { struct list_type *curr, *head; struct xtra_key *t; char *key, *data; head = curr = e->u.list; e->u.extra = NULL; while (curr) { t = user_malloc(sizeof(struct xtra_key)); data = curr->extra; key = newsplit(&data); if (data[0]) { t->key = user_malloc(strlen(key) + 1); strcpy(t->key, key); t->data = user_malloc(strlen(data) + 1); strcpy(t->data, data); list_insert((&e->u.extra), t); } curr = curr->next; } list_type_kill(head); return 1; } static int xtra_pack(struct userrec *u, struct user_entry *e) { struct list_type *t; struct xtra_key *curr, *next; curr = e->u.extra; e->u.list = NULL; while (curr) { t = user_malloc(sizeof(struct list_type)); t->extra = user_malloc(strlen(curr->key) + strlen(curr->data) + 4); sprintf(t->extra, "%s %s", curr->key, curr->data); list_insert((&e->u.list), t); next = curr->next; nfree(curr->key); nfree(curr->data); nfree(curr); curr = next; } return 1; } static void xtra_display(int idx, struct user_entry *e) { int code; Tcl_Size lc, j; EGG_CONST char **list; struct xtra_key *xk; code = Tcl_SplitList(interp, whois_fields, &lc, &list); if (code == TCL_ERROR) return; /* Scan thru xtra field, searching for matches */ for (xk = e->u.extra; xk; xk = xk->next) { /* Ok, it's a valid xtra field entry */ for (j = 0; j < lc; j++) { if (!strcasecmp(list[j], xk->key)) dprintf(idx, " %s: %s\n", xk->key, xk->data); } } Tcl_Free((char *) list); } static int xtra_gotshare(struct userrec *u, struct user_entry *e, char *buf, int idx) { char *arg; struct xtra_key *xk; int l; arg = newsplit(&buf); if (!arg[0]) return 1; xk = user_malloc(sizeof(struct xtra_key)); egg_bzero(xk, sizeof(struct xtra_key)); l = strlen(arg); if (l > 500) l = 500; xk->key = user_malloc(l + 1); strlcpy(xk->key, arg, l + 1); if (buf[0]) { int k = strlen(buf); if (k > 500 - l) k = 500 - l; xk->data = user_malloc(k + 1); strlcpy(xk->data, buf, k + 1); } xtra_set(u, e, xk); return 1; } static int xtra_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e) { struct xtra_key *x1, *x2; for (x1 = e->u.extra; x1; x1 = x1->next) { x2 = user_malloc(sizeof(struct xtra_key)); x2->key = user_malloc(strlen(x1->key) + 1); strcpy(x2->key, x1->key); x2->data = user_malloc(strlen(x1->data) + 1); strcpy(x2->data, x1->data); set_user(&USERENTRY_XTRA, new, x2); } return 1; } static int xtra_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { struct xtra_key *x; for (x = e->u.extra; x; x = x->next) if (fprintf(f, "--XTRA %s %s\n", x->key, x->data) == EOF) return 0; return 1; } int xtra_kill(struct user_entry *e) { struct xtra_key *x, *y; for (x = e->u.extra; x; x = y) { y = x->next; nfree(x->key); nfree(x->data); nfree(x); } nfree(e); return 1; } static int xtra_tcl_dstring(Tcl_DString *ds, int sublist, struct user_entry *e) { struct xtra_key *x; for (x = e->u.extra; x; x = x->next) { if (sublist) Tcl_DStringStartSublist(ds); Tcl_DStringAppendElement(ds, x->key); Tcl_DStringAppendElement(ds, x->data); if (sublist) Tcl_DStringEndSublist(ds); } return TCL_OK; } static int xtra_tcl_get(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct xtra_key *x; BADARGS(3, 4, " handle XTRA ?key?"); if (argc == 3) { Tcl_DString ds; Tcl_DStringInit(&ds); xtra_tcl_dstring(&ds, 1, e); Tcl_AppendResult(irp, Tcl_DStringValue(&ds), NULL); Tcl_DStringFree(&ds); return TCL_OK; } for (x = e->u.extra; x; x = x->next) if (!strcasecmp(argv[3], x->key)) { Tcl_AppendResult(irp, x->data, NULL); return TCL_OK; } return TCL_OK; } static int xtra_tcl_append(Tcl_Interp *irp, struct userrec *u, struct user_entry *e) { Tcl_DString ds; Tcl_DStringInit(&ds); xtra_tcl_dstring(&ds, 0, e); Tcl_AppendElement(irp, Tcl_DStringValue(&ds)); Tcl_DStringFree(&ds); return TCL_OK; } static int xtra_expmem(struct user_entry *e) { struct xtra_key *x; int tot = 0; for (x = e->u.extra; x; x = x->next) { tot += sizeof(struct xtra_key); tot += strlen(x->key) + 1; tot += strlen(x->data) + 1; } return tot; } struct user_entry_type USERENTRY_XTRA = { 0, xtra_gotshare, xtra_dupuser, xtra_unpack, xtra_pack, xtra_write_userfile, xtra_kill, def_get, xtra_set, xtra_tcl_get, xtra_tcl_set, xtra_expmem, xtra_display, "XTRA", xtra_tcl_append }; static int hosts_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e) { struct list_type *h; for (h = e->u.extra; h; h = h->next) set_user(&USERENTRY_HOSTS, new, h->extra); return 1; } static int hosts_null(struct userrec *u, struct user_entry *e) { return 1; } static int hosts_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { struct list_type *h; for (h = e->u.extra; h; h = h->next) if (fprintf(f, "--HOSTS %s\n", h->extra) == EOF) return 0; return 1; } static int hosts_kill(struct user_entry *e) { list_type_kill(e->u.list); nfree(e); return 1; } static int hosts_expmem(struct user_entry *e) { return list_type_expmem(e->u.list); } static void hosts_display(int idx, struct user_entry *e) { char s[1024]; struct list_type *q; s[0] = 0; strcpy(s, " HOSTS: "); for (q = e->u.list; q; q = q->next) { if (s[0] && !s[9]) strncat(s, q->extra, (sizeof s - strlen(s) -1)); else if (!s[0]) sprintf(s, " %s", q->extra); else { if (strlen(s) + strlen(q->extra) + 2 > 65) { dprintf(idx, "%s\n", s); sprintf(s, " %s", q->extra); } else { strcat(s, ", "); strcat(s, q->extra); } } } if (s[0]) dprintf(idx, "%s\n", s); } static int hosts_set(struct userrec *u, struct user_entry *e, void *buf) { if (!buf || !strcasecmp(buf, "none")) { /* When the bot crashes, it's in this part, not in the 'else' part */ list_type_kill(e->u.list); e->u.list = NULL; } else { char *host = buf, *p = strchr(host, ','); struct list_type **t; /* Can't have ,'s in hostmasks */ while (p) { *p = '?'; p = strchr(host, ','); } /* fred1: check for redundant hostmasks with * controversial "superpenis" algorithm ;) */ /* I'm surprised Raistlin hasn't gotten involved in this controversy */ t = &(e->u.list); while (*t) { if (cmp_usermasks(host, (*t)->extra)) { struct list_type *u; u = *t; *t = (*t)->next; if (u->extra) nfree(u->extra); nfree(u); } else t = &((*t)->next); } *t = user_malloc(sizeof(struct list_type)); (*t)->next = NULL; (*t)->extra = user_malloc(strlen(host) + 1); strcpy((*t)->extra, host); } return 1; } static int hosts_tcl_get(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct list_type *x; BADARGS(3, 3, " handle HOSTS"); for (x = e->u.list; x; x = x->next) Tcl_AppendElement(irp, x->extra); return TCL_OK; } static int hosts_tcl_append(Tcl_Interp *irp, struct userrec *u, struct user_entry *e) { Tcl_DString ds; struct list_type *x; Tcl_DStringInit(&ds); for (x = e->u.list; x; x = x->next) Tcl_DStringAppendElement(&ds, x->extra); Tcl_AppendElement(irp, Tcl_DStringValue(&ds)); Tcl_DStringFree(&ds); return TCL_OK; } static int hosts_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { BADARGS(3, 4, " handle HOSTS ?host?"); if (argc == 4) addhost_by_handle(u->handle, argv[3]); else addhost_by_handle(u->handle, "none"); /* drummer */ return TCL_OK; } static int hosts_gotshare(struct userrec *u, struct user_entry *e, char *buf, int idx) { /* doh, try to be too clever and it bites your butt */ return 0; } struct user_entry_type USERENTRY_HOSTS = { 0, hosts_gotshare, hosts_dupuser, hosts_null, hosts_null, hosts_write_userfile, hosts_kill, def_get, hosts_set, hosts_tcl_get, hosts_tcl_set, hosts_expmem, hosts_display, "HOSTS", hosts_tcl_append }; #ifdef TLS int fprint_unpack(struct userrec *u, struct user_entry *e) { char *tmp; tmp = ssl_fpconv(e->u.list->extra, NULL); nfree(e->u.list->extra); e->u.list->extra = NULL; list_type_kill(e->u.list); e->u.string = tmp; return 1; } int fprint_set(struct userrec *u, struct user_entry *e, void *buf) { char *fp = buf; if (!fp || !fp[0] || (fp[0] == '-')) { if (e->u.string) { nfree(e->u.string); e->u.string = NULL; } } else { fp = ssl_fpconv(buf, e->u.string); if (fp) e->u.string = fp; else return 0; } if (!noshare && !(u->flags & (USER_BOT | USER_UNSHARED))) shareout(NULL, "c FPRINT %s %s\n", u->handle, e->u.string ? e->u.string : ""); return 1; } static int fprint_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { BADARGS(3, 4, " handle FPRINT ?new-fingerprint?"); fprint_set(u, e, argc == 3 ? NULL : argv[3]); return TCL_OK; } struct user_entry_type USERENTRY_FPRINT = { 0, def_gotshare, def_dupuser, fprint_unpack, def_pack, def_write_userfile, def_kill, def_get, fprint_set, def_tcl_get, fprint_tcl_set, def_expmem, 0, "FPRINT", def_tcl_append }; #endif /* TLS */ static void account_display(int idx, struct user_entry *e) { char s[1024]; struct list_type *q; s[0] = 0; strcpy(s, " ACCOUNTS: "); for (q = e->u.list; q; q = q->next) { if (s[0] && !s[12]) strncat(s, q->extra, (sizeof s - strlen(s) -1)); else if (!s[0]) sprintf(s, " %s", q->extra); else { if (strlen(s) + strlen(q->extra) + 2 > 65) { dprintf(idx, "%s\n", s); sprintf(s, " %s", q->extra); } else { strcat(s, ", "); strcat(s, q->extra); } } } if (s[0]) dprintf(idx, "%s\n", s); } static int account_set(struct userrec *u, struct user_entry *e, void *buf) { if (!buf || !strcasecmp(buf, "none")) { /* When the bot crashes, it's in this part, not in the 'else' part */ list_type_kill(e->u.list); e->u.list = NULL; } else { char *acct = buf, *p = strchr(acct, ','); struct list_type **t; /* Can't have ,'s in accts */ while (p) { *p = '?'; p = strchr(acct, ','); } /* check for redundant accts */ t = &(e->u.list); while (*t) { if (!strcasecmp(acct, (*t)->extra)) { struct list_type *u; u = *t; *t = (*t)->next; if (u->extra) nfree(u->extra); nfree(u); } else t = &((*t)->next); } *t = user_malloc(sizeof(struct list_type)); (*t)->next = NULL; (*t)->extra = user_malloc(strlen(acct) + 1); strcpy((*t)->extra, acct); } return 1; } static int account_write_userfile(FILE *f, struct userrec *u, struct user_entry *e) { struct list_type *h; for (h = e->u.extra; h; h = h->next) if (fprintf(f, "--ACCOUNT %s\n", h->extra) == EOF) return 0; return 1; } static int account_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e) { struct list_type *h; for (h = e->u.extra; h; h = h->next) set_user(&USERENTRY_ACCOUNT, new, h->extra); return 1; } static int account_tcl_get(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { struct list_type *x; BADARGS(3, 3, " handle ACCOUNT"); for (x = e->u.list; x; x = x->next) Tcl_AppendElement(irp, x->extra); return TCL_OK; } static int account_tcl_set(Tcl_Interp * irp, struct userrec *u, struct user_entry *e, int argc, char **argv) { BADARGS(3, 4, " handle ACCOUNT ?account?"); if (argc == 4) if (!strcmp(argv[3], "")) { Tcl_AppendResult(irp, "Invalid account name", NULL); return TCL_OK; } else { addaccount_by_handle(u->handle, argv[3]); } else addaccount_by_handle(u->handle, "none"); /* drummer */ return TCL_OK; } struct user_entry_type USERENTRY_ACCOUNT = { 0, def_gotshare, account_dupuser, hosts_null, hosts_null, account_write_userfile, hosts_kill, def_get, account_set, account_tcl_get, account_tcl_set, hosts_expmem, account_display, "ACCOUNT", hosts_tcl_append }; int egg_list_append(struct list_type **h, struct list_type *i) { for (; *h; h = &((*h)->next)); *h = i; return 1; } int egg_list_delete(struct list_type **h, struct list_type *i) { for (; *h; h = &((*h)->next)) if (*h == i) { *h = i->next; return 1; } return 0; } int egg_list_contains(struct list_type *h, struct list_type *i) { for (; h; h = h->next) if (h == i) { return 1; } return 0; } int add_entry_type(struct user_entry_type *type) { struct userrec *u; list_insert(&entry_type_list, type); for (u = userlist; u; u = u->next) { struct user_entry *e = find_user_entry(type, u); if (e && e->name) { e->type = type; e->type->unpack(u, e); nfree(e->name); e->name = NULL; } } return 1; } int del_entry_type(struct user_entry_type *type) { struct userrec *u; for (u = userlist; u; u = u->next) { struct user_entry *e = find_user_entry(type, u); if (e && !e->name) { e->type->pack(u, e); e->name = user_malloc(strlen(e->type->name) + 1); strcpy(e->name, e->type->name); e->type = NULL; } } return egg_list_delete((struct list_type **) &entry_type_list, (struct list_type *) type); } struct user_entry_type *find_entry_type(char *name) { struct user_entry_type *p; for (p = entry_type_list; p; p = p->next) { if (!strcasecmp(name, p->name)) return p; } return NULL; } struct user_entry *find_user_entry(struct user_entry_type *et, struct userrec *u) { struct user_entry **e, *t; for (e = &(u->entries); *e; e = &((*e)->next)) { if (((*e)->type == et) || ((*e)->type && !strcasecmp((*e)->type->name, et->name))) { t = *e; *e = t->next; t->next = u->entries; u->entries = t; return t; } } return NULL; } void *get_user(struct user_entry_type *et, struct userrec *u) { struct user_entry *e; if (u && (e = find_user_entry(et, u))) return et->get(u, e); return 0; } int set_user(struct user_entry_type *et, struct userrec *u, void *d) { struct user_entry *e; int r; if (!u || !et) return 0; if (!(e = find_user_entry(et, u))) { e = user_malloc(sizeof(struct user_entry)); e->type = et; e->name = NULL; e->u.list = NULL; list_insert((&(u->entries)), e); } r = et->set(u, e, d); if (!e->u.list) { egg_list_delete((struct list_type **) &(u->entries), (struct list_type *) e); nfree(e); } return r; } eggheads-eggdrop-9c00fe1/src/userrec.c000066400000000000000000000637201524730042700177360ustar00rootroot00000000000000/* * userrec.c -- handles: * add_q() del_q() str2flags() flags2str() str2chflags() chflags2str() * a bunch of functions to find and change user records * change and check user (and channel-specific) flags */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "main.h" #include "modules.h" #include "tandem.h" extern struct dcc_t *dcc; extern struct chanset_t *chanset; extern int default_flags, default_uflags, quiet_save, dcc_total, share_greet, remove_pass; extern char ver[], botnetnick[]; extern time_t now; int noshare = 1; /* don't send out to sharebots */ struct userrec *userlist = NULL; /* user records are stored here */ struct userrec *lastuser = NULL; /* last accessed user record */ maskrec *global_bans = NULL, *global_exempts = NULL, *global_invites = NULL; struct igrec *global_ign = NULL; int cache_hit = 0, cache_miss = 0; /* temporary cache accounting */ int userfile_perm = 0600; /* Userfile permissions * (default rw-------) */ char userfile[121]; /* where the user records are stored */ void *_user_malloc(int size, const char *file, int line) { #ifdef DEBUG_MEM char x[1024]; const char *p; p = strrchr(file, '/'); snprintf(x, sizeof x, "userrec.c:%s", p ? p + 1 : file); return n_malloc(size, x, line); #else return nmalloc(size); #endif } void *_user_realloc(void *ptr, int size, const char *file, int line) { #ifdef DEBUG_MEM char x[1024]; const char *p; p = strrchr(file, '/'); snprintf(x, sizeof x, "userrec.c:%s", p ? p + 1 : file); return n_realloc(ptr, size, x, line); #else return nrealloc(ptr, size); #endif } static int expmem_mask(struct maskrec *m) { int result = 0; for (; m; m = m->next) { result += sizeof(struct maskrec); result += strlen(m->mask) + 1; if (m->user) result += strlen(m->user) + 1; if (m->desc) result += strlen(m->desc) + 1; } return result; } /* Memory we should be using */ int expmem_users() { int tot; struct userrec *u; struct chanuserrec *ch; struct chanset_t *chan; struct user_entry *ue; struct igrec *i; tot = 0; for (u = userlist; u; u = u->next) { for (ch = u->chanrec; ch; ch = ch->next) { tot += sizeof(struct chanuserrec); if (ch->info != NULL) tot += strlen(ch->info) + 1; } tot += sizeof(struct userrec); for (ue = u->entries; ue; ue = ue->next) { tot += sizeof(struct user_entry); if (ue->name) { tot += strlen(ue->name) + 1; tot += list_type_expmem(ue->u.list); } else tot += ue->type->expmem(ue); } } /* Account for each channel's masks */ for (chan = chanset; chan; chan = chan->next) { /* Account for each channel's ban-list user */ tot += expmem_mask(chan->bans); /* Account for each channel's exempt-list user */ tot += expmem_mask(chan->exempts); /* Account for each channel's invite-list user */ tot += expmem_mask(chan->invites); } tot += expmem_mask(global_bans); tot += expmem_mask(global_exempts); tot += expmem_mask(global_invites); for (i = global_ign; i; i = i->next) { tot += sizeof(struct igrec); tot += strlen(i->igmask) + 1; if (i->user) tot += strlen(i->user) + 1; if (i->msg) tot += strlen(i->msg) + 1; } return tot; } int count_users(struct userrec *bu) { int tot = 0; struct userrec *u; for (u = bu; u; u = u->next) tot++; return tot; } /* Shortcut for get_user_by_handle -- might have user record in dccs */ static struct userrec *check_dcclist_hand(char *handle) { int i; for (i = 0; i < dcc_total; i++) if (!strcasecmp(dcc[i].nick, handle)) return dcc[i].user; 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 */ struct userrec *get_user_by_account(char *acct) { struct userrec *u; struct list_type *q; if (!acct || !acct[0] || !strcmp(acct, "*")) return NULL; for (u = userlist; u; u = u->next) { for (q = get_user(&USERENTRY_ACCOUNT, u); q; q = q->next) { if (!rfc_casecmp(q->extra, acct)) { return u; } } } return NULL; } struct userrec *get_user_by_handle(struct userrec *bu, char *handle) { struct userrec *u, *ret; if (!handle) return NULL; /* FIXME: This should be done outside of this function. */ rmspace(handle); if (!handle[0] || (handle[0] == '*')) return NULL; if (bu == userlist) { if (lastuser && !strcasecmp(lastuser->handle, handle)) { cache_hit++; return lastuser; } ret = check_dcclist_hand(handle); if (ret) { cache_hit++; return ret; } cache_miss++; } for (u = bu; u; u = u->next) if (!strcasecmp(u->handle, handle)) { if (bu == userlist) lastuser = u; return u; } return NULL; } struct userrec *get_user_from_member(memberlist *m) { 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) { 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) { goto getuser_done; } } getuser_done: m->user = ret; m->tried_getuser = 1; return ret; } /* 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 *account, char *host) { 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; } /* Fix capitalization, etc */ void correct_handle(char *handle) { struct userrec *u; u = get_user_by_handle(userlist, handle); if (u == NULL || handle == u->handle) return; strcpy(handle, u->handle); } /* This will be useful in a lot of places, much more code re-use so we * endup with a smaller executable bot. */ void clear_masks(maskrec *m) { maskrec *temp = NULL; for (; m; m = temp) { temp = m->next; if (m->mask) nfree(m->mask); if (m->user) nfree(m->user); if (m->desc) nfree(m->desc); nfree(m); } } void clear_userlist(struct userrec *bu) { struct userrec *u, *v; int i; for (u = bu; u; u = v) { v = u->next; freeuser(u); } if (userlist == bu) { struct chanset_t *cst; for (i = 0; i < dcc_total; i++) dcc[i].user = NULL; clear_chanlist(); lastuser = NULL; while (global_ign) delignore(global_ign->igmask); clear_masks(global_bans); clear_masks(global_exempts); clear_masks(global_invites); global_exempts = global_invites = global_bans = NULL; for (cst = chanset; cst; cst = cst->next) { clear_masks(cst->bans); clear_masks(cst->exempts); clear_masks(cst->invites); cst->bans = cst->exempts = cst->invites = NULL; } } /* Remember to set your userlist to NULL after calling this */ } /* Find CLOSEST host match * (if "*!*@*" and "*!*@*clemson.edu" both match, use the latter!) */ struct userrec *get_user_by_host(char *host) { struct userrec *u, *ret = NULL; struct list_type *q; int cnt, i; if (host == NULL) return NULL; rmspace(host); if (!host[0]) return NULL; cnt = 0; cache_miss++; for (u = userlist; u; u = u->next) { q = get_user(&USERENTRY_HOSTS, u); for (; q; q = q->next) { i = match_useraddr(q->extra, host); if (i > cnt) { ret = u; cnt = i; } } } if (ret != NULL) { lastuser = ret; } return ret; } /* Description: checks the password given against the user's password. * Check against the password "-" to find out if a user has no password set. * * If encryption2 module is loaded and PASS2 is set PASS2 is compared; else * PASS. * * Returns: 1 if the password matches for that user; 0 otherwise. Or if we are * checking against the password "-": 1 if the user has no password set; 0 * otherwise. */ int u_pass_match(struct userrec *u, char *pass) { char *cmp = 0, *new, new2[32]; int pass2 = 1; struct user_entry *e; if (!u || !pass) return 0; if (encrypt_pass2) cmp = get_user(&USERENTRY_PASS2, u); if (!cmp) { /* implicit && encrypt_pass, due to eggdrop has at least one encryption module loaded */ cmp = get_user(&USERENTRY_PASS, u); pass2 = 0; } if (pass[0] == '-') { if (!cmp) return 1; return 0; } /* If password is not set in userrecord, or password is not sent */ if (!cmp || !pass[0]) return 0; if (u->flags & USER_BOT) { if (!crypto_verify(cmp, pass)) /* verify successful */ return 1; return 0; } if (strlen(pass) > PASSWORDMAX) pass[PASSWORDMAX] = 0; if (pass2) { new = verify_pass2(pass, cmp); if (new) { /* verify successful */ if (new != cmp) /* reenrypted with new parameters, no need to strcmp() */ set_user(&USERENTRY_PASS2, u, new); return 1; } } else if (encrypt_pass) { encrypt_pass(pass, new2); if (!crypto_verify(cmp, new2)) { /* verify successful */ if (encrypt_pass2) { new = encrypt_pass2(pass); if (new) { set_user(&USERENTRY_PASS2, u, new); if (remove_pass) { /* implicit e->u.extra != NULL */ e = find_user_entry(&USERENTRY_PASS, u); explicit_bzero(e->u.extra, strlen(e->u.extra)); nfree(e->u.extra); e->u.extra = NULL; egg_list_delete((struct list_type **) &(u->entries), (struct list_type *) e); nfree(e); } } } return 1; } } return 0; } int write_user(struct userrec *u, FILE *f, int idx) { char s[181]; struct chanuserrec *ch; struct chanset_t *cst; struct user_entry *ue; struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; fr.global = u->flags; fr.udef_global = u->flags_udef; build_flags(s, &fr, NULL); if (fprintf(f, "%-10s - %-24s\n", u->handle, s) == EOF) return 0; for (ch = u->chanrec; ch; ch = ch->next) { cst = findchan_by_dname(ch->channel); if (cst && ((idx < 0) || channel_shared(cst))) { if (idx >= 0) { fr.match = (FR_CHAN | FR_BOT); get_user_flagrec(dcc[idx].user, &fr, ch->channel); } else fr.chan = BOT_AGGRESSIVE; if ((fr.chan & BOT_AGGRESSIVE) || (fr.bot & BOT_GLOBAL)) { fr.match = FR_CHAN; fr.chan = ch->flags; fr.udef_chan = ch->flags_udef; build_flags(s, &fr, NULL); if (fprintf(f, "! %-20s %" PRId64 " %-10s %s\n", ch->channel, (int64_t) ch->laston, s, (((idx < 0) || share_greet) && ch->info) ? ch->info : "") == EOF) return 0; } } } for (ue = u->entries; ue; ue = ue->next) { if (ue->name) { struct list_type *lt; for (lt = ue->u.list; lt; lt = lt->next) if (fprintf(f, "--%s %s\n", ue->name, lt->extra) == EOF) return 0; } else if (!ue->type->write_userfile(f, u, ue)) return 0; } return 1; } int write_ignores(FILE *f, int idx) { struct igrec *i; char *mask; long expire, added; if (global_ign) if (fprintf(f, IGNORE_NAME " - -\n") == EOF) /* Daemus */ return 0; for (i = global_ign; i; i = i->next) { mask = str_escape(i->igmask, ':', '\\'); expire = i->expire; added = i->added; if (!mask || fprintf(f, "- %s:%s%lu:%s:%lu:%s\n", mask, (i->flags & IGREC_PERM) ? "+" : "", expire, i->user ? i->user : botnetnick, added, i->msg ? i->msg : "") == EOF) { if (mask) nfree(mask); return 0; } nfree(mask); } return 1; } static int sort_compare(struct userrec *a, struct userrec *b) { /* Order by flags, then alphabetically * first bots: +h / +a / +l / other bots * then users: +n / +m / +o / other users * return true if (a > b) */ if (a->flags & b->flags & USER_BOT) { if (~bot_flags(a) & bot_flags(b) & BOT_HUB) return 1; if (bot_flags(a) & ~bot_flags(b) & BOT_HUB) return 0; if (~bot_flags(a) & bot_flags(b) & BOT_ALT) return 1; if (bot_flags(a) & ~bot_flags(b) & BOT_ALT) return 0; if (~bot_flags(a) & bot_flags(b) & BOT_LEAF) return 1; if (bot_flags(a) & ~bot_flags(b) & BOT_LEAF) return 0; } else { if (~a->flags & b->flags & USER_BOT) return 1; if (a->flags & ~b->flags & USER_BOT) return 0; if (~a->flags & b->flags & USER_OWNER) return 1; if (a->flags & ~b->flags & USER_OWNER) return 0; if (~a->flags & b->flags & USER_MASTER) return 1; if (a->flags & ~b->flags & USER_MASTER) return 0; if (~a->flags & b->flags & USER_OP) return 1; if (a->flags & ~b->flags & USER_OP) return 0; if (~a->flags & b->flags & USER_HALFOP) return 1; if (a->flags & ~b->flags & USER_HALFOP) return 0; } return (strcasecmp(a->handle, b->handle) > 0); } static void sort_userlist() { int again; struct userrec *last, *p, *c, *n; again = 1; last = NULL; while ((userlist != last) && (again)) { p = NULL; c = userlist; n = c->next; again = 0; while (n != last) { if (sort_compare(c, n)) { again = 1; c->next = n->next; n->next = c; if (p == NULL) userlist = n; else p->next = n; } p = c; c = n; n = n->next; } last = c; } } /* Rewrite the entire user file. Call USERFILE hook as well, probably * causing the channel file to be rewritten as well. */ void write_userfile(int idx) { FILE *f; char new_userfile[(sizeof userfile) + 4]; /* 4 = strlen("~new") */ char s[26]; struct userrec *u; int ok; if (userlist == NULL) return; /* No point in saving userfile */ egg_snprintf(new_userfile, sizeof new_userfile, "%s~new", userfile); f = fopen(new_userfile, "w"); chmod(new_userfile, userfile_perm); if (f == NULL) { putlog(LOG_MISC, "*", "%s", USERF_ERRWRITE); return; } if (!quiet_save) putlog(LOG_MISC, "*", "%s", USERF_WRITING); sort_userlist(); ctime_r(&now, s); fprintf(f, "#4v: %s -- %s -- written %s", ver, botnetnick, s); ok = 1; /* Add all users except the -t user */ for (u = userlist; u && ok; u = u->next) if (strcasecmp(u->handle, EGG_BG_HANDLE) && !write_user(u, f, idx)) ok = 0; if (!ok || !write_ignores(f, -1) || fflush(f)) { putlog(LOG_MISC, "*", "%s (%s)", USERF_ERRWRITE, strerror(ferror(f))); fclose(f); return; } fclose(f); call_hook(HOOK_USERFILE); movefile(new_userfile, userfile); } void backup_userfile(void) { char s[(sizeof userfile) + 4]; /* 4 = strlen("~bak") */ if (quiet_save < 2) putlog(LOG_MISC, "*", "%s", USERF_BACKUP); egg_snprintf(s, sizeof s, "%s~bak", userfile); copyfile(userfile, s); } int change_handle(struct userrec *u, char *newh) { int i; char s[HANDLEN + 1]; if (!u) return 0; /* Don't allow the -t handle to be changed */ if (!strcasecmp(u->handle, EGG_BG_HANDLE)) return 0; /* Nothing that will confuse the userfile */ if (!newh[1] && strchr(BADHANDCHARS, newh[0])) return 0; check_tcl_nkch(u->handle, newh); /* Yes, even send bot nick changes now: */ if (!noshare && !(u->flags & USER_UNSHARED)) shareout(NULL, "h %s %s\n", u->handle, newh); strlcpy(s, u->handle, sizeof s); strlcpy(u->handle, newh, sizeof u->handle); for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_CHAT || dcc[i].type == &DCC_CHAT_PASS) && !strcasecmp(dcc[i].nick, s)) { strlcpy(dcc[i].nick, newh, sizeof dcc[i].nick); if (dcc[i].type == &DCC_CHAT && dcc[i].u.chat->channel >= 0) { chanout_but(-1, dcc[i].u.chat->channel, "*** Handle change: %s -> %s\n", s, newh); if (dcc[i].u.chat->channel < GLOBAL_CHANS) botnet_send_nkch(i, s); } } return 1; } extern int noxtra; struct userrec *adduser(struct userrec *bu, char *handle, char *host, char *pass, int flags) { struct userrec *u, *x; struct xtra_key *xk; int oldshare = noshare; noshare = 1; u = nmalloc(sizeof *u); /* u->next=bu; bu=u; */ strlcpy(u->handle, handle, sizeof u->handle); u->next = NULL; u->chanrec = NULL; u->entries = NULL; if (flags != USER_DEFAULT) { /* drummer */ u->flags = flags; u->flags_udef = 0; } else { u->flags = default_flags; u->flags_udef = default_uflags; } set_user(&USERENTRY_PASS, u, pass); if (!noxtra) { int l; xk = nmalloc(sizeof *xk); xk->key = nmalloc(8); strcpy(xk->key, "created"); l = snprintf(NULL, 0, "%" PRId64, (int64_t) now); xk->data = nmalloc(l + 1); sprintf(xk->data, "%" PRId64, (int64_t) now); set_user(&USERENTRY_XTRA, u, xk); } /* Strip out commas -- they're illegal */ if (host && host[0]) { char *p; p = strchr(host, ','); while (p != NULL) { *p = '?'; p = strchr(host, ','); } set_user(&USERENTRY_HOSTS, u, host); } else set_user(&USERENTRY_HOSTS, u, "none"); if (bu == userlist) clear_chanlist(); noshare = oldshare; if ((!noshare) && (handle[0] != '*') && (!(flags & USER_UNSHARED)) && (bu == userlist)) { struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; char x[100]; fr.global = u->flags; fr.udef_global = u->flags_udef; build_flags(x, &fr, 0); shareout(NULL, "n %s %s %s %s\n", handle, host && host[0] ? host : "none", pass, x); } if (bu == NULL) bu = u; else { if ((bu == userlist) && (lastuser != NULL)) x = lastuser; else x = bu; while (x->next != NULL) x = x->next; x->next = u; if (bu == userlist) lastuser = u; } return bu; } void freeuser(struct userrec *u) { struct user_entry *ue, *ut; struct chanuserrec *ch, *z; if (u == NULL) return; ch = u->chanrec; while (ch) { z = ch; ch = ch->next; if (z->info != NULL) nfree(z->info); nfree(z); } u->chanrec = NULL; for (ue = u->entries; ue; ue = ut) { ut = ue->next; if (ue->name) { struct list_type *lt, *ltt; for (lt = ue->u.list; lt; lt = ltt) { ltt = lt->next; nfree(lt->extra); nfree(lt); } nfree(ue->name); nfree(ue); } else ue->type->kill(ue); } nfree(u); } int deluser(char *handle) { struct userrec *u = userlist, *prev = NULL; int fnd = 0; while ((u != NULL) && (!fnd)) { if (!strcasecmp(u->handle, handle)) fnd = 1; else { prev = u; u = u->next; } } if (!fnd) return 0; if (prev == NULL) userlist = u->next; else prev->next = u->next; if (!noshare && (handle[0] != '*') && !(u->flags & USER_UNSHARED)) shareout(NULL, "k %s\n", handle); for (fnd = 0; fnd < dcc_total; fnd++) if (dcc[fnd].user == u) dcc[fnd].user = 0; /* Clear any dcc users for this entry, * null is safe-ish */ clear_chanlist(); freeuser(u); lastuser = NULL; return 1; } static int del_host_or_account(char *handle, char *host, int type) { struct userrec *u; struct list_type *q, *qnext, *qprev; struct user_entry *e = NULL; int i = 0; u = get_user_by_handle(userlist, handle); if (!u) return 0; if (type) { q = get_user(&USERENTRY_ACCOUNT, u); } else { q = get_user(&USERENTRY_HOSTS, u); } qprev = q; if (q) { if (!rfc_casecmp(q->extra, host)) { if (type) { e = find_user_entry(&USERENTRY_ACCOUNT, u); } else { e = find_user_entry(&USERENTRY_HOSTS, u); } e->u.extra = q->next; nfree(q->extra); nfree(q); i++; qprev = NULL; q = e->u.extra; } else q = q->next; while (q) { qnext = q->next; if (!rfc_casecmp(q->extra, host)) { if (qprev) qprev->next = q->next; else if (e) { e->u.extra = q->next; qprev = NULL; } nfree(q->extra); nfree(q); i++; } else qprev = q; q = qnext; } } if (!qprev) { if (type) { set_user(&USERENTRY_ACCOUNT, u, "none"); } else { set_user(&USERENTRY_HOSTS, u, "none"); } } if (!noshare && i && !(u->flags & USER_UNSHARED)) shareout(NULL, "-%s %s %s\n", type ? "a" : "h", handle, host); clear_chanlist(); return i; } int delhost_by_handle(char *handle, char *host) { return del_host_or_account(handle, host, 0); } int delaccount_by_handle(char *handle, char *acct) { return del_host_or_account(handle, acct, 1); } static void add_host_or_account(char *handle, char *arg, int type) { struct userrec *u = get_user_by_handle(userlist, handle); if (type) { set_user(&USERENTRY_ACCOUNT, u, arg); } else { set_user(&USERENTRY_HOSTS, u, arg); } if ((!noshare) && !(u->flags & USER_UNSHARED)) { if (u->flags & USER_BOT) { shareout(NULL, "+b%s %s %s\n", type ? "a" : "h", handle, arg); } else { shareout(NULL, "+%s %s %s\n", type ? "a" : "h", handle, arg); } } clear_chanlist(); } void addhost_by_handle(char *handle, char *host) { add_host_or_account(handle, host, 0); } void addaccount_by_handle(char *handle, char *acct) { add_host_or_account(handle, acct, 1); } void touch_laston(struct userrec *u, char *where, time_t timeval) { if (!u) return; if (timeval > 1) { struct laston_info *li = get_user(&USERENTRY_LASTON, u); if (!li) li = nmalloc(sizeof *li); else if (li->lastonplace) nfree(li->lastonplace); li->laston = timeval; if (where) { li->lastonplace = nmalloc(strlen(where) + 1); strcpy(li->lastonplace, where); } else li->lastonplace = NULL; set_user(&USERENTRY_LASTON, u, li); } else if (timeval == 1) set_user(&USERENTRY_LASTON, u, 0); } /* Go through all channel records and try to find a matching * nick. Will return the user's user record if that is known * to the bot. (Fabian) * * Warning: This is unreliable by concept! */ struct userrec *get_user_by_nick(char *nick) { struct chanset_t *chan; memberlist *m; for (chan = chanset; chan; chan = chan->next) { for (m = chan->channel.member; m && m->nick[0]; m = m->next) { if (!rfc_casecmp(nick, m->nick)) { char word[512]; egg_snprintf(word, sizeof word, "%s!%s", m->nick, m->userhost); /* No need to check the return value ourself */ return get_user_by_host(word); } } } /* Sorry, no matches */ return NULL; } void user_del_chan(char *dname) { struct chanuserrec *ch, *och; struct userrec *u; for (u = userlist; u; u = u->next) { ch = u->chanrec; och = NULL; while (ch) { if (!rfc_casecmp(dname, ch->channel)) { if (och) och->next = ch->next; else u->chanrec = ch->next; if (ch->info) nfree(ch->info); nfree(ch); break; } och = ch; ch = ch->next; } } } /* Check if the console flags specified in md are permissible according * to the given flagrec. If the FR_CHAN flag is not set in fr->match, * only global user flags will be considered. * Returns: md with all unallowed flags masked out. */ int check_conflags(struct flag_record *fr, int md) { if (!glob_owner(*fr)) md &= ~(LOG_RAW | LOG_SRVOUT | LOG_BOTNETIN | LOG_BOTNETOUT | LOG_BOTSHRIN | LOG_BOTSHROUT); if (!glob_master(*fr)) { md &= ~(LOG_FILES | LOG_LEV1 | LOG_LEV2 | LOG_LEV3 | LOG_LEV4 | LOG_LEV5 | LOG_LEV6 | LOG_LEV7 | LOG_LEV8 | LOG_DEBUG | LOG_WALL); if ((fr->match & FR_CHAN) && !chan_master(*fr)) md &= ~(LOG_MISC | LOG_CMDS); } if (!glob_botmast(*fr)) md &= ~(LOG_BOTS | LOG_BOTMSG); return md; } eggheads-eggdrop-9c00fe1/src/users.c000066400000000000000000001005561524730042700174260ustar00rootroot00000000000000/* * users.c -- handles: * testing and enforcing ignores * adding and removing ignores * listing ignores * auto-linking bots * sending and receiving a userfile from a bot * listing users ('.whois' and '.match') * reading the user file * * dprintf'ized, 9nov1995 */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" #include "modules.h" #include "tandem.h" #include #include extern struct dcc_t *dcc; extern struct userrec *userlist, *lastuser; extern struct chanset_t *chanset; extern int dcc_total, noshare, remove_pass; extern char botnetnick[]; extern Tcl_Interp *interp; extern time_t now; int ignore_time = 10; /* how many minutes will ignores last? */ /* is this nick!user@host being ignored? */ int match_ignore(char *uhost) { struct igrec *ir; for (ir = global_ign; ir; ir = ir->next) if (match_useraddr(ir->igmask, uhost)) return 1; return 0; } int delignore(char *ign) { int i, j; struct igrec **u; struct igrec *t; char temp[UHOSTLEN]; i = 0; if (!strchr(ign, '!') && (j = atoi(ign))) { for (u = &global_ign, j--; *u && j; u = &((*u)->next), j--); if (*u) { strlcpy(temp, (*u)->igmask, sizeof temp); i = 1; } } else { /* find the matching host, if there is one */ for (u = &global_ign; *u && !i; u = &((*u)->next)) if (!rfc_casecmp(ign, (*u)->igmask)) { strlcpy(temp, ign, sizeof temp); i = 1; break; } } if (i) { if (!noshare) { char *mask = str_escape(temp, ':', '\\'); if (mask) { shareout(NULL, "-i %s\n", mask); nfree(mask); } } nfree((*u)->igmask); if ((*u)->msg) nfree((*u)->msg); if ((*u)->user) nfree((*u)->user); t = *u; *u = (*u)->next; nfree(t); } return i; } void addignore(char *ign, char *from, char *mnote, time_t expire_time) { struct igrec *p = NULL, *l; for (l = global_ign; l; l = l->next) if (!rfc_casecmp(l->igmask, ign)) { p = l; break; } if (p == NULL) { p = user_malloc(sizeof(struct igrec)); p->next = global_ign; global_ign = p; } else { nfree(p->igmask); nfree(p->user); nfree(p->msg); } p->expire = expire_time; p->added = now; p->flags = expire_time ? 0 : IGREC_PERM; p->igmask = user_malloc(strlen(ign) + 1); strcpy(p->igmask, ign); p->user = user_malloc(strlen(from) + 1); strcpy(p->user, from); p->msg = user_malloc(strlen(mnote) + 1); strcpy(p->msg, mnote); if (!noshare) { char *mask = str_escape(ign, ':', '\\'); if (mask) { shareout(NULL, "+i %s %li %c %s %s\n", mask, expire_time - now, (p->flags & IGREC_PERM) ? 'p' : '-', from, mnote); nfree(mask); } } } /* take host entry from ignore list and display it ignore-style */ static void display_ignore(int idx, int number, struct igrec *ignore) { char dates[81], s[41]; if (ignore->added) { daysago(now, ignore->added, s); sprintf(dates, "Started %s", s); } else dates[0] = 0; if (ignore->flags & IGREC_PERM) strcpy(s, "(perm)"); else { char s1[29]; days(ignore->expire, now, s1); sprintf(s, "(expires %s)", s1); } if (number >= 0) dprintf(idx, " [%3d] %s %s\n", number, ignore->igmask, s); else dprintf(idx, "IGNORE: %s %s\n", ignore->igmask, s); if (ignore->msg && ignore->msg[0]) dprintf(idx, " %s: %s\n", ignore->user, ignore->msg); else dprintf(idx, " %s %s\n", MODES_PLACEDBY, ignore->user); if (dates[0]) dprintf(idx, " %s\n", dates); } /* list the ignores and how long they've been active */ void tell_ignores(int idx, char *match) { struct igrec *u = global_ign; int k = 1; if (u == NULL) { dprintf(idx, "No ignores.\n"); return; } dprintf(idx, "%s:\n", IGN_CURRENT); for (; u; u = u->next) { if (match[0]) { if (cmp_usermasks(match, u->igmask) || wild_match(match, u->msg) || wild_match(match, u->user)) display_ignore(idx, k, u); k++; } else display_ignore(idx, k++, u); } } /* check for expired timed-ignores */ void check_expired_ignores() { struct igrec **u = &global_ign; if (!*u) return; while (*u) { if (!((*u)->flags & IGREC_PERM) && (now >= (*u)->expire)) { putlog(LOG_MISC, "*", "%s %s (%s)", IGN_NOLONGER, (*u)->igmask, MISC_EXPIRED); delignore((*u)->igmask); } else u = &((*u)->next); } } /* Channel mask loaded from user file. This function is * add(ban|invite|exempt)_fully merged into one. */ static void addmask_fully(maskrec ** m, char *mask, char *from, char *note, time_t expire_time, int flags, time_t added, time_t last) { maskrec *p = user_malloc(sizeof(maskrec)); p->next = *m; *m = p; p->expire = expire_time; p->added = added; p->lastactive = last; p->flags = flags; p->mask = user_malloc(strlen(mask) + 1); strcpy(p->mask, mask); p->user = user_malloc(strlen(from) + 1); strcpy(p->user, from); p->desc = user_malloc(strlen(note) + 1); strcpy(p->desc, note); } static void restore_chanban(struct chanset_t *chan, char *host) { char *expi, *add, *last, *user, *desc; int flags = 0; expi = strchr_unescape(host, ':', '\\'); if (expi) { if (*expi == '+') { flags |= MASKREC_PERM; expi++; } add = strchr(expi, ':'); if (add) { if (add[-1] == '*') { flags |= MASKREC_STICKY; add[-1] = 0; } else *add = 0; add++; if (*add == '+') { last = strchr(add, ':'); if (last) { *last = 0; last++; user = strchr(last, ':'); if (user) { *user = 0; user++; desc = strchr(user, ':'); if (desc) { *desc = 0; desc++; addmask_fully(chan ? &chan->bans : &global_bans, host, user, desc, atoi(expi), flags, atoi(add), atoi(last)); return; } } } } else { desc = strchr(add, ':'); if (desc) { *desc = 0; desc++; addmask_fully(chan ? &chan->bans : &global_bans, host, add, desc, atoi(expi), flags, now, 0); return; } } } } putlog(LOG_MISC, "*", "*** Malformed banline for %s.", chan ? chan->dname : "global_bans"); } static void restore_chanexempt(struct chanset_t *chan, char *host) { char *expi, *add, *last, *user, *desc; int flags = 0; expi = strchr_unescape(host, ':', '\\'); if (expi) { if (*expi == '+') { flags |= MASKREC_PERM; expi++; } add = strchr(expi, ':'); if (add) { if (add[-1] == '*') { flags |= MASKREC_STICKY; add[-1] = 0; } else *add = 0; add++; if (*add == '+') { last = strchr(add, ':'); if (last) { *last = 0; last++; user = strchr(last, ':'); if (user) { *user = 0; user++; desc = strchr(user, ':'); if (desc) { *desc = 0; desc++; addmask_fully(chan ? &chan->exempts : &global_exempts, host, user, desc, atoi(expi), flags, atoi(add), atoi(last)); return; } } } } else { desc = strchr(add, ':'); if (desc) { *desc = 0; desc++; addmask_fully(chan ? &chan->exempts : &global_exempts, host, add, desc, atoi(expi), flags, now, 0); return; } } } } putlog(LOG_MISC, "*", "*** Malformed exemptline for %s.", chan ? chan->dname : "global_exempts"); } static void restore_chaninvite(struct chanset_t *chan, char *host) { char *expi, *add, *last, *user, *desc; int flags = 0; expi = strchr_unescape(host, ':', '\\'); if (expi) { if (*expi == '+') { flags |= MASKREC_PERM; expi++; } add = strchr(expi, ':'); if (add) { if (add[-1] == '*') { flags |= MASKREC_STICKY; add[-1] = 0; } else *add = 0; add++; if (*add == '+') { last = strchr(add, ':'); if (last) { *last = 0; last++; user = strchr(last, ':'); if (user) { *user = 0; user++; desc = strchr(user, ':'); if (desc) { *desc = 0; desc++; addmask_fully(chan ? &chan->invites : &global_invites, host, user, desc, atoi(expi), flags, atoi(add), atoi(last)); return; } } } } else { desc = strchr(add, ':'); if (desc) { *desc = 0; desc++; addmask_fully(chan ? &chan->invites : &global_invites, host, add, desc, atoi(expi), flags, now, 0); return; } } } } putlog(LOG_MISC, "*", "*** Malformed inviteline for %s.", chan ? chan->dname : "global_invites"); } static void restore_ignore(char *host) { char *expi, *user, *added, *desc; int flags = 0; struct igrec *p; expi = strchr_unescape(host, ':', '\\'); if (expi) { if (*expi == '+') { flags |= IGREC_PERM; expi++; } user = strchr(expi, ':'); if (user) { *user = 0; user++; added = strchr(user, ':'); if (added) { *added = 0; added++; desc = strchr(added, ':'); if (desc) { *desc = 0; desc++; } else desc = NULL; } else { added = "0"; desc = NULL; } p = user_malloc(sizeof(struct igrec)); p->next = global_ign; global_ign = p; p->expire = atoi(expi); p->added = atoi(added); p->flags = flags; p->igmask = user_malloc(strlen(host) + 1); strcpy(p->igmask, host); p->user = user_malloc(strlen(user) + 1); strcpy(p->user, user); if (desc) { p->msg = user_malloc(strlen(desc) + 1); strcpy(p->msg, desc); } else p->msg = NULL; return; } } putlog(LOG_MISC, "*", "*** Malformed ignore line."); } static void tell_user(int idx, struct userrec *u) { char s[81], s1[81], format[81]; int n = 0, p = 0; time_t now2; struct chanuserrec *ch; struct user_entry *ue; struct laston_info *li; struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; fr.global = u->flags; fr.udef_global = u->flags_udef; build_flags(s, &fr, NULL); if (module_find("notes", 0, 0)) { Tcl_SetVar(interp, "_user", u->handle, 0); if (Tcl_VarEval(interp, "notes ", "$_user", NULL) == TCL_OK) n = tcl_resultint(); } li = get_user(&USERENTRY_LASTON, u); if (!li || !li->laston) strcpy(s1, "never"); else { now2 = now - li->laston; if (now2 >= 86400) strftime(s1, 11, "%Y-%m-%d", localtime(&li->laston)); else strftime(s1, 6, "%H:%M", localtime(&li->laston)); } egg_snprintf(format, sizeof format, "%%-%us %%-5s%%5d %%-15s %%s (%%s)\n", HANDLEN); if (!u_pass_match(u, "-")) p = 1; dprintf(idx, format, u->handle, p ? "yes" : "no", n, s, s1, (li && li->lastonplace) ? li->lastonplace : "nowhere"); /* channel flags? */ for (ch = u->chanrec; ch; ch = ch->next) { fr.match = FR_CHAN | FR_GLOBAL; get_user_flagrec(dcc[idx].user, &fr, ch->channel); if (glob_op(fr) || chan_op(fr)) { if (ch->laston == 0L) strcpy(s1, "never"); else { now2 = now - (ch->laston); if (now2 >= 86400) strftime(s1, 11, "%Y-%m-%d", localtime(&ch->laston)); else strftime(s1, 6, "%H:%M", localtime(&ch->laston)); } fr.match = FR_CHAN; fr.chan = ch->flags; fr.udef_chan = ch->flags_udef; build_flags(s, &fr, NULL); egg_snprintf(format, sizeof format, "%%%us %%-18s %%-15s %%s\n", HANDLEN - 9); dprintf(idx, format, " ", ch->channel, s, s1); if (ch->info != NULL) dprintf(idx, " INFO: %s\n", ch->info); } } /* user-defined extra fields */ for (ue = u->entries; ue; ue = ue->next) if (!ue->name && ue->type->display) ue->type->display(idx, ue); } /* show user by ident */ void tell_user_ident(int idx, char *id) { char format[81]; struct userrec *u; u = get_user_by_handle(userlist, id); if (u == NULL) { dprintf(idx, "%s.\n", USERF_NOMATCH); return; } egg_snprintf(format, sizeof format, "%%-%us PASS NOTES FLAGS LAST\n", HANDLEN); dprintf(idx, format, "HANDLE"); tell_user(idx, u); } /* match string: * wildcard to match nickname or hostmasks * +attr to find all with attr */ void tell_users_match(int idx, char *mtch, int start, int limit, char *chname) { char format[81]; struct userrec *u; int fnd = 0, cnt, nomns = 0, flags = 0; struct list_type *q; struct flag_record user, pls, mns; dprintf(idx, "*** %s '%s':\n", MISC_MATCHING, mtch); cnt = 0; egg_snprintf(format, sizeof format, "%%-%us PASS NOTES FLAGS LAST\n", HANDLEN); dprintf(idx, format, "HANDLE"); if (start > 1) dprintf(idx, "(%s %d)\n", MISC_SKIPPING, start - 1); if (strchr("+-&|", *mtch)) { user.match = pls.match = FR_GLOBAL | FR_BOT | FR_CHAN; break_down_flags(mtch, &pls, &mns); mns.match = pls.match ^ (FR_AND | FR_OR); if (!mns.global && !mns.udef_global && !mns.chan && !mns.udef_chan && !mns.bot) { nomns = 1; if (!pls.global && !pls.udef_global && !pls.chan && !pls.udef_chan && !pls.bot) { /* happy now BB you weenie :P */ dprintf(idx, "Unknown flag specified for matching!!\n"); return; } } if (!chname || !chname[0]) chname = dcc[idx].u.chat->con_chan; flags = 1; } for (u = userlist; u; u = u->next) { if (flags) { get_user_flagrec(u, &user, chname); if (flagrec_eq(&pls, &user)) { if (nomns || !flagrec_eq(&mns, &user)) { cnt++; if ((cnt <= limit) && (cnt >= start)) { tell_user(idx, u); } if (cnt == limit + 1) { dprintf(idx, MISC_TRUNCATED, limit); } } } } else if (wild_match(mtch, u->handle)) { cnt++; if ((cnt <= limit) && (cnt >= start)) { tell_user(idx, u); } if (cnt == limit + 1) { dprintf(idx, MISC_TRUNCATED, limit); } } else { fnd = 0; for (q = get_user(&USERENTRY_HOSTS, u); q; q = q->next) { if (wild_match(mtch, q->extra) && !fnd) { cnt++; fnd = 1; if ((cnt <= limit) && (cnt >= start)) { tell_user(idx, u); } if (cnt == limit + 1) { dprintf(idx, MISC_TRUNCATED, limit); } } } } } dprintf(idx, MISC_FOUNDMATCH, cnt, cnt == 1 ? "" : MISC_MATCH_PLURAL); } /* if encryption mod and encryption2 mod is loaded and remove-pass is 1 delete * PASS for each user (and bot) that has PASS2 set */ static void cleanup_pass(void) { struct userrec *u; struct user_entry *e, *p, *p2; if (encrypt_pass && encrypt_pass2) { for (u = userlist; u; u = u->next) { p = NULL; p2 = NULL; for (e = u->entries; e; e = e->next) { if (!strcasecmp(e->type->name, "PASS")) p = e; else if (!strcasecmp(e->type->name, "PASS2")) p2 = e; } if (p && p2) { explicit_bzero(p->u.extra, strlen(p->u.extra)); nfree(p->u.extra); p->u.extra = NULL; egg_list_delete((struct list_type **) &(u->entries), (struct list_type *) p); nfree(p); } } } } /* * tagged lines in the user file: * * OLD: * # (comment) * ; (comment) * - hostmask(s) * + email * * dcc directory * = comment * : info line * . xtra (Tcl) * ! channel-specific * !! global laston * :: channel-specific bans * NEW: * *ban global bans * *ignore global ignores * ::#chan channel bans * - entries in each * begin user entry * --KEY INFO - info on each * NEWER: * % exemptmask(s) * @ Invitemask(s) * *exempt global exempts * *Invite global Invites * && channel-specific exempts * &&#chan channel exempts * $$ channel-specific Invites * $$#chan channel Invites */ int noxtra = 0; int readuserfile(char *file, struct userrec **ret) { char *p, buf[512], lasthand[512], *attr, *pass, *code, s1[512], *s; FILE *f; struct userrec *bu, *u = NULL; struct chanset_t *cst = NULL; int i; char ignored[LOGLINEMAX]; /* putlog() will truncate anything larger anyway */ struct flag_record fr; struct chanuserrec *cr; bu = (*ret); ignored[0] = 0; if (bu == userlist) { clear_chanlist(); lastuser = NULL; global_bans = NULL; global_ign = NULL; global_exempts = NULL; global_invites = NULL; } lasthand[0] = 0; if (!(f = fopen(file, "r"))) { debug2("users: fopen(%s): %s", file, strerror(errno)); return 0; } noshare = noxtra = 1; /* read opening comment */ s = buf; if (fgets(s, 180, f) != NULL) { if (s[0] == '#' && s[1] < '4' && s[1] >= '0' && s[2] == 'v') { fatal(USERF_OLDFMT, 0); } if (s[0] != '#' || s[1] > '4' || s[2] != 'v') fatal(USERF_INVALID, 0); } /* don't check for feof after fgets, skips last line if it has no \n (ie on windows) */ while (!feof(f) && fgets(buf, sizeof buf, f) != NULL) { /* for the sake of git blame/history, I'm not re-indenting the next 255 lines*/ s = buf; if (s[0] != '#' && s[0] != ';' && s[0]) { code = newsplit(&s); rmspace(s); if (!strcmp(code, "-")) { if (!lasthand[0]) continue; /* Skip this entry. */ if (u) { /* only break it down if there a real users */ p = strchr(s, ','); while (p != NULL) { splitc(s1, s, ','); rmspace(s1); if (s1[0]) set_user(&USERENTRY_HOSTS, u, s1); p = strchr(s, ','); } } /* channel bans are never stacked with , */ if (s[0]) { if (lasthand[0] && strchr(CHANMETA, lasthand[0]) != NULL) restore_chanban(cst, s); else if (lasthand[0] == '*') { if (lasthand[1] == 'i') restore_ignore(s); else restore_chanban(NULL, s); } else if (lasthand[0]) set_user(&USERENTRY_HOSTS, u, s); } } else if (!strcmp(code, "%")) { /* exemptmasks */ if (!lasthand[0]) continue; /* Skip this entry. */ if (s[0]) { if (lasthand[0] == '#' || lasthand[0] == '+') restore_chanexempt(cst, s); else if (lasthand[0] == '*') if (lasthand[1] == 'e') restore_chanexempt(NULL, s); } } else if (!strcmp(code, "@")) { /* Invitemasks */ if (!lasthand[0]) continue; /* Skip this entry. */ if (s[0]) { if (lasthand[0] == '#' || lasthand[0] == '+') restore_chaninvite(cst, s); else if (lasthand[0] == '*') if (lasthand[1] == 'I') restore_chaninvite(NULL, s); } } else if (!strcmp(code, "!")) { /* ! #chan laston flags [info] */ char *chname, *st, *fl; if (u) { chname = newsplit(&s); st = newsplit(&s); fl = newsplit(&s); rmspace(s); fr.match = FR_CHAN; break_down_flags(fl, &fr, 0); if (findchan_by_dname(chname)) { for (cr = u->chanrec; cr; cr = cr->next) if (!rfc_casecmp(cr->channel, chname)) break; if (!cr) { cr = (struct chanuserrec *) user_malloc(sizeof(struct chanuserrec)); cr->next = u->chanrec; u->chanrec = cr; strlcpy(cr->channel, chname, sizeof cr->channel); cr->laston = atoi(st); cr->flags = fr.chan; cr->flags_udef = fr.udef_chan; if (s[0]) { cr->info = (char *) user_malloc(strlen(s) + 1); strcpy(cr->info, s); } else cr->info = NULL; } } } } else if (!strncmp(code, "::", 2)) { /* channel-specific bans */ strcpy(lasthand, &code[2]); u = NULL; if (!findchan_by_dname(lasthand)) { strcpy(s1, lasthand); strcat(s1, " "); if (strstr(ignored, s1) == NULL) { strncat(ignored, lasthand, sizeof(ignored) - 1 - strlen(ignored)); strncat(ignored, " ", sizeof(ignored) - 1 - strlen(ignored)); } lasthand[0] = 0; } else { /* Remove all bans for this channel to avoid dupes */ /* NOTE only remove bans for when getting a userfile * from another bot & that channel is shared */ cst = findchan_by_dname(lasthand); if ((*ret == userlist) || channel_shared(cst)) { clear_masks(cst->bans); cst->bans = NULL; } else { /* otherwise ignore any bans for this channel */ cst = NULL; lasthand[0] = 0; } } } else if (!strncmp(code, "&&", 2)) { /* channel-specific exempts */ strcpy(lasthand, &code[2]); u = NULL; if (!findchan_by_dname(lasthand)) { strcpy(s1, lasthand); strcat(s1, " "); if (strstr(ignored, s1) == NULL) { strncat(ignored, lasthand, sizeof(ignored) - 1 - strlen(ignored)); strncat(ignored, " ", sizeof(ignored) - 1 - strlen(ignored)); } lasthand[0] = 0; } else { /* Remove all exempts for this channel to avoid dupes */ /* NOTE only remove exempts for when getting a userfile * from another bot & that channel is shared */ cst = findchan_by_dname(lasthand); if (cst) { if ((*ret == userlist) || channel_shared(cst)) { clear_masks(cst->exempts); cst->exempts = NULL; } else { /* otherwise ignore any exempts for this channel */ cst = NULL; lasthand[0] = 0; } } } } else if (!strncmp(code, "$$", 2)) { /* channel-specific invites */ strcpy(lasthand, &code[2]); u = NULL; if (!findchan_by_dname(lasthand)) { strcpy(s1, lasthand); strcat(s1, " "); if (strstr(ignored, s1) == NULL) { strncat(ignored, lasthand, sizeof(ignored) - 1 - strlen(ignored)); strncat(ignored, " ", sizeof(ignored) - 1 - strlen(ignored)); } lasthand[0] = 0; } else { /* Remove all invites for this channel to avoid dupes */ /* NOTE only remove invites for when getting a userfile * from another bot & that channel is shared */ cst = findchan_by_dname(lasthand); if ((*ret == userlist) || channel_shared(cst)) { clear_masks(cst->invites); cst->invites = NULL; } else { /* otherwise ignore any invites for this channel */ cst = NULL; lasthand[0] = 0; } } } else if (!strncmp(code, "--", 2)) { if (u) { /* new format storage */ struct user_entry *ue; int ok = 0; for (ue = u->entries; ue && !ok; ue = ue->next) if (ue->name && !strcasecmp(code + 2, ue->name)) { struct list_type *list; list = user_malloc(sizeof(struct list_type)); list->next = NULL; list->extra = user_malloc(strlen(s) + 1); strcpy(list->extra, s); egg_list_append((&ue->u.list), list); ok = 1; } if (!ok) { ue = user_malloc(sizeof(struct user_entry)); ue->name = user_malloc(strlen(code) - 1); ue->type = NULL; strcpy(ue->name, code + 2); ue->u.list = user_malloc(sizeof(struct list_type)); ue->u.list->next = NULL; ue->u.list->extra = user_malloc(strlen(s) + 1); strcpy(ue->u.list->extra, s); list_insert((&u->entries), ue); } } } else if (!rfc_casecmp(code, BAN_NAME)) { strcpy(lasthand, code); u = NULL; } else if (!rfc_casecmp(code, IGNORE_NAME)) { strcpy(lasthand, code); u = NULL; } else if (!rfc_casecmp(code, EXEMPT_NAME)) { strcpy(lasthand, code); u = NULL; } else if (!rfc_casecmp(code, INVITE_NAME)) { strcpy(lasthand, code); u = NULL; } else if (code[0] == '*') { lasthand[0] = 0; u = NULL; } else { pass = newsplit(&s); attr = newsplit(&s); rmspace(s); if (!attr[0] || !pass[0]) { putlog(LOG_MISC, "*", "* %s '%s'!", USERF_CORRUPT, code); lasthand[0] = 0; } else { u = get_user_by_handle(bu, code); if (u && !(u->flags & USER_UNSHARED)) { putlog(LOG_MISC, "*", "* %s '%s'!", USERF_DUPE, code); lasthand[0] = 0; u = NULL; } else if (u) { lasthand[0] = 0; u = NULL; } else { fr.match = FR_GLOBAL; break_down_flags(attr, &fr, 0); strlcpy(lasthand, code, sizeof lasthand); cst = NULL; if (strlen(code) > HANDLEN) code[HANDLEN] = 0; if (strlen(pass) > 20) { putlog(LOG_MISC, "*", "* %s '%s'", USERF_BROKEPASS, code); strcpy(pass, "-"); } bu = adduser(bu, code, 0, pass, sanity_check(fr.global &USER_VALID)); u = get_user_by_handle(bu, code); for (i = 0; i < dcc_total; i++) if (!strcasecmp(code, dcc[i].nick)) dcc[i].user = u; u->flags_udef = fr.udef_global; /* if s starts with '/' it's got file info */ } } } } } fclose(f); (*ret) = bu; if (ignored[0]) { putlog(LOG_MISC, "*", "%s %s", USERF_IGNBANS, ignored); } putlog(LOG_MISC, "*", "Userfile loaded, unpacking..."); for (u = bu; u; u = u->next) { struct user_entry *e; if (!(u->flags & USER_BOT) && !strcasecmp(u->handle, botnetnick)) { putlog(LOG_MISC, "*", "(!) I have a user record, but without +b"); /* u->flags |= USER_BOT; */ } for (e = u->entries; e; e = e->next) if (e->name) { struct user_entry_type *uet = find_entry_type(e->name); if (uet) { e->type = uet; uet->unpack(u, e); nfree(e->name); e->name = NULL; } } } noshare = noxtra = 0; /* process the user data *now* */ if (remove_pass) cleanup_pass(); return 1; } /* New methodology - cycle through list 3 times * 1st time scan for +(sbcdejnu)h bots and link if none connected * 2nd time scan for +h bots * 3rd time scan for +a/+h bots */ void autolink_cycle(char *start) { struct userrec *u = userlist, *autc = NULL; static int cycle = 0; int got_hub = 0, got_alt = 0, got_shared = 0, linked, ready = 0, i, bfl; /* don't start a new cycle if some links are still pending */ if (!start) { for (i = 0; i < dcc_total; i++) { if (dcc[i].type == &DCC_BOT_NEW) return; if (dcc[i].type == &DCC_FORK_BOT) return; if ((dcc[i].type == &DCC_DNSWAIT) && (dcc[i].u.dns && (dcc[i].u.dns->type == &DCC_FORK_BOT))) return; } } if (!start) { ready = 1; cycle = 0; } /* new run through the user list */ while (u && !autc) { while (u && !autc) { if (u->flags & USER_BOT && strcasecmp(u->handle, botnetnick)) { /* ignore our own user record */ bfl = bot_flags(u); if (bfl & (BOT_HUB | BOT_ALT)) { linked = 0; for (i = 0; i < dcc_total; i++) { if (dcc[i].user == u) { if (dcc[i].type == &DCC_BOT) linked = 1; if (dcc[i].type == &DCC_BOT_NEW) linked = 1; if (dcc[i].type == &DCC_FORK_BOT) linked = 1; } } if ((bfl & BOT_HUB) && (bfl & BOT_SHARE)) { if (linked) got_shared = 1; else if (!cycle && ready && !autc) autc = u; } else if ((bfl & BOT_HUB) && cycle > 0) { if (linked) got_hub = 1; else if ((cycle == 1) && ready && !autc) autc = u; } else if ((bfl & BOT_ALT) && (cycle == 2)) { if (linked) got_alt = 1; else if (!in_chain(u->handle) && ready && !autc) autc = u; } /* did we make it where we're supposed to start? yay! */ if (!ready) if (!strcasecmp(u->handle, start)) { ready = 1; autc = NULL; /* if starting point is a +h bot, must be in 2nd cycle */ if ((bfl & BOT_HUB) && !(bfl & BOT_SHARE)) { cycle = 1; } /* if starting point is a +a bot, must be in 3rd cycle */ if (bfl & BOT_ALT) { cycle = 2; } } } if (!cycle && (bfl & BOT_REJECT) && in_chain(u->handle)) { /* get rid of nasty reject bot */ int i; i = nextbot(u->handle); if ((i >= 0) && !strcasecmp(dcc[i].nick, u->handle)) { char *p = MISC_REJECTED; /* we're directly connected to the offending bot?! (shudder!) */ putlog(LOG_BOTS, "*", "%s bot %s.", p, dcc[i].nick); botnet_send_unlinked(i, dcc[i].nick, p); dprintf(i, "bye %s\n", BOT_REJECTING); killsock(dcc[i].sock); lostdcc(i); } else if ((i < 0) && strcasecmp(botnetnick, u->handle)) { /* The bot is not connected, but listed in our tandem list! */ putlog(LOG_BOTS, "*", "(!) BUG: rejecting not connected bot %s!", u->handle); rembot(u->handle); } } } u = u->next; } if (!autc) { if (!cycle && !got_shared) { cycle++; u = userlist; } else if ((cycle == 1) && !(got_shared || got_hub)) { cycle++; u = userlist; } } } if (got_shared && !cycle) autc = NULL; else if ((got_shared || got_hub) && (cycle == 1)) autc = NULL; else if ((got_shared || got_hub || got_alt) && (cycle == 2)) autc = NULL; if (autc) botlink("", -3, autc->handle); /* try autoconnect */ } char *traced_remove_pass(ClientData cd, Tcl_Interp *irp, EGG_CONST char *name1, EGG_CONST char *name2, int flags) { const char *value; value = Tcl_GetVar2(irp, name1, name2, TCL_GLOBAL_ONLY); if (value[0] == '1' && value[1] == '\0') { cleanup_pass(); Tcl_UntraceVar(interp, "remove-pass", TCL_GLOBAL_ONLY|TCL_TRACE_WRITES, traced_remove_pass, NULL); } return NULL; } eggheads-eggdrop-9c00fe1/src/users.h000066400000000000000000000142171524730042700174310ustar00rootroot00000000000000/* * users.h * structures and definitions used by users.c and userrec.c */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _EGG_USERS_H #define _EGG_USERS_H /* List functions :) , next *must* be the 1st item in the struct */ struct list_type { struct list_type *next; char *extra; }; #define list_insert(a,b) { \ (b)->next = *(a); \ *(a) = (b); \ } int egg_list_append(struct list_type **, struct list_type *); int egg_list_delete(struct list_type **, struct list_type *); int egg_list_contains(struct list_type *, struct list_type *); /* New userfile format stuff */ struct userrec; struct user_entry; struct user_entry_type { struct user_entry_type *next; int (*got_share) (struct userrec *, struct user_entry *, char *, int); int (*dup_user) (struct userrec *, struct userrec *, struct user_entry *); int (*unpack) (struct userrec *, struct user_entry *); int (*pack) (struct userrec *, struct user_entry *); int (*write_userfile) (FILE *, struct userrec *, struct user_entry *); int (*kill) (struct user_entry *); void *(*get) (struct userrec *, struct user_entry *); int (*set) (struct userrec *, struct user_entry *, void *); int (*tcl_get) (Tcl_Interp *, struct userrec *, struct user_entry *, int, char **); int (*tcl_set) (Tcl_Interp *, struct userrec *, struct user_entry *, int, char **); int (*expmem) (struct user_entry *); void (*display) (int idx, struct user_entry *); char *name; int (*tcl_append) (Tcl_Interp *, struct userrec *, struct user_entry *); }; #ifndef MAKING_MODS extern struct user_entry_type USERENTRY_COMMENT, USERENTRY_LASTON, USERENTRY_XTRA, USERENTRY_INFO, USERENTRY_BOTADDR, USERENTRY_HOSTS, USERENTRY_PASS, USERENTRY_PASS2, USERENTRY_BOTFL, USERENTRY_ACCOUNT; #ifdef TLS extern struct user_entry_type USERENTRY_FPRINT; #endif #endif struct laston_info { time_t laston; char *lastonplace; }; struct bot_addr { int telnet_port; int relay_port; char *address; #ifdef TLS # define TLS_BOT 1 # define TLS_RELAY 2 int ssl; #endif }; struct user_entry { struct user_entry *next; struct user_entry_type *type; union { char *string; void *extra; struct list_type *list; unsigned long ulong; } u; char *name; }; struct xtra_key { struct xtra_key *next; char *key; char *data; }; struct filesys_stats { int uploads; int upload_ks; int dnloads; int dnload_ks; }; void *_user_malloc(int size, const char *file, int line); void *_user_realloc(void *ptr, int size, const char *file, int line); #ifndef MAKING_MODS # define user_malloc(x) _user_malloc(x, __FILE__, __LINE__) # define user_realloc(x, y) _user_realloc(x, y, __FILE__, __LINE__) #endif int add_entry_type(struct user_entry_type *); int del_entry_type(struct user_entry_type *); struct user_entry_type *find_entry_type(char *); struct user_entry *find_user_entry(struct user_entry_type *, struct userrec *); void *get_user(struct user_entry_type *, struct userrec *); int set_user(struct user_entry_type *, struct userrec *, void *); #define bot_flags(u) ((long)get_user(&USERENTRY_BOTFL, (u))) #define is_bot(u) ((u) && ((u)->flags & USER_BOT)) #define is_owner(u) ((u) && ((u)->flags & USER_OWNER)) /* Fake users used to store ignores and bans */ #define IGNORE_NAME "*ignore" #define BAN_NAME "*ban" #define EXEMPT_NAME "*exempt" #define INVITE_NAME "*Invite" /* Channel-specific info */ struct chanuserrec { struct chanuserrec *next; char channel[CHANNELLEN + 1]; time_t laston; unsigned long flags; unsigned long flags_udef; char *info; }; /* New-style userlist */ struct userrec { struct userrec *next; char handle[HANDLEN + 1]; unsigned long flags; unsigned long flags_udef; struct chanuserrec *chanrec; struct user_entry *entries; }; struct igrec { struct igrec *next; char *igmask; time_t expire; char *user; time_t added; char *msg; int flags; }; extern struct igrec *global_ign; #define IGREC_PERM 2 /* * Note: Flags are in eggdrop.h */ struct userrec *get_user_by_handle(struct userrec *, char *); 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 */ int def_unpack(struct userrec *u, struct user_entry *e); int def_pack(struct userrec *u, struct user_entry *e); int def_kill(struct user_entry *e); int def_write_userfile(FILE *f, struct userrec *u, struct user_entry *e); void *def_get(struct userrec *u, struct user_entry *e); int def_set(struct userrec *u, struct user_entry *e, void *buf); int def_gotshare(struct userrec *u, struct user_entry *e, char *data, int idx); int def_tcl_get(Tcl_Interp *interp, struct userrec *u, struct user_entry *e, int argc, char **argv); int def_tcl_set(Tcl_Interp *irp, struct userrec *u, struct user_entry *e, int argc, char **argv); int def_expmem(struct user_entry *e); void def_display(int idx, struct user_entry *e); int def_dupuser(struct userrec *new, struct userrec *old, struct user_entry *e); #endif /* _EGG_USERS_H */ eggheads-eggdrop-9c00fe1/src/version.h000066400000000000000000000021531524730042700177510ustar00rootroot00000000000000/* * version.h * include file controlling version strings used by Eggdrop * * should contain something similar to only the following 3 lines: * #define EGG_STRINGVER "1.8.0" * #define EGG_NUMVER 1080000 * #define EGG_PATCH "patchstring" */ /* * Copyright (C) 1997 Robey Pointer * Copyright (C) 1999 - 2025 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define EGG_STRINGVER "1.10.2" #define EGG_NUMVER 1100203 eggheads-eggdrop-9c00fe1/ssl.conf000066400000000000000000000033361524730042700170000ustar00rootroot00000000000000# # OpenSSL configuration file for use with Eggdrop # Don't edit it unless you know what you're doing. # #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] default_days = 365 # how long to certify for default_crl_days = 30 # how long before next CRL default_md = sha1 # which md to use. #################################################################### [ req ] default_bits = 4096 default_keyfile = eggdrop.key distinguished_name = req_dn req_extensions = v3_req serial = 1337 # value between 0 and 9999 [ req_dn ] countryName = Country Name (2 letter code) countryName_default = EU countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) localityName = Locality Name (eg, city) organizationName = Organization Name (eg, company) organizationName_default = Eggheads organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = Botnet commonName = Common Name (typically your domain name) commonName_max = 128 emailAddress = Your Email Address emailAddress_max = 64 userId = Botnet Nick userId_max = 32 [ v3_req ] basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] # You should specify here all possible botaddrs. To avoid confusion, # this is what you would use in .+bot or .chaddr. # Addresses could be hostnames or IPv4/v6 addresses. DNS.1 = #DNS.2 = this.bot.net #DNS.3 = lamestbot.net #DNS.4 = ... #IP.1 = 99.99.0.0 #IP.2 = 2001:db8:618:5c0:263:: eggheads-eggdrop-9c00fe1/tests/000077500000000000000000000000001524730042700164655ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/tests/.gitignore000066400000000000000000000000511524730042700204510ustar00rootroot00000000000000.venv/ __pycache__/ *.pyc .pytest_cache/ eggheads-eggdrop-9c00fe1/tests/README.md000066400000000000000000000630561524730042700177560ustar00rootroot00000000000000# Eggdrop integration tests Pytest harness for spawning the real Eggdrop binary against a mock IRCd and asserting on its internal state. Lives outside `src/` so it adds zero risk to the bot itself: nothing here changes Eggdrop's source. ## Quick start The fastest way from a fresh clone to a green suite, with coverage instrumentation enabled: ```sh make test # distclean, configure --enable-coverage, # make config, make debug, pytest ``` The `test` target lives in the top-level `Makefile`. It rebuilds from scratch every time, so it's the right entry point for CI or for verifying a clean state. For a faster inner-loop while developing tests, build once and re-run pytest directly: ```sh make # build eggdrop in the repo root cd tests uv sync # set up .venv from pyproject.toml (one-time) uv run pytest # run the suite uv run pytest -v -k partyline # run a subset ``` `uv tool run ruff check .` and `uv tool run ty check .` should both report clean. CI runs them. ## Architectural approach ```mermaid flowchart LR test["pytest
test fn"] subgraph harness ["Python harness fixtures"] direction TB mock["mock_ircd
(asyncio TCP, sync facade)"] bridge["tcl_bridge
(TCP client, framing.py)"] end subgraph tmpdir ["per-test tmpdir"] files[("eggdrop.conf
eggdrop.user
bridge.port
eggdrop.log
eggdrop.stdout.log")] end subgraph egg ["eggdrop subprocess (-n / -nt)"] bot["eggdrop binary"] ebridge["test_bridge.tcl
(sourced from eggdrop.conf)"] bot -. "sources at startup" .-> ebridge end test --> mock test --> bridge test -. "render (jinja2)" .-> files bot -. "writes at startup
+ stdout drain" .-> files mock <-->|"IRC over TCP"| bot bridge <-->|"Tcl eval (TCP framed)"| ebridge test -- "send_partyline()
via stdin (-nt only)" --> bot ``` Three loops in play: 1. **Mock IRCd** speaks RFC 1459 + IRCv3 CAP just well enough for Eggdrop to register, join, and exchange messages. Async internally; sync API for tests (`mock_ircd.recv()`, `.send_welcome()`, `.expect_recv_match()`). 2. **Eggdrop subprocess** runs unmodified. Its `eggdrop.conf` is rendered per test from `templates/eggdrop.conf.j2`, points at the mock IRCd's port, and sources `support/test_bridge.tcl` when `EGGDROP_TEST=1` is set. 3. **Tcl bridge** is a tiny `socket -server` listener inside Eggdrop that takes line-delimited Tcl commands and returns escaped results. The Python client (`BridgeClient.eval_ok("...")`) lets a test inspect any internal state reachable from the Tcl interpreter — channels, users, settings, bind tables, raw variables. Tests assert on **state** (via the bridge) rather than on text scraped from logs or partyline output, which is more robust. Logs (`eggdrop.log`, `eggdrop.stdout.log`) survive in the tmpdir for debugging and are attached to pytest's failure report. ### Why the bridge instead of `.tcl` over partyline? The `.tcl` partyline command works but interleaves results with log lines and mangles multi-line output through `dumplots`. The bridge is a separate unbuffered channel with explicit framing and no dependence on the partyline prompt cycle, so introspection is reliable and concurrent with whatever the partyline is doing. ### Why a real subprocess instead of linking Eggdrop as a library? To keep changes to Eggdrop at zero. Library-ification of a process built around `main()` and lots of process-lifetime globals (interp, dcc table, userlist, channels, modules, signal handlers, OpenSSL, dns child, …) is a large refactor. Subprocess + bridge gets us regression coverage today and keeps the door open for a future shared-lib path if it's ever justified. ## Quick start: a test that drives the IRCd and a partyline command This is `tests/test_partyline_chan.py` (also runnable as `uv run pytest -k partyline_add`): ```python import pytest from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.irc_helpers import drive_join_with_names, drive_registration from support.mock_ircd import MockIrcd from support.waiters import wait_for @pytest.mark.partyline # ← spawns eggdrop with -nt def test_partyline_add_channel( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: drive_registration(mock_ircd) # NICK + USER → welcome drive_join_with_names(mock_ircd, "@TestBot") # JOIN echo + NAMES + WHO + ... assert tcl_bridge.eval_ok("llength [channels]") == "1" eggdrop_proc.send_partyline(".+chan #pytest") wait_for( lambda: tcl_bridge.eval_ok( 'expr {[lsearch [channels] "#pytest"] >= 0}' ) == "1", timeout=5.0, description="partyline .+chan #pytest to register", ) ``` What this exercises: 1. **IRCd dialogue**: TCP connect → CAP/NICK/USER (auto-handled) → welcome → JOIN → NAMES → bot's post-join MODE/WHO queries serviced. The mock IRCd auto-PONGs and auto-handles `CAP LS`; the helpers drive the rest. 2. **Partyline command**: `eggdrop_proc.send_partyline(".+chan #pytest")` writes to Eggdrop's stdin. The `@pytest.mark.partyline` marker tells the `eggdrop_proc` fixture to spawn with `-nt` instead of `-n`, opening the HQ partyline on stdin. The HQ user (`-HQ`) gets full owner perms automatically — no auth handshake. 3. **State assertion via bridge**: `wait_for(...)` polls `tcl_bridge.eval_ok(...)` until the Tcl side reports the new channel. Polling is needed because `.+chan` runs through Eggdrop's event loop asynchronously from the stdin write; `wait_for` has an explicit timeout instead of `time.sleep()`. ## Helpers Shared utilities for things tests would otherwise hand-roll. Each lives in its own module under `support/`; add a new module here (and a new subsection below) when something doesn't fit an existing one. ### IRC dialogue (`support/irc_helpers.py`) Shared multi-step IRC interactions so tests don't repeat boilerplate. #### `drive_registration(mock_ircd, nick="TestBot", isupport_tokens=None)` Drives Eggdrop through IRC registration: 1. Waits for the bot's TCP connect. 2. Drains the bot's `NICK` and `USER` (in either order). 3. Sends the welcome sequence (001-004, optional 005 with `isupport_tokens`, 376 end-of-MOTD). `isupport_tokens` is a list of raw `KEY=VALUE` (or bare `KEY`) strings that go into a single 005 line. Use this to test parsing of specific tokens, e.g.: ```python drive_registration(mock_ircd, isupport_tokens=[ "PREFIX=(qaohv)~&@%+", "CHANMODES=beI,kLf,l,psmntirzMQNRTOVKDdGPZSCc", ]) ``` To influence which IRCv3 caps the bot negotiates, override the `mock_ircd` fixture for the test and construct the IRCd with the cap list. The bot sends `CAP LS 302` the moment TCP connects, before the test body runs, so the cap list has to be set at construction time: ```python @pytest.fixture def mock_ircd(): ircd = MockIrcd(advertised_caps=["account-tag"]).start() try: yield ircd finally: with contextlib.suppress(Exception): ircd.stop() def test_account_tag_negotiated(eggdrop_proc, mock_ircd, tcl_bridge): drive_registration(mock_ircd) assert "account-tag" in tcl_bridge.eval_ok("cap enabled").split() ``` The bot only `REQ`s caps it has enabled in config (e.g. `account-tag` is opt-in via `set account-tag 1` in the rendered eggdrop.conf — pass `extra_tcl="set account-tag 1\n"` to `eggdrop_config.render`). After this returns, Eggdrop has processed 005 and is about to JOIN configured channels. #### `drive_join_with_names(mock_ircd, members_with_prefix, nick="TestBot", server="mock.test", member_accounts=None) -> str` Mimics a real IRCd's full post-JOIN dance for the bot: 1. Waits for the bot's `JOIN #chan`. 2. Echoes `:nick!u@h JOIN :#chan` back so Eggdrop populates `chan->name` and considers itself joined. 3. Sends `353` NAMES with `members_with_prefix` (a NAMES-style string like `"@TestBot ~bigboss +regular"`) and `366` end-of-NAMES. 4. Drains the post-join queries Eggdrop fires off: - `MODE +b/+e/+I` → empty `368/349/347` end-of-list replies - `WHO #chan ...` → if the bot sent a WHOX-style request (the `c%chnufat,222` form, used when `WHOX` ISUPPORT is on), reply with one `354` per member carrying the per-member account from `member_accounts` (default `*` = not logged in). Otherwise reply with one `352` per member (prefix symbols passed through to the WHO flags field, so `opchars`-based op detection picks them up). Either form ends with `315`. 5. Leaves `MODE #chan` (no list flag) **unanswered** so individual tests can send their own `324` mode reply if they need to. `member_accounts` is a `dict[str, str]` mapping member nick → account name. Only consulted on the WHOX path; ignored for plain WHO. Returns the channel name. Quiesces when no new lines arrive for ~300 ms (or after a 5 s hard cap). ```python chan = drive_join_with_names(mock_ircd, "@TestBot alice +bob") # bot is now fully joined to chan; alice is a plain member, bob is voiced mock_ircd.send(f":mock.test 324 TestBot {chan} +ntk secret") # custom 324 # WHOX flavour (bot is on a network that advertised WHOX in 005): chan = drive_join_with_names( mock_ircd, "@op alice", member_accounts={"op": "op"} ) # op's account is now "op" via 354 → got354 → setaccount ``` #### `wait_for_isupport(bridge, key, expected, timeout=5.0)` Polls `isupport get ` over the bridge until it returns `expected`. Useful right after `drive_registration(..., isupport_tokens=...)` to ensure Eggdrop has finished processing 005 before assertions run. ```python wait_for_isupport(tcl_bridge, "PREFIX", "(qaohv)~&@%+") ``` #### `split_member_prefix(token) -> (nick, prefix_symbols)` Tiny helper used internally by `drive_join_with_names`; exposed for test code that needs to do the same parsing. `"@alice"` → `("alice", "@")`, `"~&boss"` → `("boss", "~&")`, `"plain"` → `("plain", "")`. ### Ident responder (`support/identd.py`) Test-time RFC 1413 ident server, bound to `127.0.0.1:1113`. Used by tests that exercise the inbound DCC/telnet ident lookup path in `src/dcc.c:dcc_telnet_hostresolved2`. Eggdrop normally queries TCP/113, which is privileged; when the spawn env has `EGGDROP_TEST=1` the bot connects to `1113` instead (see the lazy `getenv` in `dcc_telnet_hostresolved2`), so the test process doesn't need root or `CAP_NET_BIND_SERVICE`. ```python from support.identd import IdentServer # Reply with USERID — exercises the happy-path parse in dcc_ident. with IdentServer("respond", user="alice"): ... # open inbound DCC; bot resolves host as alice@ # Accept then hold; eggdrop's identtimeout eventually fires. with IdentServer("timeout"): ... # bot resolves host as telnet@ after identtimeout ``` To exercise the **connection-refused** path, just *don't* construct one — with nothing listening on 1113 the kernel returns RST, and the bot resolves the host as `telnet@` immediately. See `tests/test_ident_scenarios.py` for the full three-modes × two-timeouts matrix. Caveat: eggdrop's `check_expired_dcc` only runs every 10s (`src/main.c:556`), so the effective wait for the timeout case is `identtimeout + up-to-10s`, not exactly `identtimeout`. Tests waiting on the ident timeout need a poll window of roughly `identtimeout + 12s`. ## Fixtures | Fixture | Scope | What you get | | --- | --- | --- | | `tmp_eggdir` | function | `Path` to per-test scratch dir (alias of `tmp_path`) | | `mock_ircd` | function | Started `MockIrcd` listening on `127.0.0.1:0` | | `eggdrop_config` | function | `EggdropConfig` with `.render(**overrides)` to customise the conf | | `eggdrop_proc` | function | Spawned `EggdropProc`. `-nt` if test has `@pytest.mark.partyline` | | `tcl_bridge` | function | Connected `BridgeClient` ready for `.eval_ok("...")` | | `_process_tracker` | session, autouse | Backstop kill of any leaked eggdrop pids | The fixtures wire to each other: pulling in `tcl_bridge` is enough — it depends on `eggdrop_proc` which depends on `eggdrop_config` and `mock_ircd`, all of which depend on `tmp_eggdir`. ### Markers | Marker | Effect | | --- | --- | | `@pytest.mark.partyline` | `eggdrop_proc` spawns with `-nt`; HQ partyline available on stdin | | `@pytest.mark.slow` | Tag for end-to-end / reconnect / timeout-driven tests | ## Customising a test's eggdrop.conf Render with overrides before the proc starts: ```python def test_with_custom_nick(eggdrop_config, eggdrop_proc, mock_ircd, tcl_bridge): eggdrop_config.render( nick="OtherBot", channels=[ {"name": "#a", "chanmode": "+nt"}, {"name": "#b", "chanmode": "+ntk", "key": "secret"}, ], extra_tcl="set my-test-var 42\n", ) # ...rest of the test ``` If you don't call `render()`, the `eggdrop_proc` fixture renders with defaults from `EggdropConfig.context()`. `render()` only takes effect *before* the proc fixture evaluates. If your test parameter list pulls in `eggdrop_proc` directly, the proc has already spawned by the time the test body runs. To customise after the dataclass exists but before the bot starts, take `eggdrop_config` + `request: pytest.FixtureRequest`, render, then lazy-load the rest: ```python def test_loads_my_userfile(eggdrop_config, request: pytest.FixtureRequest): eggdrop_config.render(...) eggdrop_config.userfile_path.write_text(...) # tweak files post-render proc = request.getfixturevalue("eggdrop_proc") bridge = request.getfixturevalue("tcl_bridge") ``` ### Pre-populating the userfile Two template variables (rendered by `templates/userfile.j2`) inject already-formatted ban-record lines into the userfile that's written before the bot starts: - `userfile_ban_lines` — list of strings, written under `*ban - -`. - `userfile_chan_ban_lines` — dict of `chan-name → list of strings`, each list written under `:: bans`. The channel must be configured before the userfile is read; the default eggdrop.conf `channels` list handles this for `#test` automatically (see `chanprog.c:452` for the order: conf load → HOOK_REHASH → `readuserfile`). Build the strings with `support.userfile_helpers.format_userfile_ban`, which takes every field as a required keyword argument (`mask`, `perm`, `sticky`, `expire`, `added`, `lastactive`, `creator`, `desc`) and hex-escapes `:` / `\\` in the mask per `src/misc.c:str_escape`. The template itself is a flat iteration; all formatting lives in Python. ```python from support.userfile_helpers import format_userfile_ban eggdrop_config.render( userfile_ban_lines=[ format_userfile_ban( mask="a:storedacct", perm=True, sticky=False, expire=0, added=1700000000, lastactive=0, creator="owner", desc="from disk", ), ], userfile_chan_ban_lines={ "#test": [ format_userfile_ban( mask="~a:chanonlyacct", perm=True, sticky=False, expire=0, added=1700000000, lastactive=0, creator="owner", desc="per-chan", ), ], }, ) ``` The chanfile is rendered from `templates/chanfile.j2`. Pass `chanfile_channels=[{"name": "#chan", "options": "..."}]` if you need to register channels at chanfile-load time (rather than via `channels` in the conf). Default is empty — most tests use the conf-level `channel add` instead. ### Selecting which modules to load The `modules` template variable controls the `loadmodule` lines and gates the server-related conf block (`set net-type`, `server add`, `set msg-rate`, ...) on whether `server` is in the list. Default is the full chain needed for IRC behaviour: `["pbkdf2", "channels", "server", "ctcp", "irc", "console", "notes"]`. Override to test channels-mod-only scenarios (e.g. behaviour when server.mod is absent — irc.mod and ctcp.mod will fail to load alongside since they `module_depend` on server): ```python eggdrop_config.render(modules=["pbkdf2", "channels", "console", "notes"]) ``` `extra_modules` still appends in addition to `modules`, so it's the right knob for opting into share/transfer/etc. without changing the base list. ## Layout ``` tests/ ├── pyproject.toml # uv project, pytest+ruff+ty config ├── README.md # this file ├── conftest.py # all fixtures + failure-report hook ├── support/ │ ├── framing.py # line-delimited \-escaped frame format │ ├── test_bridge.tcl # sourced inside Eggdrop, opens TCP listener │ ├── bridge_client.py # Python client → eval_ok("...") │ ├── mock_ircd.py # asyncio IRCd, sync facade │ ├── irc_helpers.py # drive_registration, drive_join_with_names, ... │ ├── identd.py # IdentServer — RFC 1413 responder on 127.0.0.1:1113 │ ├── userfile_helpers.py # format_userfile_ban (escaping per src/misc.c:str_escape) │ ├── eggdrop_proc.py # subprocess wrapper, stdout drain, terminate │ └── waiters.py # wait_for / wait_for_file / wait_for_log_match ├── templates/ │ ├── eggdrop.conf.j2 │ ├── userfile.j2 │ └── chanfile.j2 └── tests/ ├── test_framing.py ├── test_smoke_connect.py ├── test_partyline_chan.py ├── test_isupport_modes.py ├── test_ident_scenarios.py # ident lookup refused / timeout / success ├── test_tcl_passwdok.py # ported from eggdrop_tcl_passwdok.bats ├── test_tcl_iscmds.py # ported from eggdrop_tcl_iscmds.bats ├── test_tcl_matchattr.py # ported from eggdrop_tcl_matchattr.bats ├── test_tcl_server.py # ported from eggdrop_tcl_server.bats ├── test_tcl_addbot.py # ported from eggdrop_tcl_addbot.bats └── test_chanset_inputvalidation.py # ported from eggdrop_chanset_inputvalidation.bats ``` ## Bridge wire protocol Telnet-friendly, one frame per line, `\` `\n` `\r` backslash-escaped: ``` $ nc 127.0.0.1 set ::nick OK TestBot expr 2 + 2 OK 4 nosuch ERR invalid command name "nosuch" ``` Request: `\n`. Response: `OK \n` or `ERR \n`. The bridge only listens when the spawn env has `EGGDROP_TEST=1`, so production Eggdrops sourcing the same config skip it. ## Environment knobs - `EGGDROP_BIN` — absolute path to the eggdrop binary. Default: `/eggdrop`. - `EGGDROP_SRC` — absolute path to the eggdrop source tree (for `mod-path`, `help-path`, `EGG_LANGDIR`). Default: parent of `tests/`. ## Debugging a failing test - Each test's runtime files survive at `/tmp/pytest-of-/pytest-current//`. pytest preserves the last 3 sessions automatically. - Two logs are written: - `eggdrop.log` — Eggdrop's own log (raw IRC `[@]` incoming, `[m->]`/`[s->]` outgoing, plus messages, channels, output, file ops). - `eggdrop.stdout.log` — everything Eggdrop wrote to stdout/stderr. - On failure, both are attached to the pytest report. - For verbose live output: `uv run pytest -s --log-cli-level=DEBUG path::to::test`. - To poke the bridge by hand from a hung test, copy the port out of `/bridge.port` and `nc 127.0.0.1 `. ## Coverage (gcov / lcov) Eggdrop's `configure` script has an `--enable-coverage` flag (added for this test harness, see `aclocal.m4` → `EGG_ENABLE_COVERAGE`). It injects `--coverage -fPIC -O0 -ggdb3` into `CFLAGS` and `--coverage` into `LDFLAGS`, so the build emits `.gcno` notes alongside every `.o`, and each spawned process drops `.gcda` runtime data when it exits. ### Build with coverage The top-level `make test` target does the full build + run cycle: ```sh make test # = distclean → configure --enable-coverage # → make config → make debug → pytest ``` Or do it by hand if you want to control the steps: ```sh make distclean ./configure --enable-coverage make config make debug ``` `config.log` will note `enabling gcov coverage instrumentation: --coverage -fPIC -O0 -ggdb3` and `src/Makefile` will have the flags wired into `CFLAGS`/`LDFLAGS`. After `make debug`, every `.o` has a `.gcno` next to it in the same directory. ### Run the suite to populate `.gcda` ```sh cd tests uv run pytest # each spawned Eggdrop writes .gcda on exit ``` `.gcda` files land beside the corresponding `.gcno`/`.o`. One full suite run produces ~30+ `.gcda` files across `src/` and `src/mod/*/`. To reset between runs (without rebuilding) just delete the data: ```sh find . -name '*.gcda' -delete # keeps .gcno (build state) in place ``` `make clean` removes both `.gcno` and `.gcda` everywhere as a side effect; you'd then need to rebuild before the next coverage run. ### Inspect coverage Per-file: ```sh gcov -o src/mod/server.mod src/mod/server.mod/server.c # → server.c.gcov; first line reports "Lines executed: NN.NN% of N" ``` Whole-tree HTML with `lcov`: ```sh lcov --capture --directory . --output-file cov.info genhtml cov.info --output-directory cov-html xdg-open cov-html/index.html ``` ### Notes - Coverage builds are **not** for production — `-O0` plus instrumentation is slow, and `.gcda` files accumulate in the source tree (until you reset them with `find . -name '*.gcda' -delete` or `make clean`). - If you change source files between runs without rebuilding, `.gcda`/`.gcno` go out of sync and `gcov` will refuse the data. Re-run `make debug` after every source change, or delete the stale `.gcda`s. - Each Eggdrop process writes its `.gcda`s on clean exit. Tests that kill the bot with `SIGKILL` (only happens after `SIGTERM` times out) will miss data from that process; the harness uses `bridge.eval("die ...")` / `SIGTERM` first so this is rare. ## Why some things are the way they are - **`mod-path` set before `loadmodule`.** Eggdrop reads `mod-path` at each `loadmodule` call, not lazily, so the template orders them accordingly. - **`loadmodule pbkdf2` first.** The userfile read enforces that the encryption module is present (src/main.c:1076). - **`EGG_LANGDIR` env var.** Avoids a symlink in every tmpdir; the language files load from the source tree directly. - **`set msg-rate 0`.** Combined with eggdrop's one-msg-per-second `HOOK_SECONDLY` dequeue, this keeps tests fast without altering protocol semantics. WHOIS still goes out before JOIN; tests use `mock_ircd.drain_until(lambda l: l.startswith("JOIN "))` rather than `expect_recv_match` to skip past it. - **Bridge socket vs `.tcl` over partyline.** The bridge has clean framing and no log interleaving; the partyline is for tests of the partyline UX. - **No `time.sleep` in tests.** Use `waiters.wait_for(...)` / `wait_for_file(...)` / `mock_ircd.drain_until(...)` — every wait has an explicit timeout and a description. ## Converted from the legacy `eggdrop-tests/` BATS suite The 6 Tcl-only `.bats` files from the legacy suite have been ported into this framework. The original ran `cmd_accept.tcl` (a precursor to `test_bridge.tcl`) on TCP port 45678 and asserted on `nc localhost 45678` output — exactly the same shape as `tcl_bridge.eval_ok()`, so the mapping is line-for-line. | Legacy `.bats` file | New file | Notes | | --- | --- | --- | | `eggdrop_tcl_passwdok.bats` | `tests/test_tcl_passwdok.py` | 6 tests | | `eggdrop_tcl_iscmds.bats` | `tests/test_tcl_iscmds.py` | 22 tests (isban / isbansticky / isexempt / isinvite) | | `eggdrop_tcl_matchattr.bats` | `tests/test_tcl_matchattr.py` | 23 tests; the 3 "rejects unknown flag" cases now document the new silent-accept behavior since `tcl_matchattr` no longer errors on unknown flags | | `eggdrop_tcl_server.bats` | `tests/test_tcl_server.py` | 13 tests; ported from the old `addserver`/`delserver`/`set servers` API to the current `server add` / `server remove` / `server list` | | `eggdrop_tcl_addbot.bats` | `tests/test_tcl_addbot.py` | 13 tests; the 8 IPv6 cases auto-skip via an `ipv6_required` fixture when Eggdrop is built without IPv6 | | `eggdrop_chanset_inputvalidation.bats` | `tests/test_chanset_inputvalidation.py` | 9 tests for `flood-deop X:Y` parsing | Files that were **not** converted, with reasons: - `eggdrop_botnet_linking.bats`, `eggdrop_botnet_partyline.bats` — need multiple bots talking to each other; no multi-bot fixture in this framework yet. - `eggdrop_ssl_config.bats`, `eggdrop_ssl_sni.bats` — SSL/TLS not modeled in the mock IRCd. - `eggdrop_compile_*.bats` (5 files) — build-system tests, unrelated to runtime behavior. The `make test` target replaces what the `eggdrop_compile_testrun.bats` covered. - `eggdrop_partyline_bans.bats`, `eggdrop_partyline_flags.bats`, `eggdrop_console_flags.bats` — drive partyline commands and assert on textual output through a TCP partyline (port 1111/3015 in the legacy conf). Convertible using the existing `@pytest.mark.partyline` marker and `eggdrop_proc.send_partyline()`, but those tests assert on free-form output strings; preferring to land them as state-checking tests via `tcl_bridge` when possible. Deferred. ## What's intentionally out of scope (for now) - Linking eggdrop as a shared library / refactoring globals out. - C-side test hooks; everything stays in Tcl/Python. - SSL/TLS to the mock IRCd. Plain TCP only. - Botnet (`share`, `transfer` modules), DCC chat over TCP, Python module. - Real passwords. The owner in the userfile uses `pass = "-"` (no auth); the bridge bypasses the need for it. Partyline auth tests are deferred until there's a reason for them. eggheads-eggdrop-9c00fe1/tests/conftest.py000066400000000000000000000171221524730042700206670ustar00rootroot00000000000000"""Pytest fixtures for the Eggdrop integration test harness.""" from __future__ import annotations import contextlib import os from collections.abc import Generator, Iterator from dataclasses import dataclass, field from pathlib import Path from typing import Any import pytest from jinja2 import Environment, FileSystemLoader, StrictUndefined from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.mock_ircd import MockIrcd from support.waiters import wait_for_file REPO_ROOT = Path( os.environ.get("EGGDROP_SRC", str(Path(__file__).resolve().parents[1])) ) EGGDROP_BIN = Path(os.environ.get("EGGDROP_BIN", str(REPO_ROOT / "eggdrop"))) TEMPLATES_DIR = Path(__file__).resolve().parent / "templates" BRIDGE_TCL = Path(__file__).resolve().parent / "support" / "test_bridge.tcl" _jinja_env = Environment( loader=FileSystemLoader(str(TEMPLATES_DIR)), undefined=StrictUndefined, keep_trailing_newline=True, ) # ---------- session-wide process tracker ---------- @pytest.fixture(scope="session", autouse=True) def _process_tracker() -> Iterator[list[EggdropProc]]: """Backstop: kill any eggdrop processes a test forgot to clean up.""" tracked: list[EggdropProc] = [] yield tracked for proc in tracked: with contextlib.suppress(Exception): proc.terminate(timeout=2) # ---------- per-test fixtures ---------- @pytest.fixture def tmp_eggdir(tmp_path: Path) -> Path: return tmp_path @dataclass class EggdropConfig: tmp: Path mock_ircd_port: int overrides: dict[str, Any] = field(default_factory=dict) rendered: bool = False config_path: Path = field(init=False) userfile_path: Path = field(init=False) chanfile_path: Path = field(init=False) pidfile_path: Path = field(init=False) logfile_path: Path = field(init=False) def __post_init__(self) -> None: self.config_path = self.tmp / "eggdrop.conf" self.userfile_path = self.tmp / "eggdrop.user" self.chanfile_path = self.tmp / "eggdrop.chan" self.pidfile_path = self.tmp / "eggdrop.pid" self.logfile_path = self.tmp / "eggdrop.log" def context(self) -> dict[str, Any]: ctx: dict[str, Any] = dict( nick="TestBot", altnick="Test_?", realname="pytest bot", username="test", admin="pytest ", network="TestNet", botnet_nick="TestBot", owner_handle="owner", owner_flags="nmto", owner_hostmask="*!*@127.0.0.1", mod_path=str(REPO_ROOT) + "/", help_path=str(REPO_ROOT / "help") + "/", bridge_tcl_path=str(BRIDGE_TCL), modules=[ "pbkdf2", "channels", "server", "ctcp", "irc", "console", "notes", ], extra_modules=[], channels=[{"name": "#test", "chanmode": "+nt"}], chanfile_channels=[], userfile_ban_lines=[], userfile_chan_ban_lines={}, extra_tcl="", server_cycle_wait=10, server_timeout=30, log_flags="mcorvxd", tmpdir=str(self.tmp), userfile_path=str(self.userfile_path), chanfile_path=str(self.chanfile_path), pidfile_path=str(self.pidfile_path), logfile_path=str(self.logfile_path), mock_ircd_port=self.mock_ircd_port, ) ctx.update(self.overrides) return ctx def render(self, **overrides: Any) -> None: self.overrides.update(overrides) ctx = self.context() self.config_path.write_text( _jinja_env.get_template("eggdrop.conf.j2").render(**ctx) ) self.userfile_path.write_text( _jinja_env.get_template("userfile.j2").render(**ctx) ) # channels.mod wants to fopen the chanfile read-write; rendering # an empty `chanfile_channels` list produces an empty file, same # as touching it would. self.chanfile_path.write_text( _jinja_env.get_template("chanfile.j2").render(**ctx) ) self.rendered = True @pytest.fixture def mock_ircd() -> Iterator[MockIrcd]: ircd = MockIrcd().start() try: yield ircd finally: with contextlib.suppress(Exception): ircd.stop() @pytest.fixture def eggdrop_config(tmp_eggdir: Path, mock_ircd: MockIrcd) -> EggdropConfig: return EggdropConfig(tmp=tmp_eggdir, mock_ircd_port=mock_ircd.port) def _request_clean_shutdown(port_file: Path) -> None: """Open a fresh bridge connection and send Tcl `die` so Eggdrop exits via its own atexit path (writes userfile/chanfile, runs gcov atexit, etc.). The bridge connection drops as a side effect; that's fine — we just need the request to land. All errors are swallowed; this is best-effort. """ if not port_file.exists(): return with contextlib.suppress(Exception): port = int(port_file.read_text().strip()) with BridgeClient("127.0.0.1", port, timeout=2.0) as client: client.eval("die test cleanup", timeout=2.0) @pytest.fixture def eggdrop_proc( eggdrop_config: EggdropConfig, tmp_eggdir: Path, _process_tracker: list[EggdropProc], request: pytest.FixtureRequest, ) -> Iterator[EggdropProc]: if not eggdrop_config.rendered: eggdrop_config.render() if not EGGDROP_BIN.exists(): pytest.skip(f"eggdrop binary not found at {EGGDROP_BIN}") port_file = tmp_eggdir / "bridge.port" terminal = request.node.get_closest_marker("partyline") is not None proc = EggdropProc( binary=EGGDROP_BIN, config_path=eggdrop_config.config_path, cwd=tmp_eggdir, env={ "EGGDROP_TEST": "1", "EGGDROP_TEST_PORT_FILE": str(port_file), "EGG_LANGDIR": str(REPO_ROOT / "language"), }, terminal=terminal, ) proc.start() _process_tracker.append(proc) try: yield proc finally: # Prefer a clean Tcl `die` so atexit handlers (incl. gcov) run; fall # back to SIGTERM with a generous timeout for slow CI disks where the # gcov .gcda dump on exit can take several seconds. _request_clean_shutdown(port_file) rc = proc.terminate(timeout=30) # On failure, attach the eggdrop log to the report. rep = getattr(request.node, "rep_call", None) if rep is not None and rep.failed: try: log = proc.log_path.read_text() except OSError: log = "(log unavailable)" request.node.add_report_section( "call", "eggdrop stdout", f"exit={rc}\nlog at {proc.log_path}\n--- log ---\n{log}", ) @pytest.fixture def tcl_bridge( eggdrop_proc: EggdropProc, tmp_eggdir: Path ) -> Iterator[BridgeClient]: port_file = tmp_eggdir / "bridge.port" try: wait_for_file(port_file, timeout=10.0) except Exception: eggdrop_proc.assert_alive() raise port = int(port_file.read_text().strip()) client = BridgeClient.connect_with_retry( "127.0.0.1", port, total_timeout=5.0 ) try: yield client finally: client.close() # ---------- failure-report attachment hook ---------- @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport( item: pytest.Item, call: pytest.CallInfo[None] ) -> Generator[None, Any, None]: outcome = yield rep = outcome.get_result() setattr(item, f"rep_{rep.when}", rep) eggheads-eggdrop-9c00fe1/tests/pyproject.toml000066400000000000000000000024651524730042700214100ustar00rootroot00000000000000[project] name = "eggdrop-tests" version = "0.0.0" description = "Integration test harness for Eggdrop" requires-python = ">=3.11" dependencies = [] [dependency-groups] dev = [ "pytest>=8", "pytest-timeout>=2.3", "jinja2>=3.1", ] [tool.pytest.ini_options] testpaths = ["tests"] python_files = ["test_*.py"] addopts = "-ra -q --strict-markers --timeout=60 --tb=short" markers = [ "slow: end-to-end tests that exercise reconnect/timeouts", "partyline: spawns eggdrop with -nt so stdin is the HQ partyline", ] # pytest sweeps it on session exit. tmp_path_retention_policy = "all" tmp_path_retention_count = 3 [tool.ruff] target-version = "py311" src = ["."] [tool.ruff.lint] select = [ "E", "F", "W", # pycodestyle + pyflakes "I", # isort "B", # bugbear "UP", # pyupgrade "SIM", # simplifications "RET", # return statements "PIE", # misc "PT", # pytest style "RUF", # ruff-specific ] ignore = [ "E501", # line length — let formatter handle "PT011", # pytest.raises(Exception) — fine here ] [tool.ty.src] include = ["support", "tests", "conftest.py"] [tool.ty.rules] # All on by default; bump anything we want stricter here. eggheads-eggdrop-9c00fe1/tests/support/000077500000000000000000000000001524730042700202015ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/tests/support/__init__.py000066400000000000000000000000001524730042700223000ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/tests/support/bridge_client.py000066400000000000000000000066711524730042700233570ustar00rootroot00000000000000"""TCP client for the test_bridge.tcl listener inside Eggdrop.""" from __future__ import annotations import contextlib import socket import time from .framing import encode_request, parse_response class BridgeError(Exception): """Tcl evaluation returned an ERR frame.""" class BridgeTimeout(Exception): """Read from the bridge timed out before a frame arrived.""" class BridgeClient: """Synchronous client. One connection, one outstanding request at a time. Eggdrop's bridge serves frames in order, so this design is sufficient for test code that runs sequentially. """ def __init__(self, host: str, port: int, timeout: float = 5.0) -> None: """Open a TCP connection to the bridge. Raises `OSError` if it can't connect.""" self._host = host self._port = port self._sock = socket.create_connection((host, port), timeout=timeout) self._sock.settimeout(timeout) self._buf = bytearray() @classmethod def connect_with_retry( cls, host: str, port: int, total_timeout: float = 5.0 ) -> BridgeClient: """Retry the connect every 50 ms until `total_timeout` elapses. Used by the `tcl_bridge` fixture to wait through Eggdrop's startup between writing the port file and accepting on it. """ deadline = time.monotonic() + total_timeout last: Exception | None = None while time.monotonic() < deadline: try: return cls(host, port) except OSError as e: last = e time.sleep(0.05) raise BridgeTimeout( f"could not connect to bridge at {host}:{port} " f"within {total_timeout}s: {last}" ) def close(self) -> None: """Close the TCP connection. Idempotent; safe to call on a closed socket.""" with contextlib.suppress(OSError): self._sock.close() def __enter__(self) -> BridgeClient: return self def __exit__(self, *_: object) -> None: self.close() def eval(self, cmd: str, timeout: float = 5.0) -> tuple[bool, str]: """Evaluate a Tcl command. Returns (ok, result_text).""" self._sock.sendall(encode_request(cmd)) deadline = time.monotonic() + timeout while b"\n" not in self._buf: remaining = deadline - time.monotonic() if remaining <= 0: raise BridgeTimeout(f"no response to {cmd!r} within {timeout}s") self._sock.settimeout(remaining) try: chunk = self._sock.recv(4096) except TimeoutError as e: raise BridgeTimeout( f"no response to {cmd!r} within {timeout}s" ) from e if not chunk: raise BridgeError( f"bridge closed connection while waiting for " f"response to {cmd!r}" ) self._buf.extend(chunk) nl = self._buf.index(b"\n") line = bytes(self._buf[:nl]).decode("utf-8", "replace") del self._buf[: nl + 1] tag, payload = parse_response(line) return tag == "OK", payload def eval_ok(self, cmd: str, timeout: float = 5.0) -> str: """Evaluate a Tcl command and return its result, raising on ERR.""" ok, result = self.eval(cmd, timeout=timeout) if not ok: raise BridgeError(f"Tcl error evaluating {cmd!r}: {result}") return result eggheads-eggdrop-9c00fe1/tests/support/eggdrop_proc.py000066400000000000000000000126441524730042700232340ustar00rootroot00000000000000"""Subprocess wrapper for a spawned Eggdrop instance.""" from __future__ import annotations import contextlib import io import os import re import signal import subprocess import threading from pathlib import Path from typing import IO from .waiters import WaitTimeout, wait_for_log_match class EggdropDiedError(Exception): """Raised when an operation is attempted on an Eggdrop that has exited.""" class EggdropProc: """Spawned Eggdrop subprocess with a captured stdout drain. `start()` spawns the bot; `terminate()` ends it (SIGTERM, then SIGKILL after the grace period). Stdout/stderr are streamed into both an in-memory ring (`stdout_text()`) and an on-disk log (`log_path`) so failures can be inspected post-mortem. """ def __init__( self, binary: Path, config_path: Path, cwd: Path, env: dict[str, str] | None = None, log_path: Path | None = None, terminal: bool = False, ) -> None: """Configure (but do not start) a wrapped Eggdrop process. `terminal=True` spawns with `-nt` (HQ partyline on stdin, owner perms auto-granted); `False` uses `-n` (foreground only, no partyline). `env` is merged on top of the parent process env. """ self._binary = binary self._config = config_path self._cwd = cwd self._env = {**os.environ, **(env or {})} self._log_path = log_path or (cwd / "eggdrop.stdout.log") self._terminal = terminal self._buf = io.StringIO() self._buf_lock = threading.Lock() self._proc: subprocess.Popen[bytes] | None = None self._drain_thread: threading.Thread | None = None self._log_fp: IO[str] | None = None def start(self) -> EggdropProc: """Spawn Eggdrop and start the background stdout-drain thread.""" # Lifetime spans process; closed in terminate(). self._log_fp = open(self._log_path, "w", encoding="utf-8") # noqa: SIM115 flags = "-nt" if self._terminal else "-n" self._proc = subprocess.Popen( [str(self._binary), flags, str(self._config)], cwd=str(self._cwd), env=self._env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0, ) self._drain_thread = threading.Thread( target=self._drain, name="eggdrop-stdout-drain", daemon=True ) self._drain_thread.start() return self def _drain(self) -> None: # Bind to locals so the iter() closure has non-Optional types. proc = self._proc assert proc is not None assert proc.stdout is not None stdout: IO[bytes] = proc.stdout log_fp = self._log_fp for chunk in iter(lambda: stdout.read(4096), b""): text = chunk.decode("utf-8", "replace") with self._buf_lock: self._buf.write(text) if log_fp is not None: log_fp.write(text) log_fp.flush() @property def proc(self) -> subprocess.Popen[bytes]: assert self._proc is not None, "eggdrop not started" return self._proc @property def pid(self) -> int: return self.proc.pid @property def returncode(self) -> int | None: return self.proc.poll() @property def log_path(self) -> Path: return self._log_path def stdout_text(self) -> str: """Snapshot of everything written to stdout/stderr so far. Thread-safe.""" with self._buf_lock: return self._buf.getvalue() def assert_alive(self) -> None: """Raise `EggdropDiedError` if the process has exited.""" rc = self.proc.poll() if rc is not None: raise EggdropDiedError( f"eggdrop exited with rc={rc}\n--- log ---\n{self.stdout_text()}" ) def wait_for_log(self, pattern: str, timeout: float = 10.0) -> re.Match[str]: """Poll stdout for `pattern`. If it never appears, surface a useful error. On timeout, `assert_alive()` is called first so a dead Eggdrop produces an `EggdropDiedError` (with the captured log) rather than a generic `WaitTimeout`. """ try: return wait_for_log_match(self.stdout_text, pattern, timeout=timeout) except WaitTimeout: self.assert_alive() raise def send_partyline(self, line: str) -> None: """Write a line to Eggdrop's stdin (only useful with -t/HQ partyline).""" proc = self._proc assert proc is not None assert proc.stdin is not None proc.stdin.write((line.rstrip("\n") + "\n").encode("utf-8")) proc.stdin.flush() def terminate(self, timeout: float = 5.0) -> int: """SIGTERM, then SIGKILL after timeout. Returns exit code.""" proc = self._proc if proc is None: return 0 if proc.poll() is None: with contextlib.suppress(ProcessLookupError): proc.send_signal(signal.SIGTERM) try: proc.wait(timeout=timeout) except subprocess.TimeoutExpired: proc.kill() proc.wait(timeout=2) if self._drain_thread is not None: self._drain_thread.join(timeout=2) if self._log_fp is not None: self._log_fp.close() self._log_fp = None return proc.returncode eggheads-eggdrop-9c00fe1/tests/support/framing.py000066400000000000000000000044541524730042700222050ustar00rootroot00000000000000r"""Line-delimited framing for the test bridge. Wire format (one line per frame, terminated by `\n`): request: \n response: OK \n (success) response: ERR \n (Tcl error) Backslash, newline, and CR are backslash-escaped in the payload so the frame is always exactly one line. The escape character set is closed: every `\` in an encoded payload is the start of a `\\`, `\n`, or `\r` sequence. Telnet-friendly: you can `nc 127.0.0.1 ` and type commands by hand. """ from __future__ import annotations class ProtocolError(Exception): """Raised on a malformed frame (bad escape sequence or framing).""" def escape(s: str) -> str: """Escape `\\`, `\\n`, `\\r` so the result fits on a single wire line.""" return ( s.replace("\\", "\\\\") .replace("\n", "\\n") .replace("\r", "\\r") ) def unescape(s: str) -> str: """Inverse of `escape`. Raises `ProtocolError` on dangling/unknown escapes.""" out: list[str] = [] i = 0 n = len(s) while i < n: c = s[i] if c != "\\": out.append(c) i += 1 continue if i + 1 >= n: raise ProtocolError("dangling backslash at end of payload") nxt = s[i + 1] if nxt == "\\": out.append("\\") elif nxt == "n": out.append("\n") elif nxt == "r": out.append("\r") else: raise ProtocolError(f"unknown escape sequence: \\{nxt}") i += 2 return "".join(out) def encode_request(cmd: str) -> bytes: """Frame a Tcl command for transmission to the bridge listener.""" return (escape(cmd) + "\n").encode("utf-8") def encode_response(tag: str, payload: str) -> bytes: """Frame an `OK`/`ERR` reply (used by the Tcl side; tests use the bridge for input).""" return (tag + " " + escape(payload) + "\n").encode("utf-8") def parse_response(line: str) -> tuple[str, str]: """Parse one response line into (tag, payload). Strips trailing newline.""" line = line.rstrip("\n").rstrip("\r") tag, sep, payload = line.partition(" ") if not sep and tag in ("OK", "ERR"): # tag with empty payload (no separator, no escaped content) return tag, "" return tag, unescape(payload) eggheads-eggdrop-9c00fe1/tests/support/identd.py000066400000000000000000000073551524730042700220340ustar00rootroot00000000000000"""Minimal ident (RFC 1413) responder for Eggdrop integration tests. Listens on `127.0.0.1:1113` to match the `EGGDROP_TEST` override in `src/dcc.c:dcc_telnet_hostresolved2` — when that env var is set, eggdrop connects to TCP/1113 (unprivileged) instead of the wire-standard 113. Two modes: "respond" — accept, read the bot's request line, reply with `, : USERID : UNIX : \\r\\n`. "timeout" — accept, never write; eggdrop's `identtimeout` fires. The third ident scenario, "connection refused", is exercised by simply *not* constructing an `IdentServer` so port 1113 stays unbound and the kernel returns RST. """ from __future__ import annotations import contextlib import socket import threading from typing import Literal class IdentServer: """Threaded ident responder bound to 127.0.0.1: (default 1113).""" def __init__( self, mode: Literal["respond", "timeout"], user: str = "alice", host: str = "127.0.0.1", port: int = 1113, ) -> None: self._mode = mode self._user = user self._host = host self._port = port self._sock: socket.socket | None = None self._thread: threading.Thread | None = None self._stop = threading.Event() def start(self) -> IdentServer: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((self._host, self._port)) s.listen(8) s.settimeout(0.2) self._sock = s self._thread = threading.Thread( target=self._serve, name=f"identd-{self._mode}", daemon=True ) self._thread.start() return self def _serve(self) -> None: assert self._sock is not None while not self._stop.is_set(): try: conn, _ = self._sock.accept() except TimeoutError: continue except OSError: return t = threading.Thread( target=self._handle, args=(conn,), daemon=True ) t.start() def _handle(self, conn: socket.socket) -> None: try: if self._mode == "timeout": # Accept and hold; eggdrop's DCC_IDENT timeout # (identtimeout seconds) fires and closes from its side. conn.settimeout(15.0) try: while not self._stop.is_set(): chunk = conn.recv(4096) if not chunk: return except OSError: return else: # "respond" conn.settimeout(5.0) data = b"" while b"\n" not in data and len(data) < 256: chunk = conn.recv(64) if not chunk: break data += chunk line = data.decode("ascii", "replace").rstrip("\r\n") reply = f"{line} : USERID : UNIX : {self._user}\r\n" with contextlib.suppress(OSError): conn.sendall(reply.encode("ascii")) finally: with contextlib.suppress(OSError): conn.shutdown(socket.SHUT_RDWR) conn.close() def stop(self) -> None: self._stop.set() if self._sock is not None: with contextlib.suppress(OSError): self._sock.close() self._sock = None if self._thread is not None: self._thread.join(timeout=2) self._thread = None def __enter__(self) -> IdentServer: return self.start() def __exit__(self, *args: object) -> None: self.stop() eggheads-eggdrop-9c00fe1/tests/support/irc_helpers.py000066400000000000000000000134001524730042700230500ustar00rootroot00000000000000"""Shared IRC-side test helpers. These wrap common multi-step interactions with the mock IRCd so individual tests don't repeat boilerplate. Designed to be importable from any test: from support.irc_helpers import ( drive_registration, drive_join_with_names, wait_for_isupport, ) """ from __future__ import annotations import time from .bridge_client import BridgeClient from .mock_ircd import MockIrcd, MockIrcdError from .waiters import wait_for PREFIX_SYMBOLS = "~&@%+" def split_member_prefix(token: str) -> tuple[str, str]: """Split a NAMES token into (nick, prefix_symbols). >>> split_member_prefix("@alice") ('alice', '@') >>> split_member_prefix("~&boss") ('boss', '~&') >>> split_member_prefix("plain") ('plain', '') """ i = 0 while i < len(token) and token[i] in PREFIX_SYMBOLS: i += 1 return token[i:], token[:i] def drive_registration( mock_ircd: MockIrcd, nick: str = "TestBot", isupport_tokens: list[str] | None = None, ) -> None: """Drive Eggdrop through IRC registration. Waits for the TCP connect, drains the bot's NICK + USER, then sends the welcome sequence (001-004 + optional 005 with `isupport_tokens` + 376). Returns once the welcome is on the wire. To influence which IRCv3 caps the bot negotiates, construct the IRCd with `MockIrcd(advertised_caps=[...])` (typically via a local `mock_ircd` fixture override). The cap list has to be set at construction time because the bot sends `CAP LS 302` immediately on TCP connect — before this helper runs. """ mock_ircd.wait_for_connect(timeout=10.0) for _ in range(2): # NICK and USER mock_ircd.recv(timeout=5.0) mock_ircd.send_welcome(nick=nick, isupport=isupport_tokens) def drive_join_with_names( mock_ircd: MockIrcd, members_with_prefix: str, nick: str = "TestBot", server: str = "mock.test", member_accounts: dict[str, str] | None = None, ) -> str: """Drive a realistic post-registration channel JOIN to completion. Sequence: 1. wait for the bot's `JOIN #chan` 2. echo `:nick!u@h JOIN :#chan` back (this populates `chan->name`) 3. send NAMES (353) with `members_with_prefix` + end (366) 4. drain the bot's post-join queries: * `MODE #chan +b/+e/+I` → empty 368/349/347 end-of-list replies * `WHO #chan ...` → if the bot sent a WHOX-style request (the `c%chnufat,222` form, used when WHOX ISUPPORT is on), reply with one 354 per member carrying the per-member account from `member_accounts` (default "*" = not logged in). Otherwise reply with one 352 per member. Either form ends with 315. 5. leave `MODE #chan` (no list flag) unanswered so tests can send their own 324 reply `member_accounts` maps member nick → account name; nicks not in the dict get "*". Only consulted on the WHOX path; ignored for plain WHO. Returns the channel name. Quiesces when no new lines arrive for ~300 ms or after a 5 s hard cap. """ join_line = mock_ircd.drain_until( lambda line: line.startswith("JOIN "), timeout=10.0 )[-1] chan = join_line.split()[1] mock_ircd.send(f":{nick}!u@h JOIN :{chan}") mock_ircd.send(f":{server} 353 {nick} = {chan} :{members_with_prefix}") mock_ircd.send(f":{server} 366 {nick} {chan} :End of /NAMES list.") members: list[tuple[str, str]] = [ split_member_prefix(t) for t in members_with_prefix.split() if t ] accounts = member_accounts or {} def reply_who(whox: bool) -> None: for member_nick, prefix_syms in members: ident = "u" host = "h.example.com" flags = "H" + prefix_syms # H = here (not away) if whox: # 354 format from chan.c:got354: # ": 354 222 " acct = accounts.get(member_nick, "*") mock_ircd.send( f":{server} 354 {nick} 222 {chan} {ident} {host} " f"{member_nick} {flags} {acct}" ) else: mock_ircd.send( f":{server} 352 {nick} {chan} {ident} {host} {server} " f"{member_nick} {flags} :0 {member_nick}" ) mock_ircd.send(f":{server} 315 {nick} {chan} :End of /WHO list.") deadline = time.monotonic() + 5.0 idle = 0.3 while time.monotonic() < deadline: try: line = mock_ircd.recv(timeout=idle) except MockIrcdError: return chan # quiesced if line.startswith(f"MODE {chan} +b"): mock_ircd.send(f":{server} 368 {nick} {chan} :End of Channel Ban List") elif line.startswith(f"MODE {chan} +e"): mock_ircd.send( f":{server} 349 {nick} {chan} :End of Channel Exception List" ) elif line.startswith(f"MODE {chan} +I"): mock_ircd.send( f":{server} 347 {nick} {chan} :End of Channel Invite List" ) elif line.startswith(f"WHO {chan}"): # WHOX form is `WHO #chan c%chnufat,222`; eggdrop emits this when # use_354 (WHOX ISUPPORT) is on and parses replies from got354. reply_who(whox=",222" in line) # MODE #chan (no list flag) — left for the test to answer with 324. # Anything else is silently drained. return chan def wait_for_isupport( bridge: BridgeClient, key: str, expected: str, timeout: float = 5.0 ) -> None: """Block until `isupport get ` returns `expected`.""" wait_for( lambda: bridge.eval_ok(f"isupport get {key}") == expected, timeout=timeout, description=f"isupport {key}={expected!r}", ) eggheads-eggdrop-9c00fe1/tests/support/mock_ircd.py000066400000000000000000000270321524730042700225110ustar00rootroot00000000000000"""Minimal mock IRCd for Eggdrop integration tests. asyncio TCP server hosted on its own thread + private event loop. Tests interact via a synchronous facade: `recv()`, `send()`, `expect_recv_match()`, `send_welcome()`. The mock auto-replies to PING; everything else lands in a queue for tests to consume and assert on. Default behaviour is one client connection per `MockIrcd` instance. Tests that exercise reconnect logic should pass `allow_reconnect=True`. """ from __future__ import annotations import asyncio import contextlib import re import threading import time from collections.abc import Callable, Coroutine, Iterable from concurrent.futures import Future from typing import Any, TypeVar _T = TypeVar("_T") class MockIrcdError(Exception): """Raised on a timeout or other harness-side error against the mock IRCd.""" class UnexpectedReconnect(MockIrcdError): """Raised at `stop()` if the bot reconnected when `allow_reconnect=False`.""" class MockIrcd: """Synchronous-facade mock IRCd, listening on `127.0.0.1:0`. Internally an asyncio TCP server runs on a private event loop in a background thread. The public API is plain blocking calls (`recv()`, `send()`, `expect_recv_match()`, ...) so test code stays linear. Auto-handles client `PING` and (by default) the `CAP LS`/`REQ`/`END` handshake so individual tests don't have to repeat that boilerplate. Pass `auto_cap=False` to drive CAP negotiation explicitly. """ def __init__( self, allow_reconnect: bool = False, auto_cap: bool = True, server_name: str = "mock.test", advertised_caps: Iterable[str] | None = None, ) -> None: """Configure (but do not start) a mock IRCd. `allow_reconnect`: if False, a second client connection during the test is treated as a hard error at `stop()` time. `auto_cap`: auto-respond to `CAP LS`/`REQ`/`LIST`. `CAP REQ` is always ACKed for whatever the bot asks for. `server_name`: source prefix for synthetic numerics (`:server 001 ...`). `advertised_caps`: caps offered in `CAP LS` replies. Default empty. Tests that need specific caps construct their own MockIrcd (typically via a local `mock_ircd` fixture override) — by the time the test body runs the bot has already sent `CAP LS 302`, so the cap list must be set at construction time. """ self._allow_reconnect = allow_reconnect self._auto_cap = auto_cap self._server_name = server_name self._advertised_caps = " ".join(advertised_caps or []) self._loop = asyncio.new_event_loop() self._thread = threading.Thread( target=self._loop.run_forever, name="MockIrcd", daemon=True ) self._server: asyncio.base_events.Server | None = None self._writer: asyncio.StreamWriter | None = None self._recv_q: asyncio.Queue[str] | None = None self._connect_evt: asyncio.Event | None = None self._connect_count = 0 self._unexpected_reconnect = False self.port: int = 0 # ---------- lifecycle ---------- def start(self) -> MockIrcd: """Start the asyncio loop thread and bind the listener. Sets `self.port`.""" self._thread.start() self._submit(self._async_start()).result(timeout=5) return self def stop(self) -> None: """Close the listener, stop the loop thread, and assert no rogue reconnects. Raises `UnexpectedReconnect` if the client connected more than once without `allow_reconnect=True`. """ if self._loop.is_running(): with contextlib.suppress(Exception): self._submit(self._async_stop()).result(timeout=5) self._loop.call_soon_threadsafe(self._loop.stop) self._thread.join(timeout=5) if self._unexpected_reconnect and not self._allow_reconnect: raise UnexpectedReconnect( f"client connected {self._connect_count} times " f"(allow_reconnect=False)" ) def __enter__(self) -> MockIrcd: return self.start() def __exit__(self, *_: object) -> None: self.stop() async def _async_start(self) -> None: self._recv_q = asyncio.Queue() self._connect_evt = asyncio.Event() self._server = await asyncio.start_server( self._on_client, "127.0.0.1", 0 ) self.port = self._server.sockets[0].getsockname()[1] async def _async_stop(self) -> None: if self._writer is not None: self._writer.close() # wait_closed can hang if the peer is gone (FIN never ACKed). # Bound it tightly — we're tearing down the loop anyway. with contextlib.suppress(Exception): await asyncio.wait_for(self._writer.wait_closed(), timeout=0.5) if self._server is not None: self._server.close() with contextlib.suppress(Exception): await asyncio.wait_for(self._server.wait_closed(), timeout=0.5) # ---------- client handler ---------- async def _on_client( self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter ) -> None: self._connect_count += 1 if self._connect_count > 1 and not self._allow_reconnect: self._unexpected_reconnect = True writer.close() return self._writer = writer assert self._connect_evt is not None self._connect_evt.set() try: while True: line = await reader.readline() if not line: break text = line.rstrip(b"\r\n").decode("utf-8", "replace") upper = text.upper() if upper.startswith("PING"): pong = "PONG" + text[4:] + "\r\n" writer.write(pong.encode()) await writer.drain() elif self._auto_cap and upper.startswith("CAP "): self._handle_cap(text, writer) await writer.drain() else: assert self._recv_q is not None await self._recv_q.put(text) finally: self._writer = None def _handle_cap(self, text: str, writer: asyncio.StreamWriter) -> None: """Auto-respond to client CAP commands so tests don't have to. Replies to `CAP LS` with the caps the IRCd was constructed with (default empty), and ACKs whatever the bot then asks for in `CAP REQ`. """ parts = text.split(maxsplit=2) sub = parts[1].upper() if len(parts) >= 2 else "" srv = self._server_name # Use "*" as the unregistered nick placeholder per RFC. if sub == "LS": writer.write(f":{srv} CAP * LS :{self._advertised_caps}\r\n".encode()) elif sub == "REQ": cap = parts[2] if len(parts) >= 3 else ":" if cap.startswith(":"): cap = cap[1:] writer.write(f":{srv} CAP * ACK :{cap}\r\n".encode()) elif sub == "END": pass # no response needed elif sub == "LIST": writer.write(f":{srv} CAP * LIST :\r\n".encode()) # ---------- synchronous facade ---------- def _submit(self, coro: Coroutine[Any, Any, _T]) -> Future[_T]: """Schedule a coroutine on the bg loop, return a thread-safe Future.""" return asyncio.run_coroutine_threadsafe(coro, self._loop) def wait_for_connect(self, timeout: float = 10.0) -> None: """Block until the first client (Eggdrop) opens the TCP connection.""" async def _wait() -> None: assert self._connect_evt is not None await asyncio.wait_for(self._connect_evt.wait(), timeout) self._submit(_wait()).result(timeout=timeout + 1) def recv(self, timeout: float = 5.0) -> str: """Pop the next non-PING/CAP line from the client's recv queue. Raises `MockIrcdError` if no line arrives within `timeout`. """ async def _recv() -> str: assert self._recv_q is not None return await asyncio.wait_for(self._recv_q.get(), timeout) try: return self._submit(_recv()).result(timeout=timeout + 1) except TimeoutError as e: raise MockIrcdError( f"no IRC line received within {timeout}s" ) from e def expect_recv(self, expected: str, timeout: float = 5.0) -> str: """`recv()` and assert the line equals `expected` exactly.""" line = self.recv(timeout) if line != expected: raise AssertionError( f"expected IRC line {expected!r}, got {line!r}" ) return line def expect_recv_match(self, pattern: str, timeout: float = 5.0) -> re.Match[str]: """`recv()` and assert the line matches `pattern` (re.match anchored).""" line = self.recv(timeout) m = re.match(pattern, line) if not m: raise AssertionError( f"expected IRC line matching /{pattern}/, got {line!r}" ) return m def drain_until( self, predicate: Callable[[str], bool], timeout: float = 5.0 ) -> list[str]: """Read lines until `predicate(line)` is True. Returns the lines read (including the matching one). Useful for skipping registration noise.""" deadline = time.monotonic() + timeout seen: list[str] = [] while time.monotonic() < deadline: remaining = max(0.05, deadline - time.monotonic()) line = self.recv(remaining) seen.append(line) if predicate(line): return seen raise MockIrcdError( f"predicate not satisfied within {timeout}s; saw {len(seen)} lines" ) def send(self, line: str) -> None: """Push a single IRC line (CRLF appended automatically) to the client.""" async def _send() -> None: assert self._writer is not None, "no client connected" data = (line.rstrip("\r\n") + "\r\n").encode() self._writer.write(data) await self._writer.drain() self._submit(_send()).result(timeout=5) def send_from(self, prefix: str, rest: str) -> None: """Convenience: `send(": ")`. Use for messages from other users.""" self.send(f":{prefix} {rest}") def send_welcome( self, nick: str, server: str = "mock.test", isupport: Iterable[str] | None = None, ) -> None: """Send the registration response: 001-004, optional 005, then 376. `isupport` is a list of raw `KEY=VALUE` (or bare `KEY`) tokens joined into the 005 line; pass `None` to skip the 005 entirely. """ self.send(f":{server} 001 {nick} :Welcome to mock {nick}") self.send(f":{server} 002 {nick} :Your host is {server}") self.send(f":{server} 003 {nick} :This server was created today") self.send(f":{server} 004 {nick} {server} mock-1.0 oiwsx ovimnpst") if isupport: tokens = " ".join(isupport) self.send(f":{server} 005 {nick} {tokens} :are supported") self.send(f":{server} 376 {nick} :End of MOTD") def send_names( self, nick: str, channel: str, members: Iterable[str], server: str = "mock.test" ) -> None: """Send a 353 NAMES line followed by the 366 end-of-list.""" names = " ".join(members) self.send(f":{server} 353 {nick} = {channel} :{names}") self.send(f":{server} 366 {nick} {channel} :End of /NAMES") eggheads-eggdrop-9c00fe1/tests/support/test_bridge.tcl000066400000000000000000000044721524730042700232070ustar00rootroot00000000000000# Eggdrop integration-test bridge. # # Sourced from a rendered eggdrop.conf when EGGDROP_TEST is set in the # environment. Binds a TCP listener on 127.0.0.1:0, writes the OS-assigned # port to $env(EGGDROP_TEST_PORT_FILE), and accepts line-delimited Tcl # commands. Each command is evaluated in the global interpreter; the result # is returned as a single line tagged "OK" or "ERR". # # Wire format (one line per frame, \n-terminated): # request: \n # response: OK \n or ERR \n # where \, \n, \r in the payload are backslash-escaped so each frame is # always exactly one line. # # Eggdrop's Tcl_ServiceAll() runs every main-loop tick, so socket -server # and fileevent fire normally. This script is *only* loaded under the # EGGDROP_TEST gate and must never ship enabled in production. namespace eval ::eggtest { proc escape {s} { return [string map [list "\\" "\\\\" "\n" "\\n" "\r" "\\r"] $s] } proc unescape {s} { return [string map [list "\\\\" "\\" "\\n" "\n" "\\r" "\r"] $s] } proc on_data {sock} { if {[catch {gets $sock line} n] || $n < 0} { if {[eof $sock]} { close_sock $sock } return } set cmd [unescape $line] if {[catch {uplevel #0 $cmd} result]} { puts $sock "ERR [escape $result]" } else { puts $sock "OK [escape $result]" } flush $sock } proc close_sock {sock} { catch {close $sock} } proc on_accept {sock host port} { fconfigure $sock -blocking 0 -translation lf -buffering line fileevent $sock readable [list ::eggtest::on_data $sock] } proc start {} { if {![info exists ::env(EGGDROP_TEST_PORT_FILE)]} { putlog "test_bridge: EGGDROP_TEST_PORT_FILE not set, refusing to start" return } set srv [socket -server ::eggtest::on_accept -myaddr 127.0.0.1 0] set port [lindex [fconfigure $srv -sockname] 2] set portfile $::env(EGGDROP_TEST_PORT_FILE) set tmp "$portfile.tmp" set f [open $tmp w] puts -nonewline $f $port close $f file rename -force $tmp $portfile putlog "test_bridge listening on 127.0.0.1:$port (wrote $portfile)" } } ::eggtest::start eggheads-eggdrop-9c00fe1/tests/support/userfile_helpers.py000066400000000000000000000026131524730042700241150ustar00rootroot00000000000000"""Helpers for building userfile content programmatically. Tests inject pre-populated ban records into the userfile via the `userfile_ban_lines` (global) and `userfile_chan_ban_lines` (per-channel) context variables of `templates/userfile.j2`. The template just emits the strings verbatim — all formatting and escaping happens here so the template stays a flat iteration. """ from __future__ import annotations def format_userfile_ban( *, mask: str, perm: bool, sticky: bool, expire: int, added: int, lastactive: int, creator: str, desc: str, ) -> str: """Format one ban record for the userfile. Mirrors the line written by `write_bans` in `src/mod/channels.mod/userchan.c`: - ::+::: `:` and `\\` in the mask are hex-escaped per `src/misc.c:str_escape` (the parser uses `\\xy` as a hex byte; a literal `\\:` would yield NUL via strtol of ":?" base 16 and silently truncate the mask). All arguments are required — no defaults — so each test states exactly what it's pinning. """ escaped = mask.replace("\\", "\\5c").replace(":", "\\3a") perm_prefix = "+" if perm else "" sticky_suffix = "*" if sticky else "" return ( f"- {escaped}:{perm_prefix}{expire}{sticky_suffix}:" f"+{added}:{lastactive}:{creator}:{desc}" ) eggheads-eggdrop-9c00fe1/tests/support/waiters.py000066400000000000000000000033621524730042700222350ustar00rootroot00000000000000"""Wait primitives with explicit timeouts. No bare time.sleep loops in tests.""" from __future__ import annotations import re import time from collections.abc import Callable from pathlib import Path class WaitTimeout(Exception): """Raised by any `wait_for*` helper when its deadline passes.""" def wait_for_file( path: Path, timeout: float = 10.0, poll: float = 0.05 ) -> None: """Block until `path` exists. Raises WaitTimeout.""" deadline = time.monotonic() + timeout while time.monotonic() < deadline: if path.exists(): return time.sleep(poll) raise WaitTimeout(f"file {path} did not appear within {timeout}s") def wait_for( predicate: Callable[[], bool], timeout: float = 10.0, poll: float = 0.05, description: str = "predicate", ) -> None: """Poll `predicate()` every `poll` seconds until it returns truthy. `description` is included in the timeout error message — supply something specific so test failures are self-explanatory. """ deadline = time.monotonic() + timeout while time.monotonic() < deadline: if predicate(): return time.sleep(poll) raise WaitTimeout(f"{description} not satisfied within {timeout}s") def wait_for_log_match( log_text: Callable[[], str], pattern: str, timeout: float = 10.0, poll: float = 0.05, ) -> re.Match[str]: """Block until `pattern` matches anywhere in the log text.""" rx = re.compile(pattern, re.MULTILINE) deadline = time.monotonic() + timeout while time.monotonic() < deadline: m = rx.search(log_text()) if m: return m time.sleep(poll) raise WaitTimeout( f"log pattern /{pattern}/ not seen within {timeout}s" ) eggheads-eggdrop-9c00fe1/tests/templates/000077500000000000000000000000001524730042700204635ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/tests/templates/chanfile.j2000066400000000000000000000012561524730042700224750ustar00rootroot00000000000000{#- Chanfile template. Tests pass `chanfile_channels` as a list of {"name": ..., "options": ...} records. `options` is a Tcl-syntax options block (verbatim) — defaults to the empty block `{}`, which is enough to make findchan_by_dname() return the channel during userfile load. By default the list is empty and the chanfile is empty. Channels added via the eggdrop.conf `channels` template variable are *also* registered (via `channel add` lines in the conf) and run before the chanfile, so most tests don't need to populate this file at all. -#} {%- for ch in chanfile_channels -%} channel add {{ ch.name }} { {{ ch.options | default('') }} } {% endfor -%} eggheads-eggdrop-9c00fe1/tests/templates/eggdrop.conf.j2000066400000000000000000000023471524730042700233010ustar00rootroot00000000000000# Generated by the test harness. Do not edit by hand. set mod-path "{{ mod_path }}" set help-path "{{ help_path }}" {% for m in modules %} loadmodule {{ m }} {% endfor %} {% for m in extra_modules %} loadmodule "{{ m }}" {% endfor %} set nick "{{ nick }}" set altnick "{{ altnick }}" set realname "{{ realname }}" set username "{{ username }}" set admin "{{ admin }}" set network "{{ network }}" set botnet-nick "{{ botnet_nick }}" set owner "{{ owner_handle }}" set userfile "{{ userfile_path }}" set chanfile "{{ chanfile_path }}" set pidfile "{{ pidfile_path }}" {% if 'server' in modules %} set net-type "Other" set default-port "{{ mock_ircd_port }}" set servers [list] server add 127.0.0.1 "{{ mock_ircd_port }}" set server-cycle-wait "{{ server_cycle_wait }}" set server-timeout "{{ server_timeout }}" set msg-rate 0 set flood-msg 0 set flood-ctcp 0 {% endif %} set raw-log 1 logfile {{ log_flags }} * "{{ logfile_path }}" set log-time 0 set keep-all-logs 0 {% for ch in channels %} channel add {{ ch.name }} { chanmode "{{ ch.chanmode|default('+nt') }}" } {% endfor %} # Test bridge — only loaded when EGGDROP_TEST is set in the environment. if {[info exists env(EGGDROP_TEST)]} { source "{{ bridge_tcl_path }}" } {{ extra_tcl }} eggheads-eggdrop-9c00fe1/tests/templates/userfile.j2000066400000000000000000000020511524730042700225340ustar00rootroot00000000000000{#- Userfile template. Tests inject pre-formatted ban-record lines via: userfile_ban_lines — list of strings, written under `*ban - -`. userfile_chan_ban_lines — dict of chan-name -> list of strings, each written under `:: bans`. Build the strings in Python with `support.userfile_helpers.format_userfile_ban`. The `*ban - -` section header is what `write_bans` (channels.mod/userchan.c) emits and what the parser needs — `newsplit` (src/misc.c:255) splits on literal ' ' only, so `*ban\n` keeps the trailing newline glued to the token and `rfc_casecmp("*ban\n", "*ban")` fails to match. -#} #4v: eggdrop test harness -- {{ botnet_nick }} -- generated {{ owner_handle }} - {{ owner_flags }} - {{ owner_hostmask }} {% if userfile_ban_lines -%} *ban - - {% for line in userfile_ban_lines -%} {{ line }} {% endfor -%} {% endif -%} {% for chan_name, lines in userfile_chan_ban_lines.items() -%} ::{{ chan_name }} bans {% for line in lines -%} {{ line }} {% endfor -%} {% endfor -%} eggheads-eggdrop-9c00fe1/tests/tests/000077500000000000000000000000001524730042700176275ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/tests/tests/__init__.py000066400000000000000000000000001524730042700217260ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/tests/tests/test_account_tag_voice_mode.py000066400000000000000000000074321524730042700257260ustar00rootroot00000000000000"""Account-tag CAP + WHOX + tagged MODE: regression for the full account-aware path through CAP negotiation, WHOX-driven account learning, and an IRCv3-tagged mode change. End-to-end shape: - CAP LS advertises `account-tag`; bot REQ/server ACK; bot enables it. - 005 carries `WHOX` so eggdrop sets `use_354` (chan.c:3053) and emits `WHO #chan c%chnufat,222` after JOIN; the helper replies with 354s carrying op's account name "op". - The IRCd then sends a MODE +vv with an `@account=op` IRCv3 tag. Eggdrop parses the MODE (via standard mode handling) and the tag (via chan.c:gotrawt → setaccount). - Result: test1 and test2 are voiced; op's account is "op". """ from __future__ import annotations import contextlib from collections.abc import Iterator import pytest from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.irc_helpers import ( drive_join_with_names, drive_registration, wait_for_isupport, ) from support.mock_ircd import MockIrcd from support.waiters import wait_for @pytest.fixture def mock_ircd() -> Iterator[MockIrcd]: """Override the default fixture to advertise `account-tag` in CAP LS. The cap list is fixed at construction time because the bot sends `CAP LS 302` immediately on TCP connect, before the test body runs. """ ircd = MockIrcd(advertised_caps=["account-tag"]).start() try: yield ircd finally: with contextlib.suppress(Exception): ircd.stop() def test_isvoice_after_tagged_mode_with_whox_and_account_tag( eggdrop_config, request: pytest.FixtureRequest, ) -> None: # account-tag is disabled by default in server.mod (servmsg.c:44); the # bot only adds it to the CAP REQ list when the Tcl var is 1. Set it via # extra_tcl so the assignment runs after server.mod's loadmodule. eggdrop_config.render(extra_tcl="set account-tag 1\n") mock_ircd: MockIrcd = request.getfixturevalue("mock_ircd") eggdrop_proc: EggdropProc = request.getfixturevalue("eggdrop_proc") # noqa: F841 — proc must spawn before bridge tcl_bridge: BridgeClient = request.getfixturevalue("tcl_bridge") drive_registration(mock_ircd, isupport_tokens=["WHOX"]) wait_for_isupport(tcl_bridge, "WHOX", "") # bare token: value stored as "" # CAP negotiation must have completed (welcome only fires after CAP END); # account-tag must be in the enabled list. enabled = tcl_bridge.eval_ok("cap enabled").split() assert "account-tag" in enabled, f"expected account-tag enabled, got {enabled}" chan = drive_join_with_names( mock_ircd, "@op test1 test2 @TestBot", member_accounts={"op": "op"}, ) # WHOX populated op's account via 354 (chan.c:got354 → got352or4 → setaccount). wait_for( lambda: tcl_bridge.eval_ok(f'getaccount op "{chan}"') == "op", timeout=5.0, description="WHOX 354 to set op's account to 'op'", ) # Pre-MODE sanity: nobody has voice yet. assert tcl_bridge.eval_ok(f'onchan test1 "{chan}"') == "1" assert tcl_bridge.eval_ok(f'onchan test2 "{chan}"') == "1" assert tcl_bridge.eval_ok(f'isvoice test1 "{chan}"') == "0" assert tcl_bridge.eval_ok(f'isvoice test2 "{chan}"') == "0" mock_ircd.send(f"@account=op :op!op@127.0.0.1 MODE {chan} +vv test1 :test2") wait_for( lambda: ( tcl_bridge.eval_ok(f'isvoice test1 "{chan}"') == "1" and tcl_bridge.eval_ok(f'isvoice test2 "{chan}"') == "1" ), timeout=5.0, description="MODE +vv test1 test2 to voice both members", ) # And op's account survived the tag-driven setaccount call (no-op since # it was already "op", but we want to be sure gotrawt didn't clobber it). assert tcl_bridge.eval_ok(f'getaccount op "{chan}"') == "op" eggheads-eggdrop-9c00fe1/tests/tests/test_chanset_inputvalidation.py000066400000000000000000000064301524730042700261620ustar00rootroot00000000000000"""Converted from eggdrop-tests/eggdrop_chanset_inputvalidation.bats. Input validation for `channel set flood-deop X:Y` — the X:Y format parser must accept positive integer pairs, reject malformed input, and treat the all-zero forms as "off".""" from __future__ import annotations import pytest from support.bridge_client import BridgeClient CHAN = "#eggtest" @pytest.fixture def chan(tcl_bridge: BridgeClient) -> str: tcl_bridge.eval_ok(f"channel add {CHAN}") return CHAN def test_flood_deop_accepts_valid_x_colon_y( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"channel set {chan} flood-deop 253:42") info = tcl_bridge.eval_ok(f"channel info {chan}") assert "253:42" in info def test_flood_deop_rejects_non_integer_messages( tcl_bridge: BridgeClient, chan: str ) -> None: """Letters in either field of X:Y are rejected.""" for bad in ("4:D", "2a:4", "2:4a"): ok, result = tcl_bridge.eval(f"channel set {chan} flood-deop {bad}") assert not ok, f"{bad!r} unexpectedly accepted" assert "values must be integers >= 0" in result def test_flood_deop_rejects_single_value( tcl_bridge: BridgeClient, chan: str ) -> None: """A bare integer (no colon) is rejected — must be X:Y.""" ok, result = tcl_bridge.eval(f"channel set {chan} flood-deop 4") assert not ok assert "flood value must be in X:Y format" in result def test_flood_deop_rejects_invalid_format( tcl_bridge: BridgeClient, chan: str ) -> None: """`:X`, `X:`, and three-section `X:Y:Z` are all rejected.""" for bad in (":3", "3:", "53:75:86"): ok, result = tcl_bridge.eval(f"channel set {chan} flood-deop {bad}") assert not ok, f"{bad!r} unexpectedly accepted" assert "flood value must be in X:Y format" in result def test_flood_deop_rejects_negative_numbers( tcl_bridge: BridgeClient, chan: str ) -> None: ok, result = tcl_bridge.eval(f"channel set {chan} flood-deop -1:8") assert not ok assert "values must be integers >= 0" in result def test_flood_deop_zero_clears_setting( tcl_bridge: BridgeClient, chan: str ) -> None: """Bare `0` resets the setting to `0:0`.""" tcl_bridge.eval_ok(f"channel set {chan} flood-deop 6:9") tcl_bridge.eval_ok(f"channel set {chan} flood-deop 0") info = tcl_bridge.eval_ok(f"channel info {chan}") assert "0:0" in info def test_flood_deop_zero_zero_clears_setting( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"channel set {chan} flood-deop 6:9") tcl_bridge.eval_ok(f"channel set {chan} flood-deop 0:0") info = tcl_bridge.eval_ok(f"channel info {chan}") assert "0:0" in info def test_flood_deop_n_zero_keeps_n(tcl_bridge: BridgeClient, chan: str) -> None: """`N:0` is a valid (degenerate) setting and is stored verbatim.""" tcl_bridge.eval_ok(f"channel set {chan} flood-deop 6:9") tcl_bridge.eval_ok(f"channel set {chan} flood-deop 9:0") info = tcl_bridge.eval_ok(f"channel info {chan}") assert "9:0" in info def test_flood_deop_zero_n_keeps_n(tcl_bridge: BridgeClient, chan: str) -> None: tcl_bridge.eval_ok(f"channel set {chan} flood-deop 6:9") tcl_bridge.eval_ok(f"channel set {chan} flood-deop 0:8") info = tcl_bridge.eval_ok(f"channel info {chan}") assert "0:8" in info eggheads-eggdrop-9c00fe1/tests/tests/test_extban.py000066400000000000000000000623471524730042700225350ustar00rootroot00000000000000"""Integration tests for the extban support PR (channels.mod / irc.mod). Each test sets up only what it needs and reads from the bridge to assert on internal state. Where IRC traffic is involved, the test drives the mock IRCd directly and (when the bot's outgoing MODE is the thing under test) flushes the mode buffer with `flushmode` to avoid waiting on the periodic HOOK_IDLE flush. What this PR introduced (and these tests cover): - Tcl `account-extban` global, populated from ISUPPORT ACCOUNTEXTBAN. - `.+extban ` partyline command. Constructs the mask using the EXTBAN-advertised prefix, refuses while disconnected. - `u_addban` no longer auto-sticky-fies extban masks at storage time (option-1 swap: enforceability decided at runtime via `extban_is_unenforceable`, never persisted). - `check_this_ban` / `recheck_bans` / `check_expired_chanstuff` treat unenforceable extbans (q:, c:, ...) as if sticky for set-and-keep purposes, even on `+dynamicbans` channels. - `u_addban` skips the bot-self-ban check on extban masks (they don't have the nick!user@host shape that match would target). """ from __future__ import annotations import pytest from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.irc_helpers import ( drive_join_with_names, drive_registration, wait_for_isupport, ) from support.mock_ircd import MockIrcd from support.userfile_helpers import format_userfile_ban from support.waiters import wait_for # ---------- Tcl `account-extban` global ---------- def test_account_extban_tcl_var_empty_when_no_isupport( tcl_bridge: BridgeClient, ) -> None: """Reading `$account-extban` before any 005 returns the empty string. The Tcl trace fires on read, calls `servermod_isupport_get("ACCOUNTEXTBAN")`, which returns NULL since the bot hasn't connected yet, and the trace handler stores "" in the variable. """ assert tcl_bridge.eval_ok("set ::account-extban") == "" def test_account_extban_tcl_var_populated_from_isupport( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """After 005 with `ACCOUNTEXTBAN=a`, reading the Tcl global returns "a". The PR also accepts the longer `a,account` form per ircdocs; we send the short form here because it's what most networks advertise. """ drive_registration( mock_ircd, isupport_tokens=["EXTBAN=~,acrjmU", "ACCOUNTEXTBAN=a"], ) wait_for_isupport(tcl_bridge, "ACCOUNTEXTBAN", "a") assert tcl_bridge.eval_ok("set ::account-extban") == "a" # ---------- u_addban: no auto-sticky on extban storage ---------- def test_newban_extban_does_not_auto_sticky( tcl_bridge: BridgeClient, ) -> None: """`newban a:foo` stores the ban without `MASKREC_STICKY`. Before commit 9269ae64, u_addban set MASKREC_STICKY for any extban whose flag wasn't in the hardcoded enforceable set. With the option-1 swap, that decision moved to enforcement time (`extban_is_unenforceable`), so the userfile flags now reflect the user's literal intent. """ tcl_bridge.eval_ok("newban a:foo testuser comment") assert tcl_bridge.eval_ok("isbansticky a:foo") == "0" assert tcl_bridge.eval_ok("isban a:foo") == "1" def test_newban_extban_with_explicit_sticky_option_is_sticky( tcl_bridge: BridgeClient, ) -> None: """User-controlled sticky still works: `newban a:foo ... sticky` → sticky.""" tcl_bridge.eval_ok("newban a:foo testuser comment 0 sticky") assert tcl_bridge.eval_ok("isbansticky a:foo") == "1" def test_newban_unenforceable_extban_q_not_auto_sticky( tcl_bridge: BridgeClient, ) -> None: """`q:` (mute extban) is unenforceable from the eggdrop side, but the sticky bit still isn't persisted. Enforceability is a runtime decision.""" tcl_bridge.eval_ok("newban q:badactor testuser comment") assert tcl_bridge.eval_ok("isbansticky q:badactor") == "0" assert tcl_bridge.eval_ok("isban q:badactor") == "1" # ---------- u_addban: bot-self-ban check skipped for extbans ---------- def test_newban_extban_matching_botnick_is_not_self_rejected( tcl_bridge: BridgeClient, ) -> None: """The "I'm not going to ban myself" guard in u_addban only fires on the non-extban branch (extban masks don't have nick!user@host shape). `a:TestBot` should be accepted even though the bot's nick is TestBot. """ tcl_bridge.eval_ok("newban a:TestBot testuser comment") assert tcl_bridge.eval_ok("isban a:TestBot") == "1" # ---------- partyline `.+extban` ---------- @pytest.mark.partyline def test_partyline_pls_extban_refused_when_disconnected( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`.+extban` requires ISUPPORT EXTBAN to know the prefix; refuses if we haven't seen 005 yet (commit 2e195b1). No drive_registration() call here — the bot has TCP-connected but is still pre-welcome, so isupport_get("EXTBAN") returns NULL. """ snapshot = len(eggdrop_proc.stdout_text()) eggdrop_proc.send_partyline(".+extban a foo") wait_for( lambda: "must be connected to a server with EXTBAN support" in eggdrop_proc.stdout_text()[snapshot:], timeout=5.0, description="partyline .+extban to refuse with EXTBAN-unavailable msg", ) # And nothing was stored. assert tcl_bridge.eval_ok("isban a:foo") == "0" @pytest.mark.partyline def test_partyline_pls_extban_constructs_prefixed_mask( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`.+extban a Foo` on a server with `EXTBAN=~,a` stores `~a:Foo` in the channel ban list (prefix from ISUPPORT, flag and value from input). """ drive_registration( mock_ircd, isupport_tokens=["EXTBAN=~,acrjmU", "ACCOUNTEXTBAN=a"], ) drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "EXTBAN", "~,acrjmU") eggdrop_proc.send_partyline(".+extban a Foo #test") wait_for( lambda: tcl_bridge.eval_ok("isban ~a:Foo #test") == "1", timeout=5.0, description="partyline .+extban to register ~a:Foo on #test", ) @pytest.mark.partyline def test_partyline_pls_extban_constructs_unprefixed_mask( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`.+extban a Foo` on a server whose EXTBAN advertises no prefix (form: `,types`) stores `a:Foo` (no prefix prepended). """ drive_registration(mock_ircd, isupport_tokens=["EXTBAN=,acrjmU"]) drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "EXTBAN", ",acrjmU") eggdrop_proc.send_partyline(".+extban a Foo #test") wait_for( lambda: tcl_bridge.eval_ok("isban a:Foo #test") == "1", timeout=5.0, description="partyline .+extban to register a:Foo (no prefix) on #test", ) # ---------- check_this_ban / recheck_bans: unenforceable extban set on +dynamicbans ---------- def test_unenforceable_extban_queued_as_plus_b_on_dynamicbans_channel( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """A non-enforceable extban (`q:`) must still be set on the channel even with `+dynamicbans`, because eggdrop has no other way to enforce a server-side mute. The condition in check_this_ban includes `extban_is_unenforceable(banmask)` for exactly this case. """ # 'q' must appear in the EXTBAN types list, otherwise check_this_ban # short-circuits at `!extban_flag_supported('q')` before add_mode. extban = "~,acjmqrUz" drive_registration(mock_ircd, isupport_tokens=[f"EXTBAN={extban}"]) chan = drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "EXTBAN", extban) # Confirm preconditions for the +b add_mode path: # - dynamicbans is on (otherwise the path under test never gates) # - bot is op (HALFOP_CANTDOMODE('b') would short-circuit otherwise) assert tcl_bridge.eval_ok(f'channel get "{chan}" dynamicbans') == "1" assert tcl_bridge.eval_ok(f'isop TestBot "{chan}"') == "1" tcl_bridge.eval_ok(f'newchanban "{chan}" q:badmouth testuser comment') # Force-flush the mode buffer instead of waiting on HOOK_IDLE. tcl_bridge.eval_ok(f'flushmode "{chan}"') # The bot should send a MODE for chan that includes our extban as the +b # arg. The mode letters can be batched with chanmode protection (e.g. # "+tnb") so we just look for the unambiguous mask payload on a MODE line. mock_ircd.drain_until( lambda line: line.startswith(f"MODE {chan} ") and "q:badmouth" in line, timeout=5.0, ) def test_enforceable_account_extban_not_queued_on_dynamicbans_channel_without_match( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """An *enforceable* account extban for an account no current member has must NOT be set proactively on a `+dynamicbans` channel. The condition `extban_is_unenforceable("a:nooneactual")` returns false (because acc flag matches), so it falls back to the standard dynamic-bans rule: only set when a matching member triggers it. """ drive_registration( mock_ircd, isupport_tokens=["EXTBAN=~,acrjmU", "ACCOUNTEXTBAN=a"], ) chan = drive_join_with_names(mock_ircd, "@TestBot alice") wait_for_isupport(tcl_bridge, "ACCOUNTEXTBAN", "a") assert tcl_bridge.eval_ok(f'channel get "{chan}" dynamicbans') == "1" tcl_bridge.eval_ok(f'newchanban "{chan}" a:nooneactual testuser comment') tcl_bridge.eval_ok(f'flushmode "{chan}"') # No MODE +b should reach the IRCd within a reasonable window. # Use a short drain that *requires* a +b ... a:nooneactual to assert non-presence: # if the predicate never matches and we get a MockIrcdError on timeout, we win. from support.mock_ircd import MockIrcdError with pytest.raises(MockIrcdError): mock_ircd.drain_until( lambda line: line.startswith(f"MODE {chan} ") and "a:nooneactual" in line, timeout=2.0, ) # And the userfile record exists regardless. assert tcl_bridge.eval_ok(f'isban a:nooneactual "{chan}"') == "1" # And it was not auto-stickified. assert tcl_bridge.eval_ok(f'isbansticky a:nooneactual "{chan}"') == "0" def test_enforcebans_account_extban_kicks_after_account_change( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """+enforcebans + an account-extban + a member who was on the channel *before* the matching account was set: when the server sends ACCOUNT (account-notify capability), the bot must add +b for the extban and KICK the now-matching user. Path under test: ACCOUNT msg → got_account (chan.c:2877) → setaccount (chan.c:179) → banmask_list_matches_member finds the userfile ban → refresh_ban_kick → do_mask sets +b a:badname and kick_all sends KICK. `+enforcebans` is set explicitly so the test pins the "kick on account-match" intent independent of dynamic-bans defaults. Before the ACCOUNT message arrives, alice's m->account is empty, so `banmask_matches_member` correctly returns false and nothing fires. """ drive_registration( mock_ircd, isupport_tokens=["EXTBAN=~,acrjmU", "ACCOUNTEXTBAN=a"], ) chan = drive_join_with_names(mock_ircd, "@TestBot alice") wait_for_isupport(tcl_bridge, "ACCOUNTEXTBAN", "a") # Preconditions for the kick path: bot is op, alice is on chan, # alice has no account yet, +enforcebans is on. assert tcl_bridge.eval_ok(f'isop TestBot "{chan}"') == "1" assert tcl_bridge.eval_ok(f'onchan alice "{chan}"') == "1" tcl_bridge.eval_ok(f'channel set "{chan}" +enforcebans') assert tcl_bridge.eval_ok(f'channel get "{chan}" enforcebans') == "1" # Add the account-extban targeting an account alice doesn't yet have. # check_this_ban iterates members; alice's m->account is "" so # banmask_matches_member returns false. Nothing about a:badname goes # out — verified below by the negative drain. tcl_bridge.eval_ok(f'newchanban "{chan}" a:badname testuser comment') tcl_bridge.eval_ok(f'flushmode "{chan}"') from support.mock_ircd import MockIrcdError with pytest.raises(MockIrcdError): mock_ircd.drain_until( lambda line: "a:badname" in line or ( line.startswith(f"KICK {chan} alice") ), timeout=2.0, ) # Alice logs in to the matching account. mock_ircd.send(":alice!u@h.example.com ACCOUNT badname") # Bot must (a) set +b a:badname on the channel and (b) KICK alice. # Modes get flushed by add_mode/flush_mode in the refresh_ban_kick # path; KICK goes via DP_SERVER directly. Both should arrive within # the IDLE flush cycle. seen = mock_ircd.drain_until( lambda line: line.startswith(f"KICK {chan} alice"), timeout=5.0, ) assert any( line.startswith(f"MODE {chan} ") and "a:badname" in line for line in seen ), f"expected MODE +b a:badname before the KICK; got {seen}" # ---------- userfile loading: extbans loaded regardless of EXTBAN advertised ---------- def test_extbans_load_from_userfile_before_connect( eggdrop_config, request: pytest.FixtureRequest, ) -> None: """Extbans stored in the userfile must be loaded into memory at startup, independent of any EXTBAN/ACCOUNTEXTBAN ISUPPORT data — that data isn't available until after the bot connects. The load path goes through `restore_chanban → addmask_fully` (users.c), which doesn't call `isupport_get` or any of the new extban helpers. Verified by spawning without driving registration. The bans are rendered into the userfile via the `userfile_bans` / `userfile_chan_bans` template variables (see templates/userfile.j2). """ eggdrop_config.render( userfile_ban_lines=[ format_userfile_ban( mask="a:storedacct", perm=True, sticky=False, expire=0, added=1700000000, lastactive=0, creator="owner", desc="loaded from userfile", ), format_userfile_ban( mask="q:storedmute", perm=True, sticky=False, expire=0, added=1700000000, lastactive=0, creator="owner", desc="loaded from userfile", ), format_userfile_ban( mask="U:strangers!*@*", perm=True, sticky=False, expire=0, added=1700000000, lastactive=0, creator="owner", desc="loaded from userfile", ), ], userfile_chan_ban_lines={ "#test": [ format_userfile_ban( mask="~a:chanonlyacct", perm=True, sticky=False, expire=0, added=1700000000, lastactive=0, creator="owner", desc="loaded from userfile", ), ], }, ) request.getfixturevalue("eggdrop_proc") bridge: BridgeClient = request.getfixturevalue("tcl_bridge") # Bot has not yet connected — confirm ISUPPORT is empty for both keys # the new helpers consult. Then bans must still be present in memory. assert bridge.eval_ok("set ::account-extban") == "" # Global extbans loaded. assert bridge.eval_ok("isban a:storedacct") == "1" assert bridge.eval_ok("isban q:storedmute") == "1" assert bridge.eval_ok("isban U:strangers!*@*") == "1" # Per-channel extban loaded. assert bridge.eval_ok("isban ~a:chanonlyacct #test") == "1" # And — critically — none of them got auto-stickified by the new # u_addban code path, because the load path bypasses u_addban entirely. assert bridge.eval_ok("isbansticky a:storedacct") == "0" assert bridge.eval_ok("isbansticky q:storedmute") == "0" assert bridge.eval_ok("isbansticky ~a:chanonlyacct #test") == "0" def test_extban_perm_sticky_flags_survive_userfile_roundtrip( eggdrop_config, request: pytest.FixtureRequest, ) -> None: """A perm + sticky extban *that was already in the userfile* loads with those flags intact. Verifies that the `+`/`*` flag characters in the record format aren't confused by the hex-escaped `:` in the mask. """ eggdrop_config.render( userfile_ban_lines=[ format_userfile_ban( mask="a:permsticky", perm=True, sticky=True, expire=0, added=1700000000, lastactive=0, creator="owner", desc="perm-sticky from userfile", ), ], ) request.getfixturevalue("eggdrop_proc") bridge: BridgeClient = request.getfixturevalue("tcl_bridge") assert bridge.eval_ok("isban a:permsticky") == "1" assert bridge.eval_ok("isbansticky a:permsticky") == "1" assert bridge.eval_ok("ispermban a:permsticky") == "1" # ---------- partyline .+ban / .+extban across connection states ---------- @pytest.mark.partyline def test_partyline_pls_ban_extban_works_when_connected( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`.+ban a:foo` (extban via the generic +ban command, not +extban) is accepted while connected. The mask is stored verbatim — no prefix construction (that's +extban's job). """ drive_registration( mock_ircd, isupport_tokens=["EXTBAN=~,acrjmU", "ACCOUNTEXTBAN=a"], ) drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "EXTBAN", "~,acrjmU") eggdrop_proc.send_partyline(".+ban a:connectedacct #test why") wait_for( lambda: tcl_bridge.eval_ok("isban a:connectedacct #test") == "1", timeout=5.0, description="partyline .+ban (extban form) to register on #test", ) # No auto-sticky. assert tcl_bridge.eval_ok("isbansticky a:connectedacct #test") == "0" @pytest.mark.partyline def test_partyline_pls_ban_extban_works_when_not_yet_connected( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`.+ban a:foo` works even before 005 has been received — `.+ban` has no connection gate (only `.+extban` does, since only `+extban` needs the prefix). The "extban not enabled" warning fires (because EXTBAN ISUPPORT is unknown) but the ban is still stored. """ # Deliberately: NO drive_registration() — bot is pre-welcome. snapshot = len(eggdrop_proc.stdout_text()) eggdrop_proc.send_partyline(".+ban a:disconnectedacct why") wait_for( lambda: tcl_bridge.eval_ok("isban a:disconnectedacct") == "1", timeout=5.0, description="partyline .+ban (extban form) to register while disconnected", ) # Storage: not auto-stickified (regression for option-1 swap). assert tcl_bridge.eval_ok("isbansticky a:disconnectedacct") == "0" # And the user got the EXTBAN-not-enabled feedback. The message text # comes from EXTBAN_NOT_ENABLED1/2/3 in the language file; we look for # a stable substring rather than the full template. new_output = eggdrop_proc.stdout_text()[snapshot:] assert "extban is not enabled on this server" in new_output, new_output @pytest.mark.partyline def test_partyline_pls_extban_works_when_connected_already_covered( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """Sanity counterpart to the `.+extban` disconnected test above: same command, but with 005 received first, succeeds and stores the prefixed mask. (Mostly redundant with `_constructs_prefixed_mask` above — included so the connected/disconnected pair reads cleanly next to each other.) """ drive_registration(mock_ircd, isupport_tokens=["EXTBAN=~,acrjmU"]) drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "EXTBAN", "~,acrjmU") eggdrop_proc.send_partyline(".+extban a Bar #test") wait_for( lambda: tcl_bridge.eval_ok("isban ~a:Bar #test") == "1", timeout=5.0, description="connected .+extban to register ~a:Bar on #test", ) # ---------- partyline behaviour without server.mod loaded ---------- @pytest.mark.partyline def test_partyline_pls_ban_extban_works_without_server_mod( eggdrop_config, request: pytest.FixtureRequest, ) -> None: """`.+ban a:foo` stores the extban even when server.mod isn't loaded at all — the storage path doesn't need ISUPPORT, and the `servermod_isupport_get` thunk returns NULL safely when `module_find("server")` finds nothing. Without server.mod: irc.mod and ctcp.mod also can't load (they `module_depend` on server). channels.mod loads cleanly because the cross-module API bypass was fixed (commit 8cfcd51e). The bot has no outbound IRC connection at all in this configuration. Driven via the `modules` template variable — see tests/templates/eggdrop.conf.j2 and the `EggdropConfig.context()` default in conftest.py. Render must happen *before* the proc fixture evaluates, so the proc/bridge are pulled in lazily via getfixturevalue. """ eggdrop_config.render(modules=["pbkdf2", "channels", "console", "notes"]) proc: EggdropProc = request.getfixturevalue("eggdrop_proc") bridge: BridgeClient = request.getfixturevalue("tcl_bridge") # Sanity: server.mod is genuinely not loaded. assert bridge.eval_ok("expr {[catch {set ::server-online}] != 0}") == "1" # `.+ban` with an extban mask: stored, no errors. proc.send_partyline(".+ban a:noservermod why") wait_for( lambda: bridge.eval_ok("isban a:noservermod") == "1", timeout=5.0, description=".+ban (extban form) to register without server.mod", ) assert bridge.eval_ok("isbansticky a:noservermod") == "0" # `.+extban`: refused. The thunk returns NULL → no EXTBAN known → # gated by the same check that fires when disconnected. snapshot = len(proc.stdout_text()) proc.send_partyline(".+extban a foo") wait_for( lambda: "must be connected to a server with EXTBAN support" in proc.stdout_text()[snapshot:], timeout=5.0, description=".+extban refusal text without server.mod loaded", ) # Nothing stored from the +extban attempt. assert bridge.eval_ok("isban a:foo") == "0" assert bridge.eval_ok("isban ~a:foo") == "0" # ---------- dynamic ban-time expiry for channel banlist masks ---------- @pytest.mark.timeout(90) def test_dynamic_ban_time_expiry_removes_normal_and_extban_channel_modes( eggdrop_config, mock_ircd: MockIrcd, request: pytest.FixtureRequest, ) -> None: """`+dynamicbans` expires normal and extended channel ban modes alike. This drives server-side +b modes rather than internal userfile bans so the assertions cover the channel banlist cleanup path in irc.mod. `ban-time` is set negative to make the masks eligible on the next minutely hook without a multi-minute sleep; the dynamicbans gate is still the real one. """ eggdrop_config.render( channels=[ {"name": "#dyn", "chanmode": "+nt"}, {"name": "#static", "chanmode": "+nt"}, ] ) request.getfixturevalue("eggdrop_proc") tcl_bridge: BridgeClient = request.getfixturevalue("tcl_bridge") drive_registration( mock_ircd, isupport_tokens=["EXTBAN=$,aUq", "ACCOUNTEXTBAN=a"], ) dyn_chan = drive_join_with_names(mock_ircd, "@TestBot") static_chan = drive_join_with_names(mock_ircd, "@TestBot") assert dyn_chan == "#dyn" assert static_chan == "#static" tcl_bridge.eval_ok(f'channel set "{dyn_chan}" +dynamicbans ban-time -1') tcl_bridge.eval_ok(f'channel set "{static_chan}" -dynamicbans ban-time -1') assert tcl_bridge.eval_ok(f'channel get "{dyn_chan}" dynamicbans') == "1" assert tcl_bridge.eval_ok(f'channel get "{static_chan}" dynamicbans') == "0" dynamic_masks = ["*!*@dynamic.example", "$a:geo"] static_masks = ["*!*@static.example", "$a:staticgeo"] for mask in dynamic_masks: mock_ircd.send(f":Oper!oper@mock MODE {dyn_chan} +b {mask}") for mask in static_masks: mock_ircd.send(f":Oper!oper@mock MODE {static_chan} +b {mask}") for mask in dynamic_masks: wait_for( lambda mask=mask: tcl_bridge.eval_ok( f'ischanban "{dyn_chan}" "{mask}"' ) == "1", timeout=5.0, description=f"{mask} to appear on {dyn_chan}", ) for mask in static_masks: wait_for( lambda mask=mask: tcl_bridge.eval_ok( f'ischanban "{static_chan}" "{mask}"' ) == "1", timeout=5.0, description=f"{mask} to appear on {static_chan}", ) removed_dynamic: set[str] = set() def saw_dynamic_removal(line: str) -> bool: if line.startswith(f"MODE {dyn_chan} ") and "-b" in line: for mask in dynamic_masks: if mask in line: removed_dynamic.add(mask) return len(removed_dynamic) == len(dynamic_masks) mock_ircd.drain_until(saw_dynamic_removal, timeout=70.0) assert removed_dynamic == set(dynamic_masks) with pytest.raises(MockIrcdError): mock_ircd.drain_until( lambda line: line.startswith(f"MODE {static_chan} ") and "-b" in line and any(mask in line for mask in static_masks), timeout=2.0, ) eggheads-eggdrop-9c00fe1/tests/tests/test_framing.py000066400000000000000000000054631524730042700226730ustar00rootroot00000000000000"""Unit tests for the line-delimited framing helpers.""" from __future__ import annotations import pytest from support.framing import ( ProtocolError, encode_request, encode_response, escape, parse_response, unescape, ) @pytest.mark.parametrize( ("raw", "encoded"), [ ("", ""), ("hello", "hello"), ("foo bar", "foo bar"), ("a\\b", "a\\\\b"), ("a\nb", "a\\nb"), ("a\rb", "a\\rb"), ("\\", "\\\\"), ("\n", "\\n"), ("\r", "\\r"), ("\\n", "\\\\n"), # literal backslash + n, NOT a newline ("a\\\nb", "a\\\\\\nb"), # backslash then newline ("héllo", "héllo"), # unicode passes through ], ) def test_escape_unescape_roundtrip(raw: str, encoded: str) -> None: assert escape(raw) == encoded assert unescape(encoded) == raw def test_unescape_dangling_backslash_raises() -> None: with pytest.raises(ProtocolError, match="dangling backslash"): unescape("foo\\") def test_unescape_unknown_escape_raises() -> None: with pytest.raises(ProtocolError, match="unknown escape"): unescape("foo\\x") def test_encode_request_appends_newline() -> None: assert encode_request("set foo 42") == b"set foo 42\n" def test_encode_request_escapes_newline() -> None: assert encode_request("a\nb") == b"a\\nb\n" def test_encode_response_format() -> None: assert encode_response("OK", "42") == b"OK 42\n" assert encode_response("ERR", "bad") == b"ERR bad\n" def test_encode_response_escapes_payload() -> None: assert encode_response("OK", "line1\nline2") == b"OK line1\\nline2\n" def test_parse_response_ok() -> None: assert parse_response("OK 42\n") == ("OK", "42") def test_parse_response_err() -> None: assert parse_response("ERR bad command\n") == ("ERR", "bad command") def test_parse_response_empty_payload() -> None: assert parse_response("OK\n") == ("OK", "") assert parse_response("OK \n") == ("OK", "") def test_parse_response_unescapes() -> None: assert parse_response("OK line1\\nline2\n") == ("OK", "line1\nline2") assert parse_response("OK a\\\\b\n") == ("OK", "a\\b") def test_parse_response_strips_crlf() -> None: assert parse_response("OK 42\r\n") == ("OK", "42") @pytest.mark.parametrize( "payload", [ "", "simple", "with spaces and stuff", "newlines\nare\nfine", "carriage\rreturn", "back\\slash", "all\\of\nthe\rabove", "unicode: héllo, 日本語", "a" * 4096, ], ) def test_response_roundtrip(payload: str) -> None: framed = encode_response("OK", payload) assert framed.endswith(b"\n") assert framed.count(b"\n") == 1, "frame must be exactly one line" line = framed.decode("utf-8") assert parse_response(line) == ("OK", payload) eggheads-eggdrop-9c00fe1/tests/tests/test_ident_scenarios.py000066400000000000000000000223221524730042700244120ustar00rootroot00000000000000"""Ident lookup scenarios for inbound DCC/telnet connections. Exercises `dcc_telnet_hostresolved2` (src/dcc.c) which is responsible for performing an RFC 1413 ident query back to the originator of an inbound DCC connection. Three real-world outcomes are covered: * "refused" — nothing listening on the ident port; kernel RSTs the SYN. `connect_nonblock` returns -4 and the host falls back to `telnet@`. * "timeout" — ident port accepts but never writes a reply. After `ident-timeout` seconds, `timeout_dcc_ident` fires and the host falls back to `telnet@`. * "respond" — ident port replies with a valid `USERID : UNIX : ` line; the host is set to `@`. Each scenario is run twice — once with `ident-timeout 0` (eggdrop skips the lookup entirely and always falls back to `telnet@`) and once with `ident-timeout 3` (eggdrop actually does the lookup). The bot connects to TCP/1113 instead of the wire-standard 113 when `EGGDROP_TEST` is set in the env (see src/dcc.c:dcc_telnet_hostresolved2) so the test process doesn't need privileged-port access. """ from __future__ import annotations import contextlib import re import socket from collections.abc import Iterator from typing import Any import pytest from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc # noqa: F401 (fixture name) from support.identd import IdentServer from support.waiters import wait_for # Owner record matches whatever reverse-DNS returns for 127.0.0.1 # (`localhost`, `127.0.0.1`, or anything in /etc/hosts) so `protect-telnet` # accepts the inbound connection regardless of the resolver. The user has # 'n' flag (owner), which implies USER_OP for the `protect_telnet` check # in dcc_telnet_got_ident. OWNER_HOSTMASK = "*!*@*" def _pick_free_port() -> int: """Bind to :0, read the assigned port, release. Best-effort — race window between close and the eggdrop child binding the same port, but on Linux the kernel doesn't immediately reissue closed ports so this is fine.""" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] @contextlib.contextmanager def _telnet_client(host: str, port: int) -> Iterator[socket.socket]: """Open a TCP connection to the bot's listen port and keep it open for the lifetime of the `with` block. The dcc entry lives only as long as the socket — closing it ends the test scenario from the bot's side.""" s = socket.create_connection((host, port), timeout=3.0) try: yield s finally: with contextlib.suppress(OSError): s.shutdown(socket.SHUT_RDWR) s.close() def _wait_for_telnet_id_host(bridge: BridgeClient, timeout: float) -> str: """Poll `dcclist TELNET_ID` until one entry exists; return its host. The dcc entry transitions DCC_TELNET → DCC_DNSWAIT → DCC_IDENTWAIT → DCC_TELNET_ID once host resolution + ident finish. We watch the final state. The host field is `@` per `dcc_telnet_got_ident` (src/dcc.c).""" cmd = ( "if {[llength [set l [dcclist TELNET_ID]]]} " "{lindex [lindex $l 0] 2}" ) host: dict[str, str] = {} def _check() -> bool: result = bridge.eval_ok(cmd) if result: host["v"] = result return True return False wait_for(_check, timeout=timeout, description="dcc TELNET_ID entry to appear") return host["v"] # ---------- ident-timeout 0: lookup is skipped, host always telnet@... ---------- def test_ident_disabled_with_no_identd( eggdrop_config: Any, request: pytest.FixtureRequest, ) -> None: """ident-timeout=0 + no ident responder → host is `telnet@`. Verifies the early-return path in dcc_telnet_hostresolved2: identtimeout is checked *before* the connect to 1113, so the identd presence is irrelevant. The userhost should be the fallback `telnet@...` form. """ listen_port = _pick_free_port() eggdrop_config.render( owner_hostmask=OWNER_HOSTMASK, extra_tcl=f"set ident-timeout 0\nlisten {listen_port} all\n", ) bridge: BridgeClient = request.getfixturevalue("tcl_bridge") with _telnet_client("127.0.0.1", listen_port): host = _wait_for_telnet_id_host(bridge, timeout=5.0) assert re.fullmatch(r"telnet@(127\.0\.0\.1|localhost|localhost\.\S+)", host), ( f"expected telnet@, got {host!r}" ) def test_ident_disabled_with_responder_running( eggdrop_config: Any, request: pytest.FixtureRequest, ) -> None: """ident-timeout=0 + identd running → identd is NOT contacted; host stays `telnet@`. Confirms the skip path doesn't accidentally fire the lookup. """ listen_port = _pick_free_port() eggdrop_config.render( owner_hostmask=OWNER_HOSTMASK, extra_tcl=f"set ident-timeout 0\nlisten {listen_port} all\n", ) bridge: BridgeClient = request.getfixturevalue("tcl_bridge") with ( IdentServer("respond", user="alice"), _telnet_client("127.0.0.1", listen_port), ): host = _wait_for_telnet_id_host(bridge, timeout=5.0) assert re.fullmatch(r"telnet@(127\.0\.0\.1|localhost|localhost\.\S+)", host), ( f"expected telnet@ (identd ignored), got {host!r}" ) def test_ident_disabled_with_hung_responder( eggdrop_config: Any, request: pytest.FixtureRequest, ) -> None: """ident-timeout=0 + identd that hangs → host is `telnet@` immediately (no waiting on the hung server).""" listen_port = _pick_free_port() eggdrop_config.render( owner_hostmask=OWNER_HOSTMASK, extra_tcl=f"set ident-timeout 0\nlisten {listen_port} all\n", ) bridge: BridgeClient = request.getfixturevalue("tcl_bridge") with ( IdentServer("timeout"), _telnet_client("127.0.0.1", listen_port), ): # Wait window deliberately tight: with ident-timeout=0 the # lookup is skipped, so the entry should appear in <1s. host = _wait_for_telnet_id_host(bridge, timeout=2.0) assert re.fullmatch(r"telnet@(127\.0\.0\.1|localhost|localhost\.\S+)", host), ( f"expected telnet@ (identd ignored), got {host!r}" ) # ---------- ident-timeout 3: lookup actually runs ---------- def test_ident_lookup_refused( eggdrop_config: Any, request: pytest.FixtureRequest, ) -> None: """ident-timeout=3 + nothing on TCP/1113 → kernel RST → host falls back to `telnet@`. connect_nonblock detects ECONNREFUSED via its own 500ms select, so this resolves quickly regardless of ident-timeout.""" listen_port = _pick_free_port() eggdrop_config.render( owner_hostmask=OWNER_HOSTMASK, extra_tcl=f"set ident-timeout 3\nlisten {listen_port} all\n", ) bridge: BridgeClient = request.getfixturevalue("tcl_bridge") with _telnet_client("127.0.0.1", listen_port): host = _wait_for_telnet_id_host(bridge, timeout=5.0) assert re.fullmatch(r"telnet@(127\.0\.0\.1|localhost|localhost\.\S+)", host), ( f"expected telnet@ (ident refused), got {host!r}" ) @pytest.mark.slow def test_ident_lookup_timeout( eggdrop_config: Any, request: pytest.FixtureRequest, ) -> None: """ident-timeout=3 + identd accepts but never replies → DCC_IDENT timeout eventually fires; host falls back to `telnet@`. This is the case that *cannot* be exercised by leaving 1113 unbound — we need the TCP handshake to succeed so the dcc enters DCC_IDENT state and the app-level identtimeout actually counts down. Eggdrop's `check_expired_dcc` only runs every 10s (src/main.c:556) so the effective timeout latency is `identtimeout + (up to 10s)`. The 15s wait below covers the worst case. """ listen_port = _pick_free_port() eggdrop_config.render( owner_hostmask=OWNER_HOSTMASK, extra_tcl=f"set ident-timeout 3\nlisten {listen_port} all\n", ) bridge: BridgeClient = request.getfixturevalue("tcl_bridge") with ( IdentServer("timeout"), _telnet_client("127.0.0.1", listen_port), ): host = _wait_for_telnet_id_host(bridge, timeout=15.0) assert re.fullmatch(r"telnet@(127\.0\.0\.1|localhost|localhost\.\S+)", host), ( f"expected telnet@ (ident timed out), got {host!r}" ) def test_ident_lookup_success( eggdrop_config: Any, request: pytest.FixtureRequest, ) -> None: """ident-timeout=3 + identd replies `USERID : UNIX : alice` → host is `alice@`. Verifies the full happy-path parse in `dcc_ident`.""" listen_port = _pick_free_port() eggdrop_config.render( owner_hostmask=OWNER_HOSTMASK, extra_tcl=f"set ident-timeout 3\nlisten {listen_port} all\n", ) bridge: BridgeClient = request.getfixturevalue("tcl_bridge") with ( IdentServer("respond", user="alice"), _telnet_client("127.0.0.1", listen_port), ): host = _wait_for_telnet_id_host(bridge, timeout=5.0) assert re.fullmatch(r"alice@(127\.0\.0\.1|localhost|localhost\.\S+)", host), ( f"expected alice@, got {host!r}" ) eggheads-eggdrop-9c00fe1/tests/tests/test_isupport_modes.py000066400000000000000000000334541524730042700243250ustar00rootroot00000000000000"""Tests for arbitrary chan-modes (isupport PREFIX + CHANMODES) handling. Each test is self-contained — the realistic isupport strings used (drawn from the top-3 most-popular advertised values at https://stats.ircdocs.horse/isupport/) appear inline so the test reads as a complete story. Coverage: - 005 parsing of PREFIX and CHANMODES → debug log + isupport state - `.status all` on the partyline reflects what was parsed - Raw 324 (RPL_CHANNELMODEIS) with mixed known/unknown modes after join - Inbound MODE messages mixing prefix modes, hardcoded modes, and unknown modes — Eggdrop must skip what it doesn't know but still apply known ones in the right slots (op grant, key after junk modes). """ from __future__ import annotations import re import pytest from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.irc_helpers import ( drive_join_with_names, drive_registration, wait_for_isupport, ) from support.mock_ircd import MockIrcd from support.waiters import wait_for # ---------- 005 parsing: debug log + isupport state ---------- def test_005_parses_prefix_qaohv_and_chanmodes_top1( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """Most-popular PREFIX (qaohv, 123 networks) + most-popular CHANMODES. Each prefix mode and each LIST/KEY chanmode produces a corresponding "Learned mode type: ..." debug line, and isupport state matches. """ prefix = "(qaohv)~&@%+" chanmodes = "beI,kLf,l,psmntirzMQNRTOVKDdGPZSCc" drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) wait_for_isupport(tcl_bridge, "PREFIX", prefix) wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) log = eggdrop_proc.log_path.read_text() # Each prefix letter → symbol pair must appear in the debug log. for letter, symbol in [("q", "~"), ("a", "&"), ("o", "@"), ("h", "%"), ("v", "+")]: pat = ( rf"Learned mode type: \+{letter} type Prefix, " rf"prefixchar {re.escape(symbol)}" ) assert re.search(pat, log), f"missing prefix debug line for +{letter} {symbol}" # CHANMODES sections: LIST=beI, KEY=kLf. for letter in "beI": assert re.search(rf"Learned mode type: \+{letter} type List(?!.)", log), ( f"missing List debug line for +{letter}" ) for letter in "kLf": assert re.search(rf"Learned mode type: \+{letter} type Key(?!.)", log), ( f"missing Key debug line for +{letter}" ) def test_005_parses_prefix_ohv_and_chanmodes_top2( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """Second-most-popular PREFIX (47 networks) + second-most-popular CHANMODES.""" prefix = "(ohv)@%+" chanmodes = "beI,kfL,lj,psmntirRcOAQKVCuzNSMTG" drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) wait_for_isupport(tcl_bridge, "PREFIX", prefix) wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) log = eggdrop_proc.log_path.read_text() for letter, symbol in [("o", "@"), ("h", "%"), ("v", "+")]: pat = ( rf"Learned mode type: \+{letter} type Prefix, " rf"prefixchar {re.escape(symbol)}" ) assert re.search(pat, log), f"missing prefix debug line for +{letter} {symbol}" # CHANMODES sections: LIST=beI, KEY=kfL. for letter in "beI": assert re.search(rf"Learned mode type: \+{letter} type List(?!.)", log) for letter in "kfL": assert re.search(rf"Learned mode type: \+{letter} type Key(?!.)", log) def test_005_parses_prefix_ov_and_chanmodes_top3( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """Basic PREFIX (op + voice only) + CHANMODES with q/a as LIST modes instead of prefix modes.""" prefix = "(ov)@+" chanmodes = "beIqa,kLf,l,psmntirzMQNRTOVKDdGPZS" drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) wait_for_isupport(tcl_bridge, "PREFIX", prefix) wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) log = eggdrop_proc.log_path.read_text() for letter, symbol in [("o", "@"), ("v", "+")]: pat = ( rf"Learned mode type: \+{letter} type Prefix, " rf"prefixchar {re.escape(symbol)}" ) assert re.search(pat, log), f"missing prefix debug line for +{letter} {symbol}" # CHANMODES sections: LIST=beIqa (note 'q' and 'a' are LIST here, not prefix), KEY=kLf. for letter in "beIqa": assert re.search(rf"Learned mode type: \+{letter} type List(?!.)", log) for letter in "kLf": assert re.search(rf"Learned mode type: \+{letter} type Key(?!.)", log) # ---------- use-exempts / use-invites derive from CHANMODES ---------- def test_use_exempts_and_invites_set_when_e_and_I_in_list( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`use-exempts` / `use-invites` are turned on when 'e' and 'I' are in the LIST section of CHANMODES (the typical case for major IRCds).""" chanmodes = "beI,kLf,l,psmntirzMQNRTOVKDdGPZSCc" # 'e' and 'I' both in LIST drive_registration(mock_ircd, isupport_tokens=[f"CHANMODES={chanmodes}"]) wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) assert tcl_bridge.eval_ok("set ::use-exempts") == "1" assert tcl_bridge.eval_ok("set ::use-invites") == "1" def test_use_exempts_off_when_e_not_in_list( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """If 'e' is missing from the LIST section, use-exempts goes to 0 (use-invites stays on because 'I' is still in LIST).""" chanmodes = "bI,k,l,imnpst" # no 'e' in list section drive_registration(mock_ircd, isupport_tokens=[f"CHANMODES={chanmodes}"]) wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) assert tcl_bridge.eval_ok("set ::use-exempts") == "0" assert tcl_bridge.eval_ok("set ::use-invites") == "1" # ---------- .status all on the partyline ---------- @pytest.mark.partyline def test_status_all_reports_parsed_isupport( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """`.status all` partyline output contains an `isupport:` line including the PREFIX and CHANMODES we sent in 005.""" prefix = "(qaohv)~&@%+" chanmodes = "beI,kLf,l,psmntirzMQNRTOVKDdGPZSCc" drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) wait_for_isupport(tcl_bridge, "PREFIX", prefix) # Mark a snapshot so we only scan output produced after sending the cmd. snapshot_offset = len(eggdrop_proc.stdout_text()) eggdrop_proc.send_partyline(".status all") def has_isupport_line() -> bool: new_output = eggdrop_proc.stdout_text()[snapshot_offset:] return any( "isupport:" in line and "PREFIX=" in line and "CHANMODES=" in line for line in new_output.splitlines() ) wait_for( has_isupport_line, timeout=5.0, description="`.status all` to print the isupport: line", ) new_output = eggdrop_proc.stdout_text()[snapshot_offset:] assert f"PREFIX={prefix}" in new_output assert f"CHANMODES={chanmodes}" in new_output # ---------- raw 324 with mixed modes ---------- def test_got324_skips_unknown_mode_but_applies_key( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """324 `+fk test_f test_k` — `f` (forward, common on Charybdis) is unknown to Eggdrop but isupport says it has a parameter; `f` is skipped, the key `test_k` still applies.""" prefix = "(ohv)@%+" # 'f' lives in the KEY section here (kfL), so isupport says it takes an arg. chanmodes = "beI,kfL,lj,psmntirRcOAQKVCuzNSMTG" drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) chan = drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) mock_ircd.send(f":mock.test 324 TestBot {chan} +fk test_f test_k") wait_for( lambda: "test_k" in tcl_bridge.eval_ok(f'getchanmode "{chan}"'), timeout=5.0, description="324 to apply key=test_k", ) chanmode_str = tcl_bridge.eval_ok(f'getchanmode "{chan}"') # Format is "+k " — verify 'k' is set, key is correct, # and 'f' (unknown) does not appear in the flags. flags, _, key = chanmode_str.partition(" ") assert "k" in flags, chanmode_str assert "f" not in flags, chanmode_str assert key.strip() == "test_k", chanmode_str def test_got324_conflict_eggdrop_says_noargs_isupport_says_args( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """If isupport puts a mode Eggdrop hardcodes as no-arg into a section with args, got324 logs a warning and skips that mode (consuming its arg). The next mode in line still gets parsed correctly. Constructed CHANMODES: 'q' (Eggdrop hardcodes it as no-arg / quiet) is placed in the LIST section (with-arg). Sending `324 +qk arg_q test_k` must skip the `q arg_q` pair entirely and still apply `+k test_k`. """ chanmodes = "beIq,k,l,imnpst" # 'q' in LIST → isupport says it takes arg drive_registration(mock_ircd, isupport_tokens=[f"CHANMODES={chanmodes}"]) chan = drive_join_with_names(mock_ircd, "@TestBot") wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) mock_ircd.send(f":mock.test 324 TestBot {chan} +qk arg_q test_k") wait_for( lambda: "test_k" in tcl_bridge.eval_ok(f'getchanmode "{chan}"'), timeout=5.0, description="324 to apply key=test_k after skipped +q", ) log = eggdrop_proc.log_path.read_text() assert re.search( r"Eggdrop assumes mode change \+q has no parameter but isupport says yes", log, ), "expected the +q conflict warning in the log" # ---------- inbound MODE while joined ---------- def test_gotmode_op_via_prefix_after_unknown_mode_with_arg( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """MODE `+ofk alice test_f test_k` on a joined channel: - alice gets opped (prefix mode +o consumes its arg correctly), - 'f' is consumed-and-discarded (unknown to Eggdrop but isupport says it takes an arg), - 'k' still applies with `test_k`. """ prefix = "(ohv)@%+" chanmodes = "beI,kfL,lj,psmntirRcOAQKVCuzNSMTG" # 'f' is KEY-type here drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) chan = drive_join_with_names(mock_ircd, "@TestBot alice") wait_for_isupport(tcl_bridge, "CHANMODES", chanmodes) wait_for( lambda: tcl_bridge.eval_ok(f'onchan alice "{chan}"') == "1", timeout=5.0, description="alice to appear in chanlist", ) mock_ircd.send(f":someop!u@h MODE {chan} +ofk alice test_f test_k") wait_for( lambda: tcl_bridge.eval_ok(f'isop alice "{chan}"') == "1", timeout=5.0, description="alice to be opped via prefix +o", ) chanmode_str = tcl_bridge.eval_ok(f'getchanmode "{chan}"') flags, _, key = chanmode_str.partition(" ") assert key.strip() == "test_k", chanmode_str assert "f" not in flags, chanmode_str def test_gotmode_extended_prefix_modes_qa_consume_args_correctly( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """With PREFIX=(qaohv)~&@%+, MODE `+qav nick1 nick2 nick3` consumes args for each prefix mode (owner/admin/voice). Eggdrop only tracks op/halfop/voice flags by default, but the parser must still consume all three args correctly so the next mode in line isn't shifted. Verified by checking voice on the third nick. """ prefix = "(qaohv)~&@%+" chanmodes = "beI,kLf,l,psmntirzMQNRTOVKDdGPZSCc" drive_registration( mock_ircd, isupport_tokens=[f"PREFIX={prefix}", f"CHANMODES={chanmodes}"] ) chan = drive_join_with_names(mock_ircd, "@TestBot owner_user admin_user voice_user") wait_for_isupport(tcl_bridge, "PREFIX", prefix) wait_for( lambda: tcl_bridge.eval_ok(f'onchan voice_user "{chan}"') == "1", timeout=5.0, description="voice_user to appear in chanlist", ) mock_ircd.send(f":someop!u@h MODE {chan} +qav owner_user admin_user voice_user") wait_for( lambda: tcl_bridge.eval_ok(f'isvoice voice_user "{chan}"') == "1", timeout=5.0, description="voice_user to be voiced after +qav consumed all 3 args", ) def test_names_with_extended_prefix_grants_op_when_opchars_includes_it( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """NAMES line with `~` (owner) prefix grants op when `opchars` contains it. Eggdrop's op-recognition uses the `opchars` set; for networks with extended prefixes admins typically configure `opchars "~&@"`. With that config, a `~user` in NAMES (and the corresponding WHO 352 with `H~` in flags) lands as op. """ prefix = "(qaohv)~&@%+" drive_registration(mock_ircd, isupport_tokens=[f"PREFIX={prefix}"]) # Set opchars to include owner/admin symbols. Must happen before the JOIN # is driven because that's when NAMES + WHO 352 entries are processed. tcl_bridge.eval_ok('set opchars "~&@"') chan = drive_join_with_names(mock_ircd, "@TestBot ~bigboss +regular") wait_for( lambda: tcl_bridge.eval_ok(f'onchan bigboss "{chan}"') == "1", timeout=5.0, description="bigboss to appear in chanlist", ) assert tcl_bridge.eval_ok(f'isop bigboss "{chan}"') == "1" assert tcl_bridge.eval_ok(f'isvoice regular "{chan}"') == "1" eggheads-eggdrop-9c00fe1/tests/tests/test_partyline_chan.py000066400000000000000000000025051524730042700242420ustar00rootroot00000000000000"""Partyline integration test: drive HQ partyline via stdin, verify via bridge.""" from __future__ import annotations import pytest from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.irc_helpers import drive_join_with_names, drive_registration from support.mock_ircd import MockIrcd from support.waiters import wait_for @pytest.mark.partyline def test_partyline_add_channel( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """Add a channel via the HQ partyline `.+chan` command, verify via bridge.""" drive_registration(mock_ircd) drive_join_with_names(mock_ircd, "@TestBot") # Sanity: only the templated #test is configured. assert tcl_bridge.eval_ok("llength [channels]") == "1" # Drive the HQ partyline. The HQ user is `-HQ` with full owner perms in # -nt mode, so no auth handshake is needed. eggdrop_proc.send_partyline(".+chan #pytest") # The command runs asynchronously inside Eggdrop's event loop. Poll the # bridge until the new channel is visible (or the wait_for times out). wait_for( lambda: tcl_bridge.eval_ok( 'expr {[lsearch [channels] "#pytest"] >= 0}' ) == "1", timeout=5.0, description="partyline .+chan #pytest to register", ) eggheads-eggdrop-9c00fe1/tests/tests/test_smoke_connect.py000066400000000000000000000024041524730042700240670ustar00rootroot00000000000000"""End-to-end smoke test: spawn Eggdrop, register, join, introspect via bridge.""" from __future__ import annotations from support.bridge_client import BridgeClient from support.eggdrop_proc import EggdropProc from support.irc_helpers import drive_join_with_names, drive_registration from support.mock_ircd import MockIrcd def test_bridge_alive(tcl_bridge: BridgeClient) -> None: """Eggdrop boots, the bridge listens, Tcl evaluates.""" assert tcl_bridge.eval_ok("expr {2 + 2}") == "4" assert tcl_bridge.eval_ok("info patchlevel") # non-empty assert tcl_bridge.eval_ok("set ::nick") == "TestBot" def test_connect_register_join_and_introspect( eggdrop_proc: EggdropProc, mock_ircd: MockIrcd, tcl_bridge: BridgeClient, ) -> None: """Full happy path: register, join, introspect.""" drive_registration(mock_ircd) chan = drive_join_with_names(mock_ircd, "@TestBot") assert chan == "#test" # Use lsearch in Tcl so list quoting (curly-brace wrapping of names that # start with '#') doesn't trip us up. assert tcl_bridge.eval_ok('expr {[lsearch [channels] "#test"] >= 0}') == "1" # Owner host from the rendered userfile is *!*@127.0.0.1. hosts = tcl_bridge.eval_ok("getuser owner HOSTS") assert "127.0.0.1" in hosts eggheads-eggdrop-9c00fe1/tests/tests/test_tcl_addbot.py000066400000000000000000000131351524730042700233420ustar00rootroot00000000000000"""Converted from eggdrop-tests/eggdrop_tcl_addbot.bats. Tests for the `addbot` Tcl command — adding a bot record with various address/port formats. Verifies the result is the expected `botaddr` triple `host port port` (or `host botport userport`). Skipped from the original bats: the two cases that required a second non-IPv6 eggdrop instance to verify rejection (port < 1, port > 65535). Those need a multi-bot harness which is out of scope for this framework. """ from __future__ import annotations import pytest from support.bridge_client import BridgeClient DEFAULT_PORT = "3333" # default-port set in the templated config BOT = "testbot" @pytest.fixture def ipv6_required(tcl_bridge: BridgeClient) -> None: """Skip the test if Eggdrop wasn't compiled with IPv6 support.""" # `info procs` won't work; check for IPv6 by trying an IPv6 add and seeing # if it succeeds. A simpler approach: peek at `set prefer-ipv6` (only set # when the build has IPv6). Use the actual `addbot` smoke test as the # truthy probe — addbot returns "0" if IPv6 isn't supported. tcl_bridge.eval_ok("deluser ipv6probe") if tcl_bridge.eval_ok("addbot ipv6probe ::1") != "1": pytest.skip("Eggdrop built without IPv6 support") tcl_bridge.eval_ok("deluser ipv6probe") # ---------- legacy 'addbot handle address ?botport ?userport??' format ---------- def test_addbot_handle_ipv4_uses_default_port(tcl_bridge: BridgeClient) -> None: assert tcl_bridge.eval_ok(f"addbot {BOT} 1.1.1.1") == "1" assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == f"1.1.1.1 {DEFAULT_PORT} {DEFAULT_PORT}" ) def test_addbot_handle_ipv6_uses_default_port( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert tcl_bridge.eval_ok(f"addbot {BOT} fe80::69ec:cfe4:81de:4fe5") == "1" assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == f"fe80::69ec:cfe4:81de:4fe5 {DEFAULT_PORT} {DEFAULT_PORT}" ) def test_addbot_handle_ipv4_with_botport(tcl_bridge: BridgeClient) -> None: assert tcl_bridge.eval_ok(f"addbot {BOT} 1.1.1.1 5555") == "1" assert tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "1.1.1.1 5555 5555" def test_addbot_handle_ipv6_with_botport( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert tcl_bridge.eval_ok(f"addbot {BOT} fe80::69ec:cfe4:81de:4fe5 6666") == "1" assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "fe80::69ec:cfe4:81de:4fe5 6666 6666" ) def test_addbot_handle_ipv4_with_botport_and_userport( tcl_bridge: BridgeClient, ) -> None: assert tcl_bridge.eval_ok(f"addbot {BOT} 1.1.1.1 5555 6666") == "1" assert tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "1.1.1.1 5555 6666" def test_addbot_handle_ipv6_with_botport_and_userport( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert ( tcl_bridge.eval_ok(f"addbot {BOT} fe80::69ec:cfe4:81de:4fe5 6666 7777") == "1" ) assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "fe80::69ec:cfe4:81de:4fe5 6666 7777" ) # ---------- ipv4:port packed format ---------- def test_addbot_handle_ipv4_colon_port(tcl_bridge: BridgeClient) -> None: assert tcl_bridge.eval_ok(f"addbot {BOT} 1.1.1.1:4444") == "1" assert tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "1.1.1.1 4444 4444" def test_addbot_with_packed_address_ignores_extra_args( tcl_bridge: BridgeClient, ) -> None: """When the address arg uses `:`/`/` separators, extra positional args are ignored (the packed form is canonical).""" assert tcl_bridge.eval_ok(f"addbot {BOT} 1.1.1.1:4444/5555 6666") == "1" assert tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "1.1.1.1 4444 5555" # ---------- bracketed IPv6 ---------- def test_addbot_bracketed_ipv6_with_colon_botport( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert ( tcl_bridge.eval_ok(f"addbot {BOT} \\[fe80::69ec:cfe4:81de:4fe5\\]:4444") == "1" ) assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "fe80::69ec:cfe4:81de:4fe5 4444 4444" ) def test_addbot_bracketed_ipv6_with_colon_botport_slash_userport( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert ( tcl_bridge.eval_ok( f"addbot {BOT} \\[fe80::69ec:cfe4:81de:4fe5\\]:4444/5555" ) == "1" ) assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "fe80::69ec:cfe4:81de:4fe5 4444 5555" ) def test_addbot_bracketed_ipv6_with_separate_botport( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert ( tcl_bridge.eval_ok(f"addbot {BOT} \\[fe80::69ec:cfe4:81de:4fe5\\] 4444") == "1" ) assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "fe80::69ec:cfe4:81de:4fe5 4444 4444" ) def test_addbot_bracketed_ipv6_with_separate_botport_userport( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: assert ( tcl_bridge.eval_ok( f"addbot {BOT} \\[fe80::69ec:cfe4:81de:4fe5\\] 4444 5555" ) == "1" ) assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == "fe80::69ec:cfe4:81de:4fe5 4444 5555" ) def test_addbot_ipv6_with_trailing_slash_uses_default_ports( tcl_bridge: BridgeClient, ipv6_required: None ) -> None: """A trailing `/` after IPv6 means "no port given", default-port applies.""" assert tcl_bridge.eval_ok(f"addbot {BOT} fe80::69ec:cfe4:81de:4fe5/") == "1" assert ( tcl_bridge.eval_ok(f"getuser {BOT} botaddr") == f"fe80::69ec:cfe4:81de:4fe5 {DEFAULT_PORT} {DEFAULT_PORT}" ) eggheads-eggdrop-9c00fe1/tests/tests/test_tcl_iscmds.py000066400000000000000000000150731524730042700233720ustar00rootroot00000000000000"""Converted from eggdrop-tests/eggdrop_tcl_iscmds.bats. Tests for `isban`, `isbansticky`, `isexempt`, `isinvite` — the 4 lookup commands for global vs channel ban/exempt/invite lists. The original bats test had a single shared eggdrop and mutated state between cases; here each test gets a fresh spawn and sets up its own state inline, which makes the intent of each case obvious in isolation. """ from __future__ import annotations import pytest from support.bridge_client import BridgeClient CHAN = "#foober" HOST = "*!test@foo.com" @pytest.fixture def chan(tcl_bridge: BridgeClient) -> str: """Add #foober and return its name.""" tcl_bridge.eval_ok(f"channel add {CHAN}") return CHAN # ---------- isban ---------- def test_isban_returns_0_when_no_global_or_channel_ban( tcl_bridge: BridgeClient, chan: str ) -> None: assert tcl_bridge.eval_ok(f"isban {HOST}") == "0" def test_isban_returns_1_when_only_global_ban_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newban {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isban {HOST}") == "1" def test_isban_with_channel_returns_1_when_only_channel_ban_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchanban {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isban {HOST} {chan}") == "1" def test_isban_returns_1_when_both_global_and_channel_ban_exist( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newban {HOST} testuser comment") tcl_bridge.eval_ok(f"newchanban {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isban {HOST} {chan}") == "1" def test_isban_with_channel_returns_1_for_global_when_only_channel_ban_exists( tcl_bridge: BridgeClient, chan: str ) -> None: """isban without a channel hits the global list — global ban present → 1.""" tcl_bridge.eval_ok(f"newban {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isban {HOST}") == "1" def test_isban_with_channel_returns_0_when_only_global_ban_exists( tcl_bridge: BridgeClient, chan: str ) -> None: """isban WITH a channel checks both — but if neither global+channel match, returns 0. Here only global is set, so isban for chan-scope returns 1 (eggdrop combines global + chan).""" tcl_bridge.eval_ok(f"newban {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isban {HOST} {chan}") == "1" def test_isban_returns_0_when_only_channel_ban_exists_no_chan_arg( tcl_bridge: BridgeClient, chan: str ) -> None: """Without a channel arg, isban only looks at the global list.""" tcl_bridge.eval_ok(f"newchanban {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isban {HOST}") == "0" # ---------- isbansticky ---------- def test_isbansticky_returns_0_when_no_global_or_channel_sticky_ban( tcl_bridge: BridgeClient, chan: str ) -> None: assert tcl_bridge.eval_ok(f"isbansticky {HOST}") == "0" def test_isbansticky_returns_1_when_only_global_sticky_ban_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newban {HOST} testuser comment 60 sticky") assert tcl_bridge.eval_ok(f"isbansticky {HOST}") == "1" def test_isbansticky_with_channel_returns_1_when_only_channel_sticky_ban_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchanban {chan} {HOST} testuser comment 60 sticky") assert tcl_bridge.eval_ok(f"isbansticky {HOST} {chan}") == "1" def test_isbansticky_returns_1_when_both_sticky_bans_exist( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newban {HOST} testuser comment 60 sticky") tcl_bridge.eval_ok(f"newchanban {chan} {HOST} testuser comment 60 sticky") assert tcl_bridge.eval_ok(f"isbansticky {HOST} {chan}") == "1" def test_isbansticky_returns_0_when_only_channel_sticky_ban_no_chan_arg( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchanban {chan} {HOST} testuser comment 60 sticky") # Original bats test asserts isban (not isbansticky) returns 0 here. Match it. assert tcl_bridge.eval_ok(f"isban {HOST}") == "0" # ---------- isexempt ---------- def test_isexempt_returns_0_when_no_global_or_channel_exempt( tcl_bridge: BridgeClient, chan: str ) -> None: assert tcl_bridge.eval_ok(f"isexempt {HOST}") == "0" def test_isexempt_returns_1_when_only_global_exempt_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newexempt {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isexempt {HOST}") == "1" def test_isexempt_with_channel_returns_1_when_only_channel_exempt_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchanexempt {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isexempt {HOST} {chan}") == "1" def test_isexempt_returns_1_when_both_global_and_channel_exempt_exist( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newexempt {HOST} testuser comment") tcl_bridge.eval_ok(f"newchanexempt {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isexempt {HOST} {chan}") == "1" def test_isexempt_returns_0_when_only_channel_exempt_no_chan_arg( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchanexempt {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isexempt {HOST}") == "0" # ---------- isinvite ---------- def test_isinvite_returns_0_when_no_global_or_channel_invite( tcl_bridge: BridgeClient, chan: str ) -> None: assert tcl_bridge.eval_ok(f"isinvite {HOST}") == "0" def test_isinvite_returns_1_when_only_global_invite_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newinvite {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isinvite {HOST}") == "1" def test_isinvite_with_channel_returns_1_when_only_channel_invite_exists( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchaninvite {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isinvite {HOST} {chan}") == "1" def test_isinvite_returns_1_when_both_global_and_channel_invite_exist( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newinvite {HOST} testuser comment") tcl_bridge.eval_ok(f"newchaninvite {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isinvite {HOST} {chan}") == "1" def test_isinvite_returns_0_when_only_channel_invite_no_chan_arg( tcl_bridge: BridgeClient, chan: str ) -> None: tcl_bridge.eval_ok(f"newchaninvite {chan} {HOST} testuser comment") assert tcl_bridge.eval_ok(f"isinvite {HOST}") == "0" eggheads-eggdrop-9c00fe1/tests/tests/test_tcl_matchattr.py000066400000000000000000000152431524730042700240760ustar00rootroot00000000000000"""Converted from eggdrop-tests/eggdrop_tcl_matchattr.bats. Tests for `matchattr` — Tcl flag-matching against a user record. Covers global flags, channel flags, the `&` (all-of) operator, and rejection of unknown flags. Original setup: adduser foo channel add #foober chattr foo +jlmoptx|+lov #foober Result: foo has global +jlmoptx and channel +lov on #foober. """ from __future__ import annotations import pytest from support.bridge_client import BridgeClient CHAN = "#foober" @pytest.fixture def matchattr_user(tcl_bridge: BridgeClient) -> str: """Per-test setup: adduser foo with global +jlmoptx and channel +lov on #foober.""" tcl_bridge.eval_ok(f"channel add {CHAN}") tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok(f"chattr foo +jlmoptx|+lov {CHAN}") return "foo" # ---------- + (any-of) on global flags ---------- def test_matchattr_single_global_plus_flag_matches_when_user_has_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo +o") == "1" def test_matchattr_single_global_plus_flag_does_not_match_when_user_lacks_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo +g") == "0" def test_matchattr_two_global_plus_flags_matches_if_user_has_one( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: """+ is any-of by default: +on matches if user has o OR n.""" assert tcl_bridge.eval_ok("matchattr foo +on") == "1" def test_matchattr_two_global_plus_flags_does_not_match_if_user_has_neither( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo +gn") == "0" # ---------- & (all-of) operator on global flags ---------- def test_matchattr_two_global_plus_flags_with_amp_matches_if_user_has_both( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: """& makes it all-of: +mo& matches if user has BOTH global m AND o.""" assert tcl_bridge.eval_ok(f"matchattr foo +mo& {CHAN}") == "1" def test_matchattr_two_global_plus_flags_with_amp_does_not_match_if_user_has_one( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo +mn& {CHAN}") == "0" # ---------- - (none-of) on global flags ---------- def test_matchattr_single_global_minus_flag_matches_when_user_lacks_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: """-n matches if user does NOT have n.""" assert tcl_bridge.eval_ok("matchattr foo -n") == "1" def test_matchattr_single_global_minus_flag_does_not_match_when_user_has_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo -m") == "0" def test_matchattr_two_global_minus_flags_matches_if_user_lacks_one( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo -mn") == "1" def test_matchattr_two_global_minus_flags_matches_if_user_lacks_both( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo -gn") == "1" def test_matchattr_two_global_minus_flags_does_not_match_if_user_has_both( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo -om") == "0" # ---------- channel flags (| separator) ---------- def test_matchattr_single_channel_plus_flag_matches_when_user_has_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |+o {CHAN}") == "1" def test_matchattr_single_channel_plus_flag_does_not_match_when_user_lacks_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |+g {CHAN}") == "0" def test_matchattr_two_channel_plus_flags_matches_if_user_has_one( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |+on {CHAN}") == "1" def test_matchattr_two_channel_plus_flags_does_not_match_if_user_has_neither( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |+gn {CHAN}") == "0" def test_matchattr_two_channel_plus_flags_with_amp_matches_if_user_has_both( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo &+lo {CHAN}") == "1" def test_matchattr_two_channel_plus_flags_with_amp_does_not_match_if_user_has_one( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo &+om {CHAN}") == "0" def test_matchattr_single_channel_minus_flag_matches_when_user_lacks_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |-n {CHAN}") == "1" def test_matchattr_single_channel_minus_flag_does_not_match_when_user_has_it( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |-o {CHAN}") == "0" def test_matchattr_two_channel_minus_flags_matches_if_user_lacks_one( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |-on {CHAN}") == "1" def test_matchattr_two_channel_minus_flags_matches_if_user_lacks_both( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |-gn {CHAN}") == "1" def test_matchattr_two_channel_minus_flags_does_not_match_if_user_has_both( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |-ov {CHAN}") == "0" # ---------- error paths (behavior changed since the original bats tests) ---------- # # The original bats suite asserted that matchattr returned a Tcl error # `Unknown flag specified for matching` for unknown global/channel/bot flags. # The current implementation in src/tcluser.c (tcl_matchattr) calls # break_down_flags() which silently ignores unknown characters, then if the # resulting flag set is empty returns "1" (the "no flags matches anyone" # branch). The "rejection" is gone — these tests now document the new # silent-accept behavior instead. def test_matchattr_silently_accepts_unknown_global_flag( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: """`+s` is unknown globally; ignored, leaving an empty plus set → matches anyone.""" assert tcl_bridge.eval_ok("matchattr foo +s") == "1" def test_matchattr_silently_accepts_unknown_channel_flag( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok(f"matchattr foo |+j {CHAN}") == "1" def test_matchattr_silently_accepts_unknown_bot_flag( tcl_bridge: BridgeClient, matchattr_user: str ) -> None: assert tcl_bridge.eval_ok("matchattr foo ||+f") == "1" eggheads-eggdrop-9c00fe1/tests/tests/test_tcl_passwdok.py000066400000000000000000000034571524730042700237460ustar00rootroot00000000000000"""Converted from eggdrop-tests/eggdrop_tcl_passwdok.bats. Tests for the `passwdok` Tcl command — verifies user passwords match their stored value, with correct handling of the empty / dash sentinels. """ from __future__ import annotations from support.bridge_client import BridgeClient def test_passwdok_returns_1_when_password_matches(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok("setuser foo PASS asdf") assert tcl_bridge.eval_ok("passwdok foo asdf") == "1" def test_passwdok_returns_0_when_password_differs(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok("setuser foo PASS asdf") assert tcl_bridge.eval_ok("passwdok foo notasdf") == "0" def test_passwdok_returns_1_for_dash_when_user_has_no_password( tcl_bridge: BridgeClient, ) -> None: """A `-` password literal matches a user that has no password set.""" tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok("setuser foo PASS {}") # clear password assert tcl_bridge.eval_ok("passwdok foo -") == "1" def test_passwdok_returns_0_for_dash_when_user_has_a_password( tcl_bridge: BridgeClient, ) -> None: tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok("setuser foo PASS asdf") assert tcl_bridge.eval_ok("passwdok foo -") == "0" def test_passwdok_returns_0_for_empty_when_user_has_no_password( tcl_bridge: BridgeClient, ) -> None: tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok("setuser foo PASS {}") assert tcl_bridge.eval_ok('passwdok foo ""') == "0" def test_passwdok_returns_0_for_empty_when_user_has_a_password( tcl_bridge: BridgeClient, ) -> None: tcl_bridge.eval_ok("adduser foo") tcl_bridge.eval_ok("setuser foo PASS asdf") assert tcl_bridge.eval_ok('passwdok foo ""') == "0" eggheads-eggdrop-9c00fe1/tests/tests/test_tcl_server.py000066400000000000000000000124501524730042700234120ustar00rootroot00000000000000"""Converted from eggdrop-tests/eggdrop_tcl_server.bats. Tests for the `server` Tcl command — adding, removing, and listing the in-memory server list. API note: the original bats tests used the old `addserver`/`delserver`/ `set servers` interface, which no longer exists. The current command is `server add HOST ?PORT? ?PASS?` / `server remove HOST ?PORT?` / `server list` (returns a list of `{host port pass}` triples). """ from __future__ import annotations from support.bridge_client import BridgeClient def _server_entries(tcl_bridge: BridgeClient) -> list[list[str]]: """Return server list as a list of [host, port, pass] entries. `server list` formats each entry as a Tcl list `{host port pass}`. We parse it via Tcl `lmap` to keep things simple on the Python side. """ raw = tcl_bridge.eval_ok( 'lmap entry [server list] {format "%s|%s|%s" ' "[lindex $entry 0] [lindex $entry 1] [lindex $entry 2]}" ) return [e.split("|") for e in raw.split() if e] def _server_in_list( tcl_bridge: BridgeClient, host: str, port: str | None = None ) -> bool: for entry in _server_entries(tcl_bridge): if entry[0] == host and (port is None or entry[1] == port): return True return False # ---------- server add ---------- def test_server_add_just_host_uses_default_port(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add irc.foo.com") # Entry exists; port may be the configured default-port or empty. assert _server_in_list(tcl_bridge, "irc.foo.com") def test_server_add_with_explicit_port(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add irc.ferg.com 8877") assert _server_in_list(tcl_bridge, "irc.ferg.com", "8877") def test_server_add_with_port_and_password(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add irc.moo.com 4455 mypass") for entry in _server_entries(tcl_bridge): if entry[0] == "irc.moo.com" and entry[1] == "4455": assert entry[2] == "mypass" return raise AssertionError("irc.moo.com:4455 not in server list") def test_server_add_ssl_port_keeps_plus_prefix(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add irc.snell.com +7000") assert _server_in_list(tcl_bridge, "irc.snell.com", "+7000") def test_server_add_ipv6_address(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add 2344:2344:2344::5433:5433 5555") assert _server_in_list(tcl_bridge, "2344:2344:2344::5433:5433", "5555") def test_server_add_ipv4_address(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add 1.2.3.4 4444") assert _server_in_list(tcl_bridge, "1.2.3.4", "4444") # ---------- server remove ---------- def test_server_remove_first_element(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add irc.first.com 1111") tcl_bridge.eval_ok("server add irc.second.com 2222") tcl_bridge.eval_ok("server remove irc.first.com 1111") assert not _server_in_list(tcl_bridge, "irc.first.com", "1111") assert _server_in_list(tcl_bridge, "irc.second.com", "2222") def test_server_remove_middle_element(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add a.com 1111") tcl_bridge.eval_ok("server add b.com 2222") tcl_bridge.eval_ok("server add c.com 3333") tcl_bridge.eval_ok("server remove b.com 2222") assert _server_in_list(tcl_bridge, "a.com", "1111") assert not _server_in_list(tcl_bridge, "b.com", "2222") assert _server_in_list(tcl_bridge, "c.com", "3333") def test_server_remove_ipv6(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add 2344:2344:2344::5433:5433 5555") tcl_bridge.eval_ok("server remove 2344:2344:2344::5433:5433 5555") assert not _server_in_list(tcl_bridge, "2344:2344:2344::5433:5433") def test_server_remove_ipv4(tcl_bridge: BridgeClient) -> None: tcl_bridge.eval_ok("server add 1.2.3.4 4444") tcl_bridge.eval_ok("server remove 1.2.3.4 4444") assert not _server_in_list(tcl_bridge, "1.2.3.4", "4444") def test_server_remove_no_port_removes_all_matching_entries( tcl_bridge: BridgeClient, ) -> None: """Behavior change since the original bats: `server remove host` with no port iterates the list and removes EVERY entry matching `host`. The bats test assumed only the first match was removed.""" tcl_bridge.eval_ok("server add irc.firstmatch.com 1111") tcl_bridge.eval_ok("server add irc.firstmatch.com 2222") tcl_bridge.eval_ok("server remove irc.firstmatch.com") assert not _server_in_list(tcl_bridge, "irc.firstmatch.com") def test_server_remove_with_port_only_removes_matching_host_port( tcl_bridge: BridgeClient, ) -> None: tcl_bridge.eval_ok("server add irc.firstmatch.com 1111") tcl_bridge.eval_ok("server add irc.firstmatch.com 2222") tcl_bridge.eval_ok("server remove irc.firstmatch.com 2222") assert _server_in_list(tcl_bridge, "irc.firstmatch.com", "1111") assert not _server_in_list(tcl_bridge, "irc.firstmatch.com", "2222") # ---------- error paths ---------- def test_server_add_rejects_colon_port_in_address(tcl_bridge: BridgeClient) -> None: """`host:port` syntax is forbidden — port must be a separate argument.""" ok, result = tcl_bridge.eval("server add irc.port.com:1111") assert not ok assert "Make sure the port is" in result eggheads-eggdrop-9c00fe1/tests/uv.lock000066400000000000000000000577631524730042700200130ustar00rootroot00000000000000version = 1 revision = 1 requires-python = ">=3.11" [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] name = "eggdrop-tests" version = "0.0.0" source = { virtual = "." } [package.dev-dependencies] dev = [ { name = "jinja2" }, { name = "pytest" }, { name = "pytest-timeout" }, ] [package.metadata] [package.metadata.requires-dev] dev = [ { name = "jinja2", specifier = ">=3.1" }, { name = "pytest", specifier = ">=8" }, { name = "pytest-timeout", specifier = ">=2.3" }, ] [[package]] name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, ] [[package]] name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] name = "markupsafe" version = "3.0.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } wheels = [ { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 }, { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 }, { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 }, { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 }, { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 }, { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 }, { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 }, { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 }, { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 }, { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 }, { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 }, { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, ] [[package]] name = "packaging" version = "26.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134 } wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] [[package]] name = "pygments" version = "2.20.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, ] [[package]] name = "pytest" version = "9.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165 } wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249 }, ] [[package]] name = "pytest-timeout" version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973 } wheels = [ { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382 }, ] eggheads-eggdrop-9c00fe1/text/000077500000000000000000000000001524730042700163075ustar00rootroot00000000000000eggheads-eggdrop-9c00fe1/text/CONTENTS000066400000000000000000000010361524730042700174670ustar00rootroot00000000000000Directory Contents - text/ Last revised: December 02, 2003 _____________________________________________________________________ Directory Contents - text/ banner The contents of this file are displayed before logging in whenever a user telnets to the bot. motd The text of this file is displayed anytime a user successfully logs in and joins the party line. _____________________________________________________________________ Copyright (C) 2003 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/text/banner000066400000000000000000000004221524730042700174750ustar00rootroot00000000000000 ___ _ | __| __ _ __ _ __| | _ _ ___ _ __ | _| / _` |/ _` |/ _` || '_|/ _ \| '_ \ |___|\__, |\__, |\__,_||_| \___/| .__/ |___/ |___/ |_| Copyright (C) 1997 Robey Pointer Copyright (C) 1999 - 2025 Eggheads Development Team eggheads-eggdrop-9c00fe1/text/motd000066400000000000000000000016061524730042700172000ustar00rootroot00000000000000Connected to %B, running %V ____ __ / __/___ _ ___ _ ___/ /____ ___ ___ / _/ / _ `// _ `// _ // __// _ \ / _ \ /___/ \_, / \_, / \_,_//_/ \___// .__/ /___/ /___/ /_/ ----------------------------------------------- { www.eggheads.org | irc.libera.chat/#eggdrop } Hey %b%N!%b My name is %b%B%b and I am running %b%V%b, on %b%U%b. Local time is now %b%T%b %{+n} You are an owner of this bot. Only +n users can see this! For more info, see %b.help set motd%b. Please edit the %bmotd%b file in your bot's 'text' directory. %{-} Use %b.help%b for basic help. Use %b.help %b for help on a specific command. Use %b.help all%b to get a full command list. Use %b.help *somestring*%b to list any help texts containing "somestring". Have fun. Commands start with '.' (like '.quit' or '.help') Everything else goes out to the party line. eggheads-eggdrop-9c00fe1/text/motd.finnish000066400000000000000000000017601524730042700206360ustar00rootroot00000000000000Yhdistetty %B, pyrii %Vlla ____ __ / __/___ _ ___ _ ___/ /____ ___ ___ / _/ / _ `// _ `// _ // __// _ \ / _ \ /___/ \_, / \_, / \_,_//_/ \___// .__/ /___/ /___/ /_/ ----------------------------------------------- { www.eggheads.org | irc.libera.chat/#eggdrop } Hei %b%N!%b Minun nimeni on %b%B%b ja min toimin %b%V%b, %b%U%blla. Paikallisaika on nyt %b%T%b %{+n} Olet tmn botin omistaja. Vain +n kyttjt voivat nhd tmn! Lis tietoa, saat komennolla %b.help set motd%b. Editoi %bmotd%b tiedosto botillesi 'text' kansiosta. %{-} Kyt komentoa %b.help%b saadaksesi perus apua. Kyt komentoa %b.help %b saadaksesi apua eikoisempiin komentoihin. Kyt komentoa %b.help all%b saadaksesi tydellisen listan komennoista. Kyt komentoa %b.help *jokinsana*%b listataksesi kaikki komennot aiheesta "jokinsana". Pid hauskaa. Komennot alkavat merkill piste '.' (kuten '.quit' or '.help') Kaikki muut menevt pois party linjalta. eggheads-eggdrop-9c00fe1/text/motd.italian000066400000000000000000000015331524730042700206170ustar00rootroot00000000000000Collegato a %B, eseguendo un %V ____ __ / __/___ _ ___ _ ___/ /____ ___ ___ / _/ / _ `// _ `// _ // __// _ \ / _ \ /___/ \_, / \_, / \_,_//_/ \___// .__/ /___/ /___/ /_/ ----------------------------------------------- { www.eggheads.org | irc.libera.chat/#eggdrop } Hey %b%N!%b Il mio nome è %b%B%b e sono un %b%V%b, su %b%U%b. Orario locale %b%T%b %{+n} Tu sei un padrone di questo bot. Solo gli utenti +n possono vedere questo! %{-} Usa %b.help%b per gli aiuti di base. Usa %b.help %b per l'aiuto su un comando specifico. Usa %b.help all%b per avere l'elenco completo dei comandi. Usa %b.help *stringa*%b per avere la lista degli aiuti il cui testo contiene "stringa". I comandi iniziano con '.' (esempio : '.quit' o '.help') Tutto il resto sarà scritto/letto nella chat interna.