diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 Library 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 + + Appendix: 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) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General +Public License instead of this License. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..8ee689c --- /dev/null +++ b/CREDITS @@ -0,0 +1,30 @@ +If you feel left out, feel free to contact me (proton). + +Contributors in alphabetical order: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Azmodan patches, testing & documentation +Deke source cleanups +detriment bug reports +DMC bug reports +endorphin major programming, energymech.net domain +guppy ftp hosting, patches +[IO] DNS hosting +jugslam bug fix +MadCamel major programming +Mourgos patches +mr_c bug reports +Nemesis128 python scripting backend +polygon major programming +proton major programming +Qm-D bug reports +sinthetek bug reports +The_Dawn testing & bug reports +vaniljus website hosting +wy|d source cleanups +zip- patches + + +Moral support: +~~~~~~~~~~~~~~ +CaptHowdy, CrazyBear, Dark-Lord, davidX, drlfa, +josh, sin`, SouLShado, symetrix, The_1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f30d43 --- /dev/null +++ b/Makefile @@ -0,0 +1,105 @@ +# +# EnergyMech, IRC Bot software +# Copyright (c) 1997-2009 proton +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +MISCFILES = COPYING CREDITS README README.TCL TODO VERSIONS VERSIONS-1 Makefile configure \ + checkmech sample.conf sample.py sample.tcl sample.userfile default.bigchars + +HELPFILES = help/8BALL help/ACCESS help/ALIAS help/AWAY help/BAN help/BANLIST help/BYE \ + help/CCHAN help/CHACCESS help/CHANBAN help/CHANNELS help/CHAT help/CLEARSHIT \ + help/CMD help/CORE help/CSERV help/CTCP help/CYCLE help/DEOP help/DIE \ + help/DNS help/DNSSERVER help/DO help/DOWN help/ECHO help/ESAY help/FORGET \ + help/GREET help/HELP help/IDLE help/INSULT help/INVITE help/JOIN help/KB \ + help/KICK help/KS help/LAST help/LEVELS help/LINK help/LOAD help/LUSERS help/ME \ + help/MODE help/MSG help/NAMES help/NEXTSERVER help/NICK help/NOTIFY help/ONTIME \ + help/OP help/PART help/PASSWD help/PROTECTION help/PYTHON help/PYTHONSCRIPT \ + help/QSHIT help/RESET help/RKS help/RSHIT help/RSPY help/RT help/SAVE help/SAY \ + help/SCREW help/SEEN help/SERVER help/SERVERGROUP help/SET help/SETAAWAY \ + help/SETAOP help/SETAUB help/SETAVOICE help/SETCC help/SETCHANBAN help/SETCKL \ + help/SETDCC help/SETENFM help/SETENFMODES help/SETENFPASS help/SETFL help/SETFPL \ + help/SETIKT help/SETKS help/SETMAL help/SETMBL help/SETMDL help/SETMKL \ + help/SETMODES help/SETMPL help/SETNCL help/SETPASS help/SETPROT help/SETPUB \ + help/SETRK help/SETSD help/SETSERVERGROUP help/SETSHIT help/SETSO help/SETSTATS \ + help/SETTOP help/SHIT help/SHITLIST help/SHOWIDLE help/SHUTDOWN help/SITEBAN \ + help/SITEKB help/SPY help/STATS help/TCL help/TCLSCRIPT help/TIME help/TOPIC \ + help/UNALIAS help/UNBAN help/UNVOICE help/UP help/UPTIME help/USAGE help/USER \ + help/USERHOST help/USERLIST help/VER help/VERIFY help/VOICE help/WALL help/WHO \ + help/WHOIS help/WHOM + +RANDFILES = messages/8ball.txt messages/away.txt messages/insult.txt \ + messages/kick.txt messages/nick.txt messages/pickup.txt \ + messages/say.txt messages/signoff.txt messages/version.txt + +STUBFILES = src/Makefile.in src/config.h.in src/ld/mech.ldscript + +TESTFILES = config/cc.c config/inet_addr.c config/ldtest.c \ + config/ptr_size.c config/socket.c config/tcl.c config/which \ + config/md5.h config/md5_internal.c config/pw.c \ + config/inet_aton.c config/unaligned.c config/python.c + +TRIVFILES = trivia/mkindex.c + +SRCFILES = src/alias.c src/auth.c src/bounce.c src/chanban.c src/channel.c \ + src/core.c src/ctcp.c src/debug.c src/dns.c src/dynamode.c \ + src/function.c src/gencmd.c src/greet.c src/help.c src/irc.c \ + src/kicksay.c src/main.c src/mega.c src/net.c src/net_chan.c \ + src/note.c src/notify.c src/ons.c src/parse.c src/perl.c \ + src/prot.c src/python.c src/redirect.c src/reset.c src/seen.c \ + src/shit.c src/socket.c src/spy.c src/stats.c src/tcl.c \ + src/telnet.c src/toybox.c src/trivia.c src/uptime.c src/user.c \ + src/vars.c src/web.c src/md5/md5.c src/md5/md5.h + +HDRFILES = src/defines.h src/global.h src/h.h src/settings.h src/structs.h src/text.h src/usage.h + +CONFFILES = src/Makefile src/config.h + +DISTFILES = $(MISCFILES) $(RANDFILES) $(SRCFILES) $(HELPFILES) \ + $(STUBFILES) $(HDRFILES) $(TRIVFILES) $(TESTFILES) + +# +# simple make rules +# + +mech: $(SRCFILES) $(CONFFILES) + $(MAKE) -C src energymech + +energymech: $(SRCFILES) $(CONFFILES) + $(MAKE) -C src energymech + +clean: FORCE + $(MAKE) -C src clean + +install: FORCE + $(MAKE) -C src install + +# +# packing things up for distribution +# + +dist: FORCE + $(MAKE) dist2 DISTDIR=`sed 's/^.*VERSION.*"\(.*\)".*$$/emech-\1/p; d;' src/global.h` + +dist2: FORCE + rm -rf /tmp/$(DISTDIR) + mkdir /tmp/$(DISTDIR) + tar cf - $(DISTFILES) | ( cd /tmp/$(DISTDIR) ; tar --preserve -xf - ) + cd /tmp ; tar cf - $(DISTDIR) | gzip -9 > $(DISTDIR).tar.gz + rm -rf /tmp/$(DISTDIR) + chmod 644 /tmp/$(DISTDIR).tar.gz + +FORCE: diff --git a/README b/README new file mode 100644 index 0000000..c83410c --- /dev/null +++ b/README @@ -0,0 +1,130 @@ +,-----------------------------------------------------------------.,---------------------------. +| ,-----; ,------, ,-----; ,-------, ,----, , ,-. | ,--------------. | +| / ,---' | ,. \ / ,---' | ,--, \ / ,-. \ /| | \ | / ,-. \ | +| | |___ | | \ \ | |___ | |__| ;| | `~' / | | | | `---------' \ \ | +| | ,--' | | | || ,--' | |--; < | | ,--./ `-' | | / \ | +| | |__,-,| | | || |__,-,| | | || | \ |\___. | | / / | +| | || | | || || | | || `~' | \ | | ,----------' / | +| | || | | / | || | | / | | | | | \ _ \ | +| `~~~~~`\/ `---' `/ `~~~~~`\/ `---' `/ `/~~~~~~' |,--' | \_________,' \ \ | +| ,---, ,---, ,-----; ,----. ,-. ,--. | / \ | +| | \ / | / ,---' / ,--' / | | | | / | | +| | \/ || |___ | | | |__| | | / | | +| | |\ /| || ,--' | | | ,--. | | ______________,' | | +| | | \/ | || |__,-,| | /|| | | | | / / | +| | | | || || `--' || | | | | \ / | +| | | | || || || | | / | \ / | +| `---' `---'`~~~~~`\/ `\___/~~~'`---' `/ | \_________________,' | +`-----------------------------------------------------------------'`---------------------------' + +Compiling? +~~~~~~~~~~ +To compile the source: + +1) Uncompress the source code distribution archive. + +2) cd emech-3.0.99p3 +-- Since you are reading this file, you have most likely already + come to this point. + +3) ./configure +-- This script will prompt you for features to include or exclude, + going with the default is not a bad idea. + +4) make clean install + +If all went well you should now have an executable called +``energymech''. + +---*--- + +Setup? +~~~~~~ + +Read the sample.conf file to get an idea of the config file +commands and then try to make your own. A basic setup doesnt need +much more than NICK, ALTNICK, SET USERFILE, JOIN and SERVER +entries, the rest is just tweaks of default values. + +Quick steps: + +1) cp sample.conf mech.conf + +2) pico mech.conf +-- replace ''pico'' with your favourite text editor, look through + the file for sections to change, you will have to remove lines + in order to get the bot to work. Check the file completely! + +3) make a userfile + + construct a temporary file (trick.conf) containing; + --------------------------------------------------------------- + set userfile whateveryouwanttonameit + user + handle * *!*yourident@*.yourdomain.com 100 password + save + shutdown + --------------------------------------------------------------- + + then 'run' this file with './energymech -f trick.conf'. this + will create a userfile with the name you chose ('mech.passwd' + is a good descriptive name which I usually use myself). + + re-use the filename you selected in your proper configuration + file. and remember to 'rm -f mech.session' if you compiled your + energymech with session support. + +4) ./energymech + +That should get you running. If you don't see your bot come on IRC +after a few minutes, you should try running the bot in debug mode +to see what's going on with... + + ./energymech -d + +If you get an 'Unknown option -d', you need to answer 'Y' to debug +support when running ./configure from the compiling section above. + +---*--- + +Updated Files? +~~~~~~~~~~~~~~ +The main distro-site for the EnergyMech is: + + http://www.energymech.net + +---*--- + +More Help? +~~~~~~~~~~ + +Read the website for goodness sake! Thats what its there for! + + #emech is not a help channel + +If you HAVE to ask a question in #emech then make damn sure that +its not covered by the website documentation and try to ask it in +a way so that we dont have to type half a book just to tell you +the answer. + +Do NOT ask to be guided in #emech. We will under no circumstances +help you compile, configure or run a bot. + +We absolutely hate people who come into the channel asking utterly +simple questions like `Why doesnt my bot connect', `Whats the +command to add a user', etc. If you manage to stay in the channel +after asking something like that its simply because we're too damn +lazy to kickban your ass. + +We will NOT teach you how to work with UNIX shells or how to use +your mech, you have to learn that by yourself. Im noting this here +since it seems to be a common misconception that we would. + +In short: + +THIS SOFTWARE IS PROVIDED AS IS. YOU ARE ENTIRELY ON YOUR OWN WHEN +IT COMES TO CONFIGURING AND USING IT. + +---*--- + +proton, July 24th, 2009. + diff --git a/README.TCL b/README.TCL new file mode 100644 index 0000000..b90976e --- /dev/null +++ b/README.TCL @@ -0,0 +1,76 @@ +Variables +~~~~~~~~~ + +$mech_currentnick read only nick of whoever triggered the current action +$mech_guid read only GUID of the current bot +$mech_nick read only nick of the current bot +$mech_wantnick read/write wanted nick of the current bot (write unimplemented) +$mech_server read only ID of the server of the current bot +$mech_nextserver read/write ID of the next server for the current bot (write unimplemented) +$mech_currentchan read only active channel of the current bot +$mech_version read only version string of the mech +$mech_srcdate read only source date of the mech +$mech_class read only class of the mech + +Functions +~~~~~~~~~ +hook timer + + specifier when this event should run + script proc + + callback arguments: {} + + * Still broken + +hook parse + + can be any IRC command such as PRIVMSG, NOTICE, JOIN, PART, QUIT, etc.. + script proc + + callback arguments: {from rest} + + If the callback returns 0 (zero), normal parsing is done by the EnergyMech after + the script returns. If non-zero is returned, the bot will not parse the message. + +hook command + + + script proc + + callback arguments: {from rest} + + It is the scripts responsibility to check access. + +hook dcc_complete - + + script proc + + callback arguments: {to filename cps} + +dcc_sendfile + +debug "" + +to_file + +to_server "" [number] + + text to be sent to server + [number] max number of lines in the sendq + + * if number is not specified, the line will be sent immediately to the current bots server socket. + to_server returns the number of bytes written or -1 on an error + (in which case the server socket will have been closed) + + * if number is zero (0), the line will be added to the sendq for the current server socket. + to_server returns the number of lines in the sendq + + * if number is a positive integer, the line will only be added to the sendq if the number + of lines in the sendq is smaller than the given number. + to_server returns the number of lines in the sendq + + ! when sending lines directly (number not specified), a newline must be supplied with the buffer. + when sending through the sendq, the newline is optional + +userlevel [channel] diff --git a/README.md b/README.md deleted file mode 100644 index 813f8df..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -energymech -========== - -EnergyMech IRC Bot diff --git a/TODO b/TODO new file mode 100644 index 0000000..e503ff2 --- /dev/null +++ b/TODO @@ -0,0 +1,37 @@ +/* + * EnergyMech TODO (May 17th, 2009) + */ + + - Script timer + + - Genuine telnet protocol support for telnet sessions (?) + + - SOCKS4/5 support (?) + + - Undernet CService support (IMHO, should be channel settings (set=password, tog=enable/disable) /proton) + + - DalNet Nickserv/Chanserv support (needed? they dont tolerate much bots on that network /proton) + + - Update/complete the help files. + + - Mode +/-o commands to check for SO and if SO is set, check userlevel and make sure user has access before sending MODEs. + + - Make it possible to pipe SPY through another bot. + Ex: bots = laurel, hardy; /msg laurel -spy #emech hardy!#emech + (done: local bot spy's) + + - Mode +v a user who verifies if channel is set to +m + (too clever?) + + - Recognize some common forms of channel spamming (eg, `join #blablabla') + (can be done with kicksays?) + + - botnet logic so that all linked bots dont end up on the same server(s) + prevent losing channels cuz they were all on the same one -- the rebooted one.. + +/* + * Most of these features are open for discussion on how to + * (or even if) implement them + * + * Patches are most welcome + */ diff --git a/VERSIONS b/VERSIONS new file mode 100644 index 0000000..b28a6ad --- /dev/null +++ b/VERSIONS @@ -0,0 +1,205 @@ +3.0.99p3 -- July 24th, 2009. + + * Added: Python scripting support (supplied by S.Marquis) + * Added: Users are copied to other local bots. + * Fixed: Obscure configure options for new features. + * Fixed: Updated help files. + * Fixed: Local bots should now auto-op eachother (if AOP is set). + * Fixed: Filenames for various functions is now sanity checked. + * Fixed: Saving greet updates to userfile. + +3.0.99p1 -- May 11th, 2009. + + * Added: Scripting hook for dns (only if compiled with rawdns support) + Usage(TCL): mech_dns + * Added: Scripting hook for commands + Usage(TCL): mech_hook command + * Added: DNS command (only if compiled with rawdns support) + Usage: DNS + * Added: Shit records to debug dump file. + * Added: CHANBAN feature + * Added: Spy channels can now be created cross-bot. + Usage: SPY [STATUS|MESSAGE|RAWIRC|[guid":"|botnick":"] [channel|">" filename] + * Added: IRCd extension flags are now recovered in a reset. + * Added: BANLIST command also lists +e and +I masks (ircnet) + * Added: Recognizes IRCd extensions from the 005 line such as ircnet modes (+e/+I) + * Added: SERVERGROUP command and SERVERGROUP setting (per bot setting) + Usage: SERVERGROUP [identifier] + Usage: SET SERVERGROUP + * Fixed: async dns should eat less cpu if it bugs. + * Fixed: Bot can connect to undernet servers presenting an additional + challenge if there is no ident. + +3.0.3 - August 27th, 2008. + + * Fixed: DNS query recursion flag was set incorrectly. + +3.0.2 -- May 8th, 2006. + + * Fixed: Critical crash bug in parse_notice + +3.0.1 -- August 28th, 2005. + + * Fixed: Socket corruption when terminating a DCC or telnet chat. + * Fixed: Bouncer using virtual host. + * Fixed: Bouncer timeout after irc connection. + * Added: Status message for trying a new server. + * Fixed: BNCPORT setting now reacts when altering a live port + * Added: DIE in config file + * Fixed: When users are added or modified, the channel user + list will update to reflect the proper user levels. + * Fixed: Greet will now display properly in userlist. + * Fixed: ALTNICK in sample.conf + * Fixed: Cygwin uptime bot type WINMECH + * Fixed: Makefile flaws + * Fixed: Server removal (server -host) + * Fixed: DNS timeout server cycle + * Fixed: Channel spy source. + +3.0 -- August 1st, 2005. + + * Added: "USER +handle" now works aswell as "USER + handle" + * Fixed: bus error on sparc in async dns + * Fixed: crash bug in LINK command (faulty arguments) + * Fixed: ENFPASS + * Fixed: botnet verify + * Added: +NS (non shared) and +RO (readonly) user flags. + * Added: CMD command + * Removed: ADDLINK/DELLINK commands, use LINK +/- + * Added: Verify over botnet + * Added: botnet spy source + * Added: Multiple channels per user record + * Added: sendQ for certain command output + * Fixed: faulty error message in KB/SITEKB/SCREW + * Added: reset recovers server name + * Added: Telnet connections are now recovered in reset. + * Added: Shitlist output now shows all relevant information. + * Added: Action levels to the KS feature. + * Fixed: commands that output tables now output better formatted tables + * Fixed: bots crashing when they are kicked (themselves) + * Fixed: botnet bots given op without matching access 200. + * Fixed: a few commands crashing when given no arguments. + * Fixed: USER command setting a users access to zero every change. + * Added: Auto-detecting MODES setting on networks with 005 reply. + * Changed: OPMODES renamed to MODES, affecting how many modes per line. + * Removed: BANMODES setting. + * Fixed: Compiled OK with GCC 3.4.3 + * Removed: ADD, DEL commands, use USER +/- instead. + * Fixed: ESAY no longer suffers from buffer overflow problems + * Fixed: AUB will no longer unban Shits level 3+ + * Fixed: Session file now saves all (?) new options and settings + * Fixed: NCL (Nick flood kicks) now work properly. + * Changed: "NETPASS" setting renamed to "LINKPASS" + * Changed: file comment char changed to ; (semicolon) + * Removed: ADDSERVER, DELSERVER commands, use SERVER +/- instead. + * Added: telnet. you can connect to the bot via the botnet listen socket. + * Added: Its now possible to use an internal md5 version if your + system does not provide one. ./configure --md5=internal + * Added: Settings now have access levels associated with them. + It should be impossible to view/set/access a setting if your + level is insufficient. + * Added: USER command now accepts option +/-host + Usage: USER <+HOST|-HOST> + * Removed: HOST command. + * Added: the mech now accepts certain chars at the end of its + nickname when you give it commands, ex: "mechname," works + * Added: Notify no longer loads automatically, you need + to put "NOTIFY -RELOAD" in the configuration file. + * Added: Notify nicks can now be added and removed online + Usage: NOTIFY [+ [mask(s)] [:comment]] + Usage: NOTIFY [- ] + * Added: DCC file receive and settings for DCC control + Usage: SET DCCANON + Usage: SET DCCUSER + Usage: SET DCCFILES + * Removed: REPORT command, use `SET [channel]' for the same functionality + * Changed: commands can now be disabled permanently (until bot is restarted) + Usage: CHACCESS disable + * Added: DYNLIMIT string setting to configure the dynlimit feature + Usage: SET DYNLIMIT xx:yy:zz + where xx is the number of seconds between each mode update, 20-600 seconds + where yy is the number to divide the current user number with to get the + window size, 5-50 (ex, 100 users on channel divided by 10 = window of +/-10) + where zz is the minimum window size, 1-50. + default values are 90 seconds, window 10 and minimum 4; "90:10:4" + * Removed: DYNLIMIT toggle + * Added: userlist sharing over botnet + * Added: MD5 bot linking password exchange support + * Added: TCL: `hook dcc_complete - callback' + callback arguments: to, filename, cps + * Fixed: Compiled OK with GCC 3.4.2 (with -fno-strict-aliasing) + * Added: internally generated html for /internalstatus.html + and /internaldebug.html (if compiled with debug support) + * Added: INFO command shows basic statistics for all channels + * Changed: SERVERLIST command removed, SERVER command with + no arguments now lists all known servers + * Added: Support for Undernet WALLCHOPS command (WALL) + * Fixed: MODE command now working as intended + * Added: tcl_dcc_sendfile + returns filesize on success + * Changed: E3 now accepts nicks of any length + * Added: DCC filesend: SEND [to] + * Added: now using async/rawdns to lookup uptime hostname + * Added: now using async/rawdns to connect to irc servers + * Changed: timestamp in log format now includes year, month, date + * Fixed: crash bug with `SETPASS none' + * Fixed: crash bug with HELP commands having no arguments + * Added: ALTNICK setting for desired alternate nicks + * Fixed: Removed lingering prototypes in h.h [ wy|d + Deke ] + * Added: MD5 support for password encryption + * Fixed: mech.session now gets saved on SHUTDOWN [ endorphin ] + * Added: CTIMEOUT setting for connection timeouts + * Added: NOTE command to send notes to users in the userlist + Usage: NOTE + * Fixed: Respawn counter now works properly when bot restarts + * Added: dynamode.c, dynamic altering of +l limit according + to the number of users present in the channel + * Fixed: `SETPASS none' bug regarding min pass len + * Changed: SeenList is now kept on process level + * Added: UMODES setting to control the modes that the bot + sets for itself when connecting to servers + * Changed: ESAY $tog() has been changed into a full-featured + system that handles all settings; $tog() renamed to $var() + * Fixed: ESAY $tog() parsing bug causing bad output + * Added: Something that still looks like a HTTP interface + this is a very sketchy feature sofar tho + * Added: Chosen commands {now=HELP,USAGE} can be redirected + to other users or channels + * Added: ONJOINFILE channel setting, wonder why :] + * Added: greets (readded from old times) + * Added: Keep debug fd and server sockets when using + the RESET command. Even tho it execs, it's never + disconnected from the server. + * Fixed: Auths not timing out if user was visible in [ found by endorphin ] + an old (kicked/parted) channel userlist + * Removed: TOG command, use SET instead + * Changed: aliases are now way powerful + * Added: All types of SPY can be redirected to + channels and/or files. + Usage: SPY [channel|">" filename] + Usage: RSPY [nick|channel|">" filename] + * Removed: CHANLOG toggle + * Removed: STATMSG, SPYMSG, RSTATMSG, RSPYMSG + * Added: Short delay for server reconnect actions + * Fixed: Random server disconnects + * Removed: USTATS, stupid redundant command + * Removed: SHITLVL command, use RSHIT+SHIT instead + * Removed: All linking and telnet code is gone and + pipeuser code is now broken + * Changed: config file is now a list of `real' commands + * Changed: CMDCHAR --> SET CMDCHAR + * Changed: AUTOLINK --> TOG AUTOLINK <0|1|ON|OFF> + * Changed: USERFILE --> SET USERFILE + * Changed: HASONOTICE --> TOG ONOTICE <0|1|ON|OFF> + * Removed: IWM toggle, didnt do anything useful + * Fixed: Random disconnects from DCC + * Added: Extra entries in randinsult.e + * Fixed: Merged endo's seen-ago patch [ endorphin ] + * Added: Uptime reporting client code + * Fixed: WinGate proxy now works again... + * Added: Fastnick option hidden in src/config.h + * Fixed: Bot not knowing it was opped when joining [ found by endorphin ] + empty channels. + * Changed: Split mech.help into ./help/* files + * Fixed: No-key-saved in session file plus some small + optimizations... diff --git a/VERSIONS-1 b/VERSIONS-1 new file mode 100644 index 0000000..5a65670 --- /dev/null +++ b/VERSIONS-1 @@ -0,0 +1,582 @@ + +2.7.6.1 -- June 4th, 2000. + + * Fixed: Strcat() not skipping to end of string + +2.7.6 -- February 17th, 2000. + + * Fixed: Lockup/crash bug in WALL command + * Fixed: Major headache with DIE has been fixed + * Fixed: Brain-damaged-undernet-coder-kludge for +k + where the key itself is not sent if the user (bot) + isnt opped + * Fixed: The bot will no longer kick/+ban itself if it + triggers massdeop protection + * Fixed: You can now delete servers by their real name + aswell and not just by the name listed in the config + * Fixed: With AOP enabled, AVOICE would not work at all [ endorphin ] + * Fixed: DCC user + linking crash bug + * Fixed: Major crash bug in the newly optimized + chanuser handling... + * Fixed: Ghost chanuser problem when the bot joins a + channel and someone else joins before the channel is + synched (/WHO list has been received) + +2.7.5 -- February 12th, 2000. + + * Removed: UPDATE command (didnt actually have any real + effect on anything...) + * Changed: spymessages are now prefixed with `[message]' + instead of `$' + * Changed: The CHANNEL command now shows all the info + that CSTATS and ALLSTATS used to show, and CSTATS & + ALLSTATS has been removed + * Changed: A discusting amount of optimizations... + * Changed: Temporarily took out the WinGate question + since it doesnt work + * Added: configure script now asks if superdebug is to + be included instead of enabling it by default + * Added: SIGMASTER setting to decide which bot handles + signals like, HUP, USR1, etc + * Changed: Lots of optimizations + * Fixed: New (since 2.7.3) bug with AVOICE system + * Changed: Special case get_token()'s split into chop() + routine for optimizations + +2.7.4 -- January 31st, 2000. + + * Fixed: DEBUG mishap (possible crasher) in + SockConnect() (formerly known as connect_by_number) + * Fixed: NOIDLE sending refreshes before the bot was + `officially online' + * Fixed: SEGV and redundant code in find_userhost() + +2.7.3 -- January 19th, 2000. + + * Fixed: WIN and ALS was missing from feature line + * Changed: Listener socks have been made non-blocking + * Fixed: Compiling errors re undef'd DEBUG & PIPEUSER + * Changed: avoice and on_join() heavily optimized + * Changed: Renamed strcasecmp to Strcasecmp cuz some + ld tools would bong out when it finds two functions + with the same name (one internal, one in libc) + * Added: gcc/gprof profiling support + * Added: sendnotice() was missing from h.h [ found by azmodan ] + * Fixed: The fork bug when superdebug was undef'd + * Fixed: Bug in find_userhost() (?) caused crashes + when it was called by Link_needop() + * Fixed: Unused user records werent skipped in the + saveuserlist() routine -- until now + +2.7.2 -- January 16th, 2000. + + * Fixed: Updated server IPs in sample.set + * Changed: SeenList memory allocating and handling + have been fixed & optimized + * Added: More superdebug debug info + * Fixed: Memory leaks + * Fixed: KSLIST now works [ found by endorphin ] + * Added: Channel keys are now saved to session file [ found by detriment ] + * Fixed: Help command works again + * Fixed: link.c now compiles OK even if PIPEUSER is [ azmodan ] + undefined + * Fixed: If AVOICE setting was set to 1, it would +v [ endorphin ] + everything in sight even though it should only +v AV + users, its now handled correctly + * Added: Most 2.6.1b11 beta addons have been merged + with the current source-tree + +2.7.1 -- December 2nd, 1999. + + * Fixed: needinvite now actually works + * Changed: rewrote my_stricmp and renamed it + strcasecmp() + * Fixed: Small bug in time2away which could cause + garbage to be appended to the "am" and "pm" in some + time strings. + * Changed: telnet users no longer need to verify and + never gets deauthenticated + * Changed: All f* (buffered file) routines has been + replaced with open(), sockread(), send_to_socket(), + and equivalents == less libc calls & better code use + * Changed: LAST now stops listing "(none)" entries + after the first one has been printed + * Added: Channel keys can now be specified in the + config file like so; + CHANNEL + * Changed: DCC clients are now being deleted in the + main loop instead of all over the place + * Changed: DCC and telnet now uses non-blocking IO + * Changed: freadln() has been removed and sockread() + is being used to read files instead + * Added: BinaryDefault settings are now used to avoid + saving default settings in session files + * Changed: Rewrote the readcfgfile() function to use + sockread() instead of freadln() + * Changed: the usage texts has now been moved into + the code again. It should be more concurrent aswell + as one less external file to keep track of + * Changed: Removed 95% of all time() calls by making + "now" a global variable. time() is called after + select() to update it + * Added: -Wshadow switch to configure script so that + imporoperly shadowed variables can be weeded out + * Fixed: SEGV in RKS (a simple swap of two lines) [ found by mr_c ] + +2.7.0 -- October 24th, 1999. + + * Added: Partyline users are now visible across links + * Changed: New routine for reading server input. This + breaks wingate support but improves overall quality + and speed. All server reads are now completely + converted to non-blocking IO + * Added: "-o file" option for debug output + * Changed: Optimizations in the handling of variables + * Added: More warnings for stray settings in config + file parser + * Changed: Global settings are now bot specific, not + process specific as before. This will require that + some mech.set files be rewritten (global settings + moved into the bot configuration part (and possibly + duplicated for multiple bots)). + * Fixed: Idle-kick (TOG IK, SET IKT) now works [ endorphin + proton ] + * Added: Sanity checking in CMD routines (disallow certain + commands, like CHAT, CMD and SHUTDOWN) + * Added: Command table compile-time parser has been merged + from 2.6.1b10+ + * Added: --location=LOCATION option in configure script + * Fixed: system() calls to reboot/reset the mech has been + replaced with an execl() call (in main.c->mech_exec()) + * Fixed: Makefile stub now handles "make mech" like it + should + * Fixed: DCC/telnet users who "flood" the partyline + with more than 2000 bytes (editable in config.h) + are disconnected (chargen DoS fix) + * Fixed: Mech now silenty ignores DCC CHAT requests + originating from ports below 1024 (chargen DoS fix) + * Fixed: (hopefully) checkmech problem on some machines + (portable shellscripting is a bitch) + * Fixed: get_userlevel() returned 100 for the bot itself, + it now returns 200 which is more appropriate + * Fixed: "LAST 20" now works as it should + * Fixed: Shitlist related bugs, does it all work now? + * Added: SHUTDOWN command + * Removed: NSL and FINGER commands (this code is much too + troublesome, it can be made much more simple and secure) + * Added: TODO file + * Fixed: CPU-eating bug in connect_to_server() + * Fixed: SERVERLIST now reports connection errors properly [ endorphin ] + * Changed: AVOICE is now a SET (0 = no autovoice, [ endorphin ] + 1 = autovoice +AV users, 2 = autovoice everyone) + * Fixed: connect_to_server() would connect to the MOST used + server in the serverlist, this has been corrected. + * Changed: connect_to_server() has been optimized [ proton + endorphin ] + * Fixed: parse_kick() would remove the kicker from the [ endorphin ] + internal channel lists and not the person being kicked + * Fixed: Port parameter for DELSERVER now works properly [ endorphin ] + * Changed: Invalid nicks in randnicks.e was replaced with + valid ones + * Fixed: SEGV in ADDSERVER command (user need access 80+) + * Fixed: SEGV in VERIFY caused by too long passwords being + allowed. Password must now be between 4 and 50 chars + * Fixed: SEGV in SERVER command (user need access 80+) + * Fixed: WHO voice/op bug + +2.6.2 -- September 10th, 1999. + + * Added: AutoVoice support. + * Added: WinGate -w option for SPAWN command. + * Fixed: PASSWD command no longer requires "old" password + if no password has been set. + Usage: PASSWD [oldpassword] + * Fixed: Very vague infinite-loop possibility in server + connects existed, it has been fixed. + * Fixed: NOIDLE not working [ starlite ] + * Fixed: Authentication lost after nick change [ starlite ] + * Fixed: Problem with shitlist functions [ starlite ] + * Fixed: The "/msg " bug. + +2.6.1.1 -- + + * Fixed: Problems with CMD execution on multi-headed mechs. + * Fixed: Problem with authentications staying in place on + multi-headed mechs. + * Fixed: ESAY $channels listing inactive/parted/not-joined [ found by DMC ] + channels + * Fixed: Lockup bug in main loop when dealing with signals. + * Fixed: Telnet password checking. + * Fixed: Updated servers in sample.set. + * Fixed: Bot not rejoining when it's kicked [ found by DMC ] + * Fixed: CHAT command not working on Intel hardware. + +2.6.1 -- + + ## Beta 10 Marker ## + * Fixed: SEGV when doing "-TOG * toggle". + * Fixed: New bug from Beta 9 wrecking servers connects. + * Fixed: Password authentication problem... ouch. + + ## Beta 9 Marker ## + * Added: NEED-INVITE should now be working. + * Changed: Output format of HELP, CHANNELS, SERVERLIST + and BANLIST. + * Changed: Internal handling of channels. New bugs might + have been introduced. + * Fixed: A bundle of optimizations. + * Fixed: Session data is now also saved on SIGTERM. + * Fixed: RT now checks if the user giving the command has + access on the target channel. It's also possible to set + topics in channels with no +t flag when the bot isnt + opped. -t has also been fixed in TOPIC command. + * Fixed: Mech rejoined *all* channels known to it when + reconnecting to server, this is not correct behaviour + since it might have been -PART'ed but not -FORGET'ed. + * Fixed: CSERV didnt print the real server name if one + was available (well it does now!). + * Fixed: User turned into ghost users if they parted the + channel with a message. + * Added: Help and correct usage line for WHO command. + * Fixed: WHOM now formats output correctly for linked bots. + * Fixed: vars.c should now compile even with NEWBIE support + enabled. + + ## Beta 8 Marker ## + * Fixed: Crash bug in on_msg() on SunOS. + * Fixed: Missing reference to mcmd[] in combot.c. + + ## Beta 7 Marker ## + * Changed: Source distribution tarball is now unpacked into + a versioned directory, GNU-style. + * Changed: WHOM output format slightly changed. + * Fixed: VOICE doing +o if no nick was given. This is a bug + since channel_massmode() was changed. + * Changed: Refreshed the mech.help and mech.usage files. + * Fixed: USERHOST command now works... + * Added: Command USAGE, shows the usage for a command. + Usage: USAGE + * Changed: Format of LAST output has been changed. It's now + also a per-bot list, not a global as before. + * Added: Server passwords, ALIAS settings and wingate + settings are now saved to session file. + * Changed: SET CMDCHAR => CMDCHAR. With no args given, the + current commandchar is printed. Only superusers can change + the commandchar. + Usage: CMDCHAR [cmdchar] + * Changed: SET WINGATE => WINGATE + Usage: WINGATE + * Changed: SET VIRTUAL => VIRTUAL + Usage: VIRTUAL + * Changed: SET LINKPORT => LINK PORT + Usage: LINK PORT + * Changed: LUSERS output format has been changed. + * Fixed: Userlist loading in multi-headed bots. + * Fixed: savesession() return values. + * Fixed: ALIAS references to on_msg_command[] => mcmd[]. + * Fixed: Missing @SESSIONS@ thingy in config.h.in. + * Removed: Commands AOP, RAOP and PROT: replaced by USER. + + ## Beta 6 Marker ## + * Added: The missing LINK stuffs in session support. There + could still be some more missing though... Also added a + configure section for it. + * Changed: With NEWBIE support *disabled* the REPORT cmd + now prints all the settings in a much more dense format. + * Fixed: Autolink would try to connect to entities with no + linkport listed, this has been fixed. + + ## Beta 5 Marker ## + * Changed: No more hardcoded limit for how many bots can + run in a process at the same time. Spawn all you want ;P + All these changes probably introduced a bunch of new bugs. + * Changed: Replaced an unbelievably ugly kludge in on_msg() + with some decent code (for "bot cmd ..." style commands). + * Fixed: Garbage and potential bugs in do_linkcmd(). + * Changed: Lots of optimizations/rewrites all over. + * Changed: channel_massmode() has been modified to accept + nicks and several patterns at a time enabling stuff like + "-deop *snuffit.org *aol.com sicko dumbo". + * Added: Makefile stub in the root directory which passes + on all make commands to the src/Makefile. + * Changed: Lots of code optimizations in on_mode(). + * Fixed: Having +l in ENFM would reset the limit to 69 if + the limit was removed. Its now reset to the original value. + * Fixed: Spelling error in configure script. + + ## Beta 4 Marker ## + * Fixed: Small memory leak in parse_part(). + * Fixed: Typo stuff stopped the bot from undoing bans that + were set on protected users by people not in the userlist. + * Fixed: Spy now prints channel for both quits and + nickchanges. If a person is in several spied-on channels, + the change will be seen several times. + * Fixed: Lots of bizarre bug-ish stuff in on_kick(). + * Fixed: Using USER command to set protlevel now works. + * Changed: CORE information has been enhanced and the info is + presented in a more dense format. This has made the INFO + command useless so it has been removed. + * Fixed: find_shit() was broken so shitlist wouldnt work. + * Changed: Output of HELP command has been changed. + * Changed: Output of SERVERLIST command slightly changed. + * Fixed: Screwed output in CHANNELS, ALLSTATS and CSTATS + commands when both +l and +k was set. Output of CHANNELS + command has been slightly changed. + + ## Beta 3 Marker ## + * Fixed: Problem with %*-style strings in ESAY. + * Fixed: Problem adding users with long handle names. + * Fixed: BOTLEVEL (200) users could chat/telnet a bot. + (BOTLEVEL users *cannot* execute commands tho, they can + only listen to statmsgs and partyline). + * New command: USER, should make user sharing work altho + some parts are still missing. See HELP USER for more details. + Usage: USER + * SIGHUP and SIGUSR1 now affect only the 1st created bot. + Before it was rather random which performed the action. + * Fixed SEGV problem in Atoi(). Caused SEGV if reading config + file w/ servers with no ports. + + ## Beta 2 Marker ## + * Fixed obscure SEGV in the backgrounding routine (Ugly but + not a showstopper) + * Single char error prevented adding of new KICKSAYs. Fixed. + * Public password commands (VERIFY, PASSWD, SETPASS) are + ignored to discourage idiots from giving out passwords. + * Failed autolinks spew 'Link to "blablabla" failed', now only + manual LINK UPs will give that message. + * Telnet has been restricted to L10+ users (as with DCC). + * With LINKING defined, ADDed users werent locked as needed + thus creating ghost users. This would be a major problem + when setting up a 2.6.1b1 for the first time... Fixed. + * Fixed SEGV in cycle_channel(). + * Fixed small FD leakage in parse_error(). + + ## Beta 1 Marker ## + * Added BASIC support for userlist sharing over mechnets. + Its not quite finished yet. + * Added SET WINGATE command. + Usage: SET WINGATE + * Added missing code for saving the ECHO flag. + * Link messages from "$" are passed to send_global_statmsg(). + This should have been in 2.6.0 but was forgotten. + * Verification is required before a bot will reply to + DCC CHAT requests. + * Fixed small bug in cfg_nickname(). Trailing spaces or [ found by Qm-D ] + other illegal nick could mess things up really big + * Added WinGate support. + * Added PASS support for server connect. + * Made [#channel] option for USERLIST command also list + global users. Use -C option to list channel-only users. + * Added command BYE for "clean" DCC/Telnet disconnects. + Usage: BYE + * MAJOR struct rewrites. New bugs can have been introduced. + +2.6.0 -- May 27th, 1999 + +2.5.31 -- Version changed to 2.6.0 + + * LINK DOWN command completed (`Scooby). + * Completed $NICK link command for synched bot nicks on + mech networks. + * leave_channel() has been moved into do_leave(). + * parse_352() (RPL_WHOREPLY) has been optimized/fixed. + * do_steal() and do_rsteal() has been rewritten. + * Channel steals has been optimized and #def'd. The steal + setting is now part of the channel struct instead of + having its own list pointer in the bot struct. + * Revenge kick has been optimized. + * New config file command: ALIAS, use it to set an alias + for an already existing command. A section has been added + to the configure script has been added for this feature. + Usage: ALIAS + * Telnet now works (it seems anyways), a section in the + configure script has been added for this feature. Linking + has to be enabled to allow telnet. + * Single char error in send_statmsg() has been fixed so that + it sends a timestamp as it should. + * TRACE command removed. I'd be surprised if anyone even + knew what this command was used for. + * TOPKICKS command removed. Too much code for so little. + * DCCLIST command which was pretty pointless, has been + removed (Use WHOM instead). + * Single char error made "Virtual host" always appear in + the CORE info even if no vhost was set. Fixed. + + ## Beta 10 Marker ## + * Fixed the no-rejoin-after-disconnection bug. + * HELP and HELPFULL now lists all commands as a comma + separated list instead of a fixed width table. + * CORE will no longer list active channels (use CHANNELS). + * CORE will now show the currently set vhost, if any. + * SET can now be used to change virtual host of a bot. + Usage: SET VIRTUAL + * Fixed a problem where a mech with an invalid vhost would + cause heavy system load, it now reverts to INADDR_ANY if + the vhost cannot be used. + * Added code for the "HELP on " and "Level needed:" + in the help-routine which I apparently forgot when + I remade it. + * Removed the BK toggle since all the code for the + beep-kicking has been removed earlier. + * Added missing help topic for SCREW command. + * Command SCREWBAN has been renamed to SCREW. + * Default level for SPAWN has been changed from 90 to 100. + * Fixed compilation errors in main.c on some UNIX flavours. + * There was FD leakage in the do_help() routine so I rewrote + the whole thing. This should greatly help overall stability. + * Default level for SPYMSG was changed from 80 to 90 + (Why? Cuz RSPYMSG was level 90...). + + ## Beta 9 Marker ## + * Userlist related coredumps on 64-bit systems has been + fixed (hopefully anyways). + * USTATS now checks for matching handles before searching + after a user by nick. + * Complete rewrite of the README file. + * SHITREASON command has been removed: too much code for + such a small task IMO, use RSHIT+SHIT instead. + * mech.usage routines has been remade yet again. Some + commands has been added (DO, JOIN, SET). + * URL for www.emech.cx added to README file + * mech.help updates (ADD, SET, TOG). + * ADD does some extra sanity-checking. + * Bots can now be added with the ADD command using both + "BOT" level and "200" ("BOT" is only for backwards + compatibility, it confuses lamers). + * HOST command now checks for invalid or global masks + before adding them... + * SET can now be used to change commandchar. + Usage: SET CMDCHAR + * SET can now be used to change Linkport. + Usage: SET LINKPORT <1-65535> + * TOG command now supports arguments for wether a toggle + should be SET, UNSET or TOGGLED (default). + Usage: TOG [channel|*] [0|1|ON|OFF] + * Internal handling of CMD command execution have been + improved somewhat... + * Command spy (TOG SPY) now wont print random nicks when + commands are executed across links. + * Completed the "mech.usage" file. I will try to make + good use of it now aswell ;) + * configure script now checks if -pipe switch works and + uses it (cuts down on disk access). + * The beep-flooding code has been removed (when was the + last time you saw a tsunami flood?) + * The problem with the mech joining and parting when + its lagged has been fixed (and hopefully nothing new + has been put in its place). + * HELP commands with no arguments given now calls HELPFULL [ guppy ] + to list all commands + * HELPFULL now lists commands grouped by level. + * The 'Beginner help routines' are as follows: + DIE command in mech configuration file. + REPORT shows descriptions of what certain switches do. + * New linking debug code added (debug output to LinkEvents + file). Debug need to be compiled into the mech to enable + LinkEvents debugging. + * Added a Levels section in the online help file (mech.help). + * The SETPASS now works as its supposed to (it used to allow + users to set passwords of any other user. Also, you can now + unset passwords with SETPASS command if you're at level 80 + or higher, just do "SETPASS none". + * Bad behaviour on behalf of the configure script could + cause some big problems (Linux systems) if it was executed + as root, this has been fixed. + + ## Beta 5 Marker ## + * New command added: DELSERVER, and updated mech.help and + mech.usage with help for it. + * Added a couple of lines to randsignoff.e + * Internal structure of serverlist has been reworked for + a more efficient (hopefully anyways) format. Some commands + has changed functionality because of it (CORE,SERVERLIST). + * New variable for ESAY: $links. It will show all active mech + links (if any), prefixing local uplinks with a '*' and + remote uplinks with a '='. + * Setting prot to 4 would actually reset it to 0 due to + a storage limit (only 2 bits were used), this has been + fixed (it now uses 3 bits, woopie!!)... + * Linking has been fixed/changed to make it more stable and + foolproof. Linking with older versions will no longer work, + so make sure you upgrade... + * SIGSEGV handler is no longer installed when mech is runnin + in debug mode (mech -d) so that coredumps will be made + properly. + * Removed LOADKICKS and SAVEKICKS commands since they dont + serv much of a purpose + updated helpfile. + * Fixed an infinite loop in the USERLIST command + (only happened if you listed users based on mask). + * Fixed infinite loops around SIGSEGV debug routine (it now + cores and exists even if it was compiled with debug). + * Minor/cosmetic changes of configuration file loader. + * configure script now asks user about compile time options + and alters config.h based on the answers. + * Added a DIE option for configuration file loader to get + rid of the absolute lamers who copy sample.set to mech.set + and run it... + +2.5.30 -- November 15th, 1998 + + * Single char error in command replies over links resulting + in no replies at all. + +2.5.29 -- November 15th, 1998 + + * PROT now works for bots aswell (level<200 = access denied) + * PROT command now accepts level 0 to 4, eliminating the + need of the RPROT command which has been removed. + * More mech.usage... + * SESSIONS define is now off by default (sessions isnt fully + functioning yet) + * Due to braindamage some of the new linking routines caused + bots to stick around while entities was unlinked, causing + huge link-lists (.whom) after running for a while. This + should be fixed now... This fix should also make for more + stable linking of mechs :] + +2.5.28 -- November 13, 1998 + + FRIDAY THE 13TH!!! -- uh oh... + + * Fixed SESSIONS define in cfgfile.c (error if SESSIONS is + undefd). + * When you verify, you should be verified with all linked bots, + if you have the same password. + * SCREWBAN now only accepts nicknames (as it should). + * WALL command now supports undernet NOTICE @#channel stuff, + add HASONOTICE to your config file to make use of it. + +2.5.27 -- October 24th, 1998 + + * You can no longer add multiple identical masks to the same + handle. It will complain (HOST command) or silently ignore + the mask (internal routines). + * Fixed the SPY & RSPY commands (RSPY wasnt working at all). + * Added a new user #define for dynamic command levels (change + of needed levels with chaccess). + * Fixed a few entries in the mech.help file. + * Added a few entries to mech.usage. + * Updated the sample.set file with some linking stuff. + * Added a cfg_noop routine to kill warnings for the */NO* type + tags. + * Added README file. + +2.5.26 -- October 20th, 1998 + + * Fixed the coredump from trying to do + ``LINK UP unknownentity'' + * Fixed some config.h defines stuff (when undef'd it would + error when compiling). + +2.5.25 -- October 19th, 1998 + + * Fixed an infinite loop in send_global_statmsg() when you + had more than 2 bots in the same process and chatted more + than one bot. + * DEL command now only accepts handles to delete. + * VOICE with no nickname given did +o instead of +v, fixed. + * Added a VERSIONS file... + +2.5.24 -- September 10th, 1998 + + no previous record. + diff --git a/checkmech b/checkmech new file mode 100755 index 0000000..c745923 --- /dev/null +++ b/checkmech @@ -0,0 +1,54 @@ +#! /bin/sh +#-------------------------------------------------- +# Cron script for EnergyMech 3.0 +#-------------------------------------------------- +# +# you can add this to your crontab (crontab -e) as +# something like: +# +# 0,10,20,30,40,50 * * * * /path/to/checkmech +# +# this will check the mech every 10 minutes. +# +#-------------------------------------------------- + +echo "You havent edited checkmech yet!!" +exit 0 + +# CHANGE THIS LINE!! +cd /path/to/energymech/directory + +# name of executable +RUN=./mech + +# options passed to the energymech +# ( debug, non-default config file... ) +OPT= + +# example: debug output to `mech.debug' file +#OPT=-d -o mech.debug + +# example: non-default configuration file +#OPT=-f othermech.conf + +# Send output to... +OUTPUT=./mech.cron +#OUTPUT=/dev/null + +if [ -r mech.pid ]; then + PID=`cat mech.pid` + if [ -r /proc/$PID ]; then + exit 0 + fi + if ( kill -0 $PID 1> /dev/null 2> /dev/null ); then + exit 0 + fi +fi + +# its not running... + +echo >> $OUTPUT +echo "Mech restarted:" >> $OUTPUT +( date 2>&1 ) >> $OUTPUT +echo >> $OUTPUT +( $RUN $OPT 2>&1 ) >> $OUTPUT diff --git a/config/cc.c b/config/cc.c new file mode 100644 index 0000000..e9c0994 --- /dev/null +++ b/config/cc.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +int main(int argc, char **argv) +{ +#if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 3) + write(1,"gnucc33\n",9); + exit(0); +#endif +#if (__GNUC__ >= 2) && (__GNUC_MINOR__ >= 95) + write(1,"gnucc95\n",8); + exit(0); +#endif +#ifdef __GNUC__ + write(1,"gnucc\n",6); +#else + write(1,"yes\n",4); +#endif + return(0); +} diff --git a/config/inet_addr.c b/config/inet_addr.c new file mode 100644 index 0000000..1b7b58e --- /dev/null +++ b/config/inet_addr.c @@ -0,0 +1,9 @@ +#include +#include +int main(int argc, char **argv) +{ + struct in_addr ia; + ia.s_addr = inet_addr("0.0.0.0"); + return(0); +} + diff --git a/config/inet_aton.c b/config/inet_aton.c new file mode 100644 index 0000000..9cab812 --- /dev/null +++ b/config/inet_aton.c @@ -0,0 +1,8 @@ +#include +#include +int main(int argc, char **argv) +{ + struct in_addr ia; + return(inet_aton("0.0.0.0",&ia)); +} + diff --git a/config/ldtest.c b/config/ldtest.c new file mode 100644 index 0000000..45f6078 --- /dev/null +++ b/config/ldtest.c @@ -0,0 +1,32 @@ +#include +#include + +#if defined(__GNUC__) && defined(__ELF__) +#define __page(x) __attribute__ (( __section__ (x) )) +#else +#define __page(x) /* nothing */ +#endif + +#define S(x) x,sizeof(x) + +__page(".text.e") +int function1(int a) +{ + return a + 1; +} + +__page(".text.c") +int function2(int a) +{ + return a + 2; +} + +__page(".text.d") +int main(int argc, char **argv) +{ + if (((void*)main < (void*)function1) && ((void*)function1 < (void*)function2)) + write(1,S("yes\n")); + else + write(1,S("no\n")); + return(0); +} diff --git a/config/md5.h b/config/md5.h new file mode 120000 index 0000000..7fc84a2 --- /dev/null +++ b/config/md5.h @@ -0,0 +1 @@ +../src/md5/md5.h \ No newline at end of file diff --git a/config/md5_internal.c b/config/md5_internal.c new file mode 120000 index 0000000..b97ef28 --- /dev/null +++ b/config/md5_internal.c @@ -0,0 +1 @@ +../src/md5/md5.c \ No newline at end of file diff --git a/config/ptr_size.c b/config/ptr_size.c new file mode 100644 index 0000000..4931814 --- /dev/null +++ b/config/ptr_size.c @@ -0,0 +1,10 @@ +#include +#include +#include +#include +int main(int argc, char **argv) +{ + printf("%i\n",sizeof(void*)); + return(0); +} + diff --git a/config/pw.c b/config/pw.c new file mode 100644 index 0000000..05bcd0f --- /dev/null +++ b/config/pw.c @@ -0,0 +1,27 @@ +#include +#include +#include +char * CRYPT_FUNC (const char *, const char *); +int main(void) +{ + char *enc; + int md5,des; + + des = md5 = 0; + enc = CRYPT_FUNC ("password","$1$XX"); + if (enc && !strcmp(enc,"$1$XX$HxaXRcnpWZWDaXxMy1Rfn0")) + md5 = 1; + enc = CRYPT_FUNC ("password","XX"); + if (enc && !strcmp(enc,"XXq2wKiyI43A2")) + des = 1; + + if (md5 && des) + write(1,"DESaMD5\n",8); + else + if (des) + write(1,"DES\n",4); + else + if (md5) + write(1,"MD5\n",4); + return(0); +} diff --git a/config/python.c b/config/python.c new file mode 100644 index 0000000..7854ff9 --- /dev/null +++ b/config/python.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + Py_Initialize(); + Py_Finalize(); + return 0; +} diff --git a/config/socket.c b/config/socket.c new file mode 100644 index 0000000..a73ebd0 --- /dev/null +++ b/config/socket.c @@ -0,0 +1,9 @@ +#include +#include +int main(int argc, char **argv) +{ + int s; + s = socket(AF_INET,SOCK_STREAM,0); + return(0); +} + diff --git a/config/tcl.c b/config/tcl.c new file mode 100644 index 0000000..3a76edd --- /dev/null +++ b/config/tcl.c @@ -0,0 +1,10 @@ +#include +#include +Tcl_Interp *interp = NULL; +int main(void) +{ +interp = Tcl_CreateInterp(); +Tcl_Init(interp); +return(0); +} + diff --git a/config/unaligned.c b/config/unaligned.c new file mode 100644 index 0000000..bd3bb1b --- /dev/null +++ b/config/unaligned.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +int buserror(char *data) +{ + int *x; + x = (int*)(data+1); + return(*x); +} +int main(int argc, char **argv) +{ + char busdata[6]; + int x; + strcpy(busdata,"Xyes\n"); + x = buserror(busdata); + write(1,&x,4); + return(0); +} diff --git a/config/which b/config/which new file mode 100755 index 0000000..55307ea --- /dev/null +++ b/config/which @@ -0,0 +1,14 @@ +bin=$1 +spath="`echo $PATH | sed 's/:/ /g;'`" +if [ -x $bin ]; then + echo $bin + exit +fi +for pd in $spath; do + if [ -x $pd/$bin ]; then + echo $pd/$bin + exit + fi +done +echo 'not found' + diff --git a/configure b/configure new file mode 100755 index 0000000..9a67a96 --- /dev/null +++ b/configure @@ -0,0 +1,1144 @@ +#!/bin/sh +# +# EnergyMech, IRC Bot software +# Copyright (c) 1997-2009 proton +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +umask 077 + +# perl still unsupported +has_perl=no + +for opt +do + + case "$opt" in + -*=*) + optarg=`echo $opt | sed 's/.*=//;'` ;; + *) + optarg= ;; + esac + + yesno= + case "$opt" in + --with=* ) yesno=yes ;; + --enable=* ) yesno=yes ;; + --without=* ) yesno=no ;; + --disable=* ) yesno=no ;; + esac + + if [ "$yesno" = yes -o "$yesno" = no ]; then + IFSBACKUP=$IFS + IFS='=,' + notfirst= + for nn in $opt + do + if [ "$notfirst" ]; then + case "$nn" in + alias ) ft_alias=$yesno ;; + botnet ) ft_botnet=$yesno ;; + bounce ) ft_bounce=$yesno ;; + chanban ) ft_chanban=$yesno ;; + ctcp ) ft_ctcp=$yesno ;; + dccfile ) ft_dccfile=$yesno ;; + debug ) ft_debug=$yesno ;; + dynamode ) ft_dynamode=$yesno ;; + dyncmd ) ft_dyncmd=$yesno ;; + greet ) ft_greet=$yesno ;; + idwrap ) ft_idwrap=$yesno ;; + ircd_ext ) ft_ircd_ext=$yesno ;; + md5 ) ft_md5=$yesno ;; + newbie ) ft_newbie=$yesno ;; + note ) ft_note=$yesno ;; + notify ) ft_notify=$yesno ;; + perl ) ft_perl=$yesno ;; + python ) ft_python=$yesno ;; + rawdns ) ft_rawdns=$yesno ;; + redirect ) ft_redirect=$yesno ;; + seen ) ft_seen=$yesno ;; + session ) ft_session=$yesno ;; + stats ) ft_stats=$yesno ;; + tcl ) ft_tcl=$yesno ;; + telnet ) ft_telnet=$yesno ;; + toybox ) ft_toybox=$yesno ;; + trivia ) ft_trivia=$yesno ;; + uptime ) ft_uptime=$yesno ;; + web ) ft_web=$yesno ;; + wingate ) ft_wingate=$yesno ;; + esac + else + notfirst=yes + fi + done + IFS=$IFSBACKUP + fi + + case "$opt" in + --enable-*=*) + feature=`echo $opt | sed 's/^\-\-enable\-//; s/=.*$//'` ;; + --enable-*) + feature=`echo $opt | sed 's/^\-\-enable\-//;'` ;; + --disable-*) + feature=`echo $opt | sed 's/^\-\-disable\-//;'` ;; + --with-*=*) + feature=`echo $opt | sed 's/^\-\-with\-//; s/=.*$//'` ;; + --with-*) + feature=`echo $opt | sed 's/^\-\-with\-//;'` ;; + --without-*) + feature=`echo $opt | sed 's/^\-\-without\-//;'` ;; + *) + feature=___none___ ;; + esac + + case "$feature" in + debug | botnet | telnet | alias | seen | session | dyncmd | newbie | wingate | md5 \ + | ctcp | dccfile | uptime | redirect | greet | perl | profiling | tcl | dynamode | web \ + | note | notify | trivia | toybox | bounce | stats | rawdns | ircd_ext | idwrap | chanban | python ) + case _"$optarg"_ in + _yes_ | _no_ | __ ) + ;; + *) + echo "unknown argument for $feature":" $optarg" + echo "Usage: configure [options]" + echo "use \"$0 --help\" for help" + exit 1 + esac + ;; + ___none___ ) + ;; + *) + echo "unknown feature: $feature" + echo "Usage: configure [options]" + echo "use \"$0 --help\" for help" + exit 1 + esac + + case "$opt" in + --with=* | --without=* | --enable=* | --disable=* ) ;; + --enable-* | --with-*) + case "$feature"_"$optarg" in + alias_yes | alias_ ) ft_alias=yes ;; + alias_no ) ft_alias=no ;; + botnet_yes | botnet_ ) ft_botnet=yes ;; + botnet_no ) ft_botnet=no ;; + bounce_yes | bounce_ ) ft_bounce=yes ;; + bounce_no ) ft_bounce=no ;; + chanban_yes | chanban_ ) ft_chanban=yes ;; + chanban_no ) ft_chanban=no ;; + ctcp_yes | ctcp_ ) ft_ctcp=yes ;; + ctcp_no ) ft_ctcp=no ;; + dccfile_yes | dccfile_ ) ft_dccfile=yes ;; + dccfile_no ) ft_dccfile=no ;; + debug_yes | debug_ ) ft_debug=yes ;; + debug_no ) ft_debug=no ;; + dynamode_yes | dynamode_ ) ft_dynamode=yes ;; + dynamode_no ) ft_dynamode=no ;; + dyncmd_yes | dyncmd_ ) ft_dyncmd=yes ;; + dyncmd_no ) ft_dyncmd=no ;; + greet_yes | greet_ ) ft_greet=yes ;; + greet_no ) ft_greet=no ;; + idwrap_yes | idwrap_ ) ft_idwrap=yes ;; + idwrap_no ) ft_idwrap=no ;; + ircd_ext_yes | ircd_ext_ ) ft_ircd_ext=yes ;; + ircd_ext_no ) ft_ircd_ext=no ;; + md5_yes | md5_ ) ft_md5=yes ;; + md5_no ) ft_md5=no ;; + newbie_yes | newbie_ ) ft_newbie=yes ;; + newbie_no ) ft_newbie=no ;; + note_yes | note_ ) ft_note=yes ;; + note_no ) ft_note=no ;; + notify_yes | notify_ ) ft_notify=yes ;; + notify_no ) ft_notify=no ;; + perl_yes | perl_ ) ft_perl=yes ;; + perl_no ) ft_perl=no ;; + python_yes | python_ ) ft_python=yes ;; + python_no ) ft_python=no ;; + rawdns_yes | rawdns_ ) ft_rawdns=yes ;; + rawdns_no ) ft_rawdns=no ;; + redirect_yes | redirect_ ) ft_redirect=yes ;; + redirect_no ) ft_redirect=no ;; + seen_yes | seen_ ) ft_seen=yes ;; + seen_no ) ft_seen=no ;; + session_yes | session_ ) ft_session=yes ;; + session_no ) ft_session=no ;; + stats_yes | stats_ ) ft_stats=yes ;; + stats_no ) ft_stats=no ;; + tcl_yes | tcl_ ) ft_tcl=yes ;; + tcl_no ) ft_tcl=no ;; + telnet_yes | telnet_ ) ft_telnet=yes ;; + telnet_no ) ft_telnet=no ;; + toybox_yes | toybox_ ) ft_toybox=yes ;; + toybox_no ) ft_toybox=no ;; + trivia_yes | trivia_ ) ft_trivia=yes ;; + trivia_no ) ft_trivia=no ;; + uptime_yes | uptime_ ) ft_uptime=yes ;; + uptime_no ) ft_uptime=no ;; + web_yes | web_ ) ft_web=yes ;; + web_no ) ft_web=no ;; + wingate_yes | wingate_ ) ft_wingate=yes ;; + wingate_no ) ft_wingate=no ;; + profiling_yes | profiling_ ) ft_prof=yes ;; + profiling_no ) ft_prof=no ;; + esac + ;; + --disable-* | --without-*) + case "$feature" in + alias ) ft_alias=no ;; + botnet ) ft_botnet=no ;; + bounce ) ft_bounce=no ;; + chanban ) ft_chanban=yes ;; + ctcp ) ft_ctcp=no ;; + dccfile ) ft_dccfile=no ;; + debug ) ft_debug=no ;; + dynamode ) ft_dynamode=no ;; + dyncmd ) ft_dyncmd=no ;; + greet ) ft_greet=no ;; + idwrap ) ft_idwrap=no ;; + ircd_ext ) ft_ircd_ext=no ;; + md5 ) ft_md5=no ;; + newbie ) ft_newbie=no ;; + note ) ft_note=no ;; + notify ) ft_notify=no ;; + perl ) ft_perl=no ;; + profiling ) ft_prof=no ;; + python ) ft_python=no ;; + rawdns ) ft_rawdns=no ;; + redirect ) ft_redirect=no ;; + seen ) ft_seen=no ;; + session ) ft_session=no ;; + stats ) ft_stats=no ;; + tcl ) ft_tcl=no ;; + telnet ) ft_telnet=no ;; + toybox ) ft_toybox=no ;; + trivia ) ft_trivia=no ;; + uptime ) ft_uptime=no ;; + web ) ft_web=no ;; + wingate ) ft_wingate=no ;; + esac + ;; + --md5=internal) ft_md5=internal ;; + --use-cpuflags) cc_arch_opt=yes ;; + --no-cpuflags) cc_arch_opt=no ;; + --use-optimize) cc_optimize_opt=yes ;; + --no-optimize) cc_optimize_opt=no ;; + --use-gnudebug) cc_g_opt=yes ;; + --no-gnudebug) cc_g_opt=no ;; + --use-warnflag) cc_wall_opt=yes ;; + --no-warnflag) cc_wall_opt=no ;; + --use-pipeflag) cc_pipe_opt=yes ;; + --no-pipeflag) cc_pipe_opt=no ;; + --use-ofp) cc_ofp_opt=yes ;; + --no-ofp) cc_ofp_opt=no ;; + --yes) ft_alias=yes + ft_botnet=yes + ft_bounce=yes + ft_chanban=yes + ft_ctcp=yes + ft_dccfile=yes + ft_debug=yes + ft_dynamode=yes + ft_dyncmd=yes + ft_greet=yes + ft_ircd_ext=yes + ft_md5=yes + ft_newbie=yes + ft_note=yes + ft_notify=yes + ft_python=yes + ft_rawdns=yes + ft_redirect=yes + ft_seen=yes + ft_session=yes + ft_stats=yes + ft_tcl=yes + ft_telnet=yes + ft_toybox=yes + ft_trivia=yes + ft_uptime=yes + ft_web=yes + ft_wingate=yes + ;; + --help | -h) + cat <<__EOT__ +Usage: configure [options] +Configuration: + --help print this message +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-FEATURE[=ARG] use FEATURE [ARG=yes] + --without-FEATURE do not use FEATURE (same as --with-FEATURE=no) + --md5=internal Use internal routines for MD5 password hashes instead of system libraries +#md5 | ctcp | dccfile | uptime | redirect | greet | perl | dynamode | web \ +#note | notify | trivia | toybox | bounce | stats | rawdns | ircd_ext | idwrap | chanban | python ) + --with-alias ALIAS support + --with-botnet Botnet support + --with-debug Debug support + --with-dyncmd Dynamic command levels support + --with-newbie Newbie support + --with-profiling Profiling (gcc+gprof) + --with-seen SEEN support + --with-session Session support + --with-tcl Tcl support + --with-telnet Telnet support + --with-wingate Wingate support +__EOT__ + exit 0 + ;; + CC=*) + CC=`echo $opt | sed 's/.*=//;'` + ;; + *) + echo "unknown option: $opt" + echo "Usage: configure [options]" + echo "use \"$0 --help\" for help" + exit 1 + esac +done + +if (echo "testing\c"; echo 1,2,3) | grep c > /dev/null; then + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn > /dev/null; then + ac_n= + ac_c=' +' + ac_t=' ' + else + ac_n=-n + ac_c= + ac_t= + fi +else + ac_n= + ac_c='\c' + ac_t= +fi + +echo $ac_n "checking system type ... "$ac_c + +cf_ERR=yes +UNAME=`config/which uname` +test -x $UNAME && cf_ERR= + +if test -z "$cf_ERR" && cf_ERR=yes && $UNAME -s 1> /dev/null 2> /dev/null; then + cf_SYS=`$UNAME -s` + cf_ERR= +fi + +if test -z "$cf_ERR" && cf_ERR=yes && $UNAME -m 1> /dev/null 2> /dev/null; then + cf_MACH=`$UNAME -m` + cf_ERR= +fi + +case _"$cf_ERR"_ in + _yes_ ) + echo $ac_t unknown + echo "Unable to determine system type." + echo "Dont know how to compile the EnergyMech." + echo "exiting ..." + exit 1 + ;; +esac + +if [ "$cf_SYS" = AIX ]; then + cf_MACH= + cf_SYSMACH="$cf_SYS" +else + cf_SYSMACH="$cf_SYS"'-'"$cf_MACH" +fi + +echo $ac_t "$cf_SYSMACH" + +# +# +# + +echo $ac_n "checking for C compiler ... "$ac_c +if [ "$CC" ]; then + CCshort="$CC" + CC=`config/which $CC` +else + CCshort=gcc + CC=`config/which gcc` +fi +if [ ! -x "$CC" ]; then + CCshort=cc + CC=`config/which cc` + if [ ! -x "$CC" ]; then + echo $ac_t "not found" + echo "A working C compiler is needed to compile the EnergyMech" + echo "exiting ..." + exit 1 + fi +fi +CClong="$CC" +CC="$CCshort" +echo $ac_t "$CC" + +# set up things for test compiling + +TESTO=./test$$.o +TESTP=./test$$ +rm -f $TESTO $TESTP + +# +# does the C compile work? +# + +TESTC=config/cc.c + +echo $ac_n "checking whether $CC works or not ... "$ac_c +$CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null +WORK=no +if [ -x "$TESTP" ]; then + res=_`$TESTP`_ + WORK=yes + case $res in + _yes_ ) + ;; + _gnucc_ ) + cf_GNUCC=yes + ;; + _gnucc95_ ) + cf_GNUCC=95 + ;; + _gnucc33_ ) + cf_GNUCC=33 + ;; + esac +fi + +rm -f $TESTP + +if [ "$WORK" = yes ]; then + echo $ac_t yes + compiler=$CC +else + echo $ac_t no + echo '' + echo "A working C compiler is needed to compile the EnergyMech" + echo "exiting ..." + echo '' + exit 1 +fi + +# +# compiler flags +# + +echo $ac_n "checking C compiler flags ... "$ac_c + +# +# -g +# +if [ -z "$cc_g_opt" ]; then + cc_g_opt=no + if $CC -g -c -o $TESTO $TESTC 1> /dev/null 2> /dev/null && test -r $TESTO; then + cc_g_opt=yes + cc_g_flag=-g + echo $ac_n "-g "$ac_c + fi +fi +# +# GNU GCC +# +if [ -n "$cf_GNUCC" ]; then + # + # -Wall -Wshadow + # + if [ -r .use_warn -o -n "$cc_wall_opt" ]; then + if [ -z "$cc_wall_opt" -o -z "$cc_wshadow_opt" ]; then + if $CC -Wall -Wshadow -S -o - -xc /dev/null 1> /dev/null 2> /dev/null; then + cc_wall_flag="-Wall" + cc_wshadow_flag="-Wshadow" + echo $ac_n "-Wall -Wshadow "$ac_c + fi + fi + fi + if [ "$cc_wall_flag" = -Wall -a "$cf_GNUCC" = 33 ]; then + cc_fnostrictalias="-fno-strict-aliasing" + echo $ac_n "-fno-strict-aliasing "$ac_c + fi + # + # -march=i386/i486/i586 + # + test -z "$cc_arch_opt" && cc_arch_opt=yes + if [ "$cc_arch_opt" = yes ]; then + case _"$cf_GNUCC"_"$cf_MACH"_ in + _95_i486_ | _33_i486_ ) + if $CC -march=i486 -S -o - -xc /dev/null 1> /dev/null 2> /dev/null; then + cc_arch_flag="-march=i486" + echo $ac_n "-march=i486 "$ac_c + fi + ;; + _95_i[56789]86_ | _33_i[56789]86_ ) + if $CC -march=i586 -S -o - -xc /dev/null 1> /dev/null 2> /dev/null; then + cc_arch_flag="-march=i586" + echo $ac_n "-march=i586 "$ac_c + fi + ;; + esac + fi + # + # -fomit-frame-pointer + # + if [ -z "$cc_ofp_opt" -o "$cc_ofp_opt" = yes ]; then + if $CC -fomit-frame-pointer -c -o $TESTO $TESTC 1> /dev/null 2> /dev/null && test -r $TESTO; then + cc_ofp_flag=-fomit-frame-pointer + echo $ac_n "-fomit-frame-pointer "$ac_c + fi + fi +fi + +if [ -z "$cc_pipe_opt" ]; then + if $CC -pipe -c -o $TESTO $TESTC 1> /dev/null 2> /dev/null && test -r $TESTO; then + cc_pipe_flag="-pipe" + echo $ac_n "-pipe "$ac_c + fi +fi + +if [ -z "$cc_optimize_opt" ]; then + if $CC -O2 -o $TESTO $TESTC 1> /dev/null 2> /dev/null; then + cc_optimize_flag="-O2" + echo $ac_n "-O2 "$ac_c + fi + if [ -z "$cc_optimize_flag" ]; then + if $CC -O -o $TESTO $TESTC 1> /dev/null 2> /dev/null; then + cc_optimize_flag="-O" + echo $ac_n "-O "$ac_c + fi + fi +fi + +echo $ac_t "" + +if [ "$ft_prof" = yes ]; then + echo $ac_n "checking profiling support ... "$ac_c + if $CC -pg -o $TESTO $TESTC 1> /dev/null 2> /dev/null; then + cc_ofp_flag= + cc_pg_flag="-pg" + cc_pg_define="-D__profiling__" + libpgdl= + echo $ac_t yes + else + if $CC -pg -o $TESTO $TESTC -ldl 1> /dev/null 2> /dev/null; then + cc_ofp_flag= + cc_pg_flag="-pg" + cc_pg_define="-D__profiling__" + libpgdl="-ldl" + echo $ac_t "-ldl" + else + echo $ac_t no + fi + fi +fi + +rm -f $TESTO $TESTP + +# +# 32bit? 64bit? +# + +TESTC=config/ptr_size.c + +if [ -z "$ptr_size" ]; then + ptr_size=unknown + echo $ac_n "checking pointer size ... "$ac_c + $CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null + if [ -x $TESTP ]; then + ptr_size=`$TESTP` + fi + echo $ac_t "$ptr_size" +fi + +case _"$ptr_size"_ in + _8_ ) + PTSIZE_DEFINE32='#undef PTSIZE_32BIT' + PTSIZE_DEFINE64='#define PTSIZE_64BIT' + ;; + * ) + PTSIZE_DEFINE32='#define PTSIZE_32BIT' + PTSIZE_DEFINE64='#undef PTSIZE_64BIT' + ;; +esac + +rm -f $TESTP + +# +# +# + +has_unaligned=no +UNALIGNED_MEM='#undef UNALIGNED_MEM' +TESTC=config/unaligned.c + +echo $ac_n "checking if cpu can access unaligned memory ... "$ac_c + +$CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null +if [ "`$TESTP`" = yes ]; then + has_unaligned=yes + UNALIGNED_MEM='#define UNALIGNED_MEM' +fi +echo $ac_t "$has_unaligned" + +rm -f $TESTP + +# +# where is inet_addr() ? +# + +has_inet_addr=no +TESTC=config/inet_addr.c + +echo $ac_n "checking for inet_addr() ... "$ac_c + +$CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null +if [ -x $TESTP ]; then + has_inet_addr=yes +else + $CC -o $TESTP $TESTC -lnsl 1> /dev/null 2> /dev/null + if [ -x $TESTP ]; then + has_inet_addr=-lnsl + libnsl=-lnsl + fi +fi +echo $ac_t "$has_inet_addr" +rm -f $TESTP + +# +# where is inet_addr() ? +# + +has_inet_aton=no +TESTC=config/inet_aton.c + +echo $ac_n "checking for inet_aton() ... "$ac_c + +$CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null +if [ -x $TESTP ]; then + has_inet_aton=yes +else + $CC -o $TESTP $TESTC -lresolv 1> /dev/null 2> /dev/null + if [ -x $TESTP ]; then + has_inet_aton=-lresolv + libresolv=-lresolv + fi +fi +echo $ac_t "$has_inet_aton" +rm -f $TESTP + +# +# where is socket() ? +# + +has_socket=no +TESTC=config/socket.c + +echo $ac_n "checking for socket() ... "$ac_c + +$CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null +if [ -x $TESTP ]; then + has_socket=yes +else + $CC -o $TESTP $TESTC -lsocket 1> /dev/null 2> /dev/null + if [ -x $TESTP ]; then + has_socket=-lsocket + libsocket=-lsocket + fi +fi +echo $ac_t "$has_socket" +rm -f $TESTP + +# +# check for MD5 capabilities +# +has_md5=no +has_des=no +if [ ! "$ft_md5" = no ]; then + TESTC=config/pw.c + echo $ac_n "checking for crypt() ... "$ac_c + md5_internal= + crypt_func='-DCRYPT_FUNC=crypt' + CRYPT_FUNCTION='#define CRYPT_FUNC crypt' + if [ "$ft_md5" = internal ]; then + md5_internal=config/md5_internal.c + crypt_func='-DCRYPT_FUNC=md5_crypt' + CRYPT_FUNCTION='#define CRYPT_FUNC md5_crypt' + fi + $CC -o $TESTP $TESTC $md5_internal $crypt_func 1> /dev/null 2> /dev/null + if [ ! -x $TESTP ]; then + libcrypt=-lcrypt + has_md5=yes + $CC -o $TESTP $TESTC $crypt_func $libcrypt 1> /dev/null 2> /dev/null + fi + if [ ! -x $TESTP ]; then + libcrypt=/usr/lib/libcrypt.so + has_md5=yes + $CC -o $TESTP $TESTC $crypt_func $libcrypt 1> /dev/null 2> /dev/null + fi + if [ ! -x $TESTP ]; then + has_md5=no + libcrypt= + fi + if [ -x $TESTP ]; then + pwhash=`$TESTP` + case "$pwhash" in + MD5 ) has_md5=yes ;; + DES ) has_des=yes ;; + DESaMD5 ) + has_md5=yes + has_des=yes + ;; + esac + fi + echo $ac_t "$has_md5" + [ "$has_md5" = yes -a "$md5_internal" ] && has_md5=internal + rm -f $TESTP +fi + +# +# check for tcl +# +has_tcl=no +if [ ! "$ft_tcl" = no ]; then + echo $ac_n "checking for tcl ... "$ac_c + TESTC=config/tcl.c + libtcl= + for tclver in empty 8.9 8.8 8.7 8.6 8.5 8.4 8.3 8.2 8.1 8.0 + do + if [ -z "$libtcl" ]; then + test "$tclver" = "empty" && TCLLIB=-ltcl || TCLLIB=-ltcl$tclver + if $CC -o $TESTP $TESTC $TCLLIB 1> /dev/null 2> /dev/null; then + libtcl=$TCLLIB + has_tcl=$TCLLIB + test "$tclver" = "empty" && echo $ac_t "yes" || echo $ac_t "yes (version $tclver)" + fi + fi + done + if [ -z "$libtcl" ]; then + has_tcl=no + echo $ac_t "no" + fi + rm -f $TESTP +fi + +# +# check for python +# +has_python=no +if [ ! "$ft_python" = no ]; then + echo $ac_n "checking for python ... "$ac_c + TESTC=config/python.c + libpython= + incpython= + for pyver in 2.9 2.8 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0 + do + if [ -z "$libpython" ]; then + PYINCLUDE=python$pyver/Python.h + PYLIB=-lpython$pyver + if $CC -o $TESTP $TESTC -include $PYINCLUDE $PYLIB 1> /dev/null 2> /dev/null; then + libpython=$PYLIB + incpython="-include $PYINCLUDE" + has_python=yes + echo $ac_t "yes (version $pyver)" + fi + fi + done + if [ -z "$libpython" ]; then + has_python=no + echo $ac_t "no" + fi + rm -f $TESTP +fi + +# +# can we use our custom little ld script? +# +ldscript= +has_ldscript=no +TESTC=config/ldtest.c + +echo $ac_n "checking for friendly ld ... "$ac_c + +$CC -o $TESTP $TESTC -Wl,-T,src/ld/mech.ldscript 1> /dev/null 2> /dev/null +if [ -x $TESTP ]; then + if [ `$TESTP` = "yes" ]; then + ldscript='-Wl,-T,ld/mech.ldscript' + has_ldscript=yes + fi +fi +echo $ac_t "$has_ldscript" +rm -f $TESTP + +# +# idwrap +# +has_idwrap=no +def_idwrap='#undef IDWRAP' +IDWRAP_PATH='/* nothing */' + +if [ -r .use_idwrap -o "$ft_idwrap" = yes ]; then + echo $ac_n "checking for idwrap path ... "$ac_c + if test -z "$idwrappath"; then + idwrappath="/tmp/.ident/" + fi + if test -d $idwrappath; then + IDWRAP_PATH='"'"$idwrappath"'"' + has_idwrap="$IDWRAP_PATH" + def_idwrap='#define IDWRAP' + fi + echo $ac_t "$has_idwrap" +fi + +# +# +# +echo $ac_n "checking for objcopy ... "$ac_c +OBJCOPY=`config/which objcopy` +echo $ac_t "$OBJCOPY" +PROGSIZE= +sizecomment='#' +if [ -r .use_size ]; then + echo $ac_n "checking for size ... "$ac_c + PROGSIZE=`config/which size` + echo $ac_t "$PROGSIZE" + [ ! "$PROGSIZE" = "not found" ] && sizecomment= +fi + +objcomment= +[ "$OBJCOPY" = "not found" ] && objcomment='#' + +MD5_O= +if [ "$ft_md5" = internal ]; then + MD5_O=md5/md5.o +fi + +echo +echo "Do you want ..." +echo + +def_alias='#undef ALIAS' +unset ans +echo $ac_n "Alias support? ............................. [Y/n] "$ac_c +test "$ft_alias" && echo "$ft_alias" && ans=$ft_alias +test -z "$ft_alias" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_alias='#define ALIAS' + +def_toybox='#undef TOYBOX' +unset ans +echo $ac_n "Amusing misc commands (toybox)? ............ [Y/n] "$ac_c +test "$ft_toybox" && echo "$ft_toybox" && ans=$ft_toybox +test -z "$ft_toybox" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_toybox='#define TOYBOX' + +def_rawdns='#undef RAWDNS' +unset ans +echo $ac_n "Async DNS support? ......................... [Y/n] "$ac_c +test "$ft_rawdns" && echo "$ft_rawdns" && ans=$ft_rawdns +test -z "$ft_rawdns" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_rawdns='#define RAWDNS' + +def_botnet='#undef BOTNET' +unset ans +echo $ac_n "Botnet support? ............................ [Y/n] "$ac_c +test "$ft_botnet" && echo "$ft_botnet" && ans=$ft_botnet +test -z "$ft_botnet" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_botnet='#define BOTNET' + +def_chanban='#undef CHANBAN' +unset ans +echo $ac_n "Channel ban support? ....................... [Y/n] "$ac_c +test "$ft_chanban" && echo "$ft_chanban" && ans=$ft_chanban +test -z "$ft_chanban" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_chanban='#define CHANBAN' + +def_ctcp='#undef CTCP' +unset ans +echo $ac_n "CTCP? ...................................... [Y/n] "$ac_c +test "$ft_ctcp" && echo "$ft_ctcp" && ans=$ft_ctcp +test -z "$ft_ctcp" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_ctcp='#define CTCP' + +def_dccfile='#undef DCC_FILE' +unset ans +echo $ac_n "DCC file support? .......................... [Y/n] "$ac_c +test "$ft_dccfile" && echo "$ft_dccfile" && ans=$ft_dccfile +test -z "$ft_dccfile" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_dccfile='#define DCC_FILE' + +def_debug='#undef DEBUG' +unset ans +echo $ac_n "Debug support? ............................. [y/N] "$ac_c +test "$ft_debug" && echo "$ft_debug" && ans=$ft_debug +test -z "$ft_debug" && read ans +test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_debug='#define DEBUG' + +def_dynamode='#undef DYNAMODE' +unset ans +echo $ac_n "Dynamic channel limit (+l)? ................ [Y/n] "$ac_c +test "$ft_dynamode" && echo "$ft_dynamode" && ans=$ft_dynamode +test -z "$ft_dynamode" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_dynamode='#define DYNAMODE' + +def_dyncmd='#undef DYNCMD' +unset ans +echo $ac_n "Dynamic command levels support? ............ [Y/n] "$ac_c +test "$ft_dyncmd" && echo "$ft_dyncmd" && ans=$ft_dyncmd +test -z "$ft_dyncmd" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_dyncmd='#define DYNCMD' + +def_bounce='#undef BOUNCE' +unset ans +echo $ac_n "IRC proxy support? ......................... [Y/n] "$ac_c +test "$ft_bounce" && echo "$ft_bounce" && ans=$ft_bounce +test -z "$ft_bounce" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_bounce='#define BOUNCE' + +def_ircd_ext='#undef IRCD_EXTENSIONS' +unset ans +echo $ac_n "IRCD extensions support? ................... [Y/n] "$ac_c +test "$ft_ircd_ext" && echo "$ft_ircd_ext" && ans=$ft_ircd_ext +test -z "$ft_ircd_ext" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_ircd_ext='#define IRCD_EXTENSIONS' + +def_greet='#undef GREET' +unset ans +echo $ac_n "Greet support? ............................. [Y/n] "$ac_c +test "$ft_greet" && echo "$ft_greet" && ans=$ft_greet +test -z "$ft_greet" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_greet='#define GREET' + +def_web='#undef WEB' +unset ans +echo $ac_n "HTTP server support? ....................... [y/N] "$ac_c +test "$ft_web" && echo "$ft_web" && ans=$ft_web +test -z "$ft_web" && read ans +test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_web='#define WEB' + +def_md5='#undef MD5CRYPT' +unset ans +echo $ac_n "MD5 password support? ...................... [Y/n] "$ac_c +if [ "$has_md5" = no ]; then + echo 'no (unsupported)' +else + test "$ft_md5" && echo "$ft_md5" && ans=$ft_md5 + test -z "$ft_md5" && read ans + test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes -o "$ans" = internal && def_md5='#define MD5CRYPT' +fi + +def_newbie='#undef NEWBIE' +unset ans +echo $ac_n "Newbie support? ............................ [Y/n] "$ac_c +test "$ft_newbie" && echo "$ft_newbie" && ans=$ft_newbie +test -z "$ft_newbie" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_newbie='#define NEWBIE' + +def_note='#undef NOTE' +unset ans +echo $ac_n "Note support? .............................. [Y/n] "$ac_c +test "$ft_note" && echo "$ft_note" && ans=$ft_note +test -z "$ft_note" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_note='#define NOTE' + +def_notify='#undef NOTIFY' +unset ans +echo $ac_n "Notify support? ............................ [Y/n] "$ac_c +test "$ft_notify" && echo "$ft_notify" && ans=$ft_notify +test -z "$ft_notify" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_notify='#define NOTIFY' + +# +# perl support not yet functional (2009-05-11) +# +def_perl='#undef PERL' +# unset ans +# echo $ac_n "Perl scripting support? .................... [y/N] "$ac_c +# if [ "$has_perl" = no ]; then +# echo 'no (unsupported)' +# else +# test "$ft_perl" && echo "$ft_perl" && ans=$ft_perl +# test -z "$ft_perl" && read ans +# test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_perl='#define PERL' +# fi + +def_python='#undef PYTHON' +unset ans +echo $ac_n "Python scripting support? .................. [y/N] "$ac_c +if [ "$has_python" = no ]; then + echo 'no (unsupported)' +else + test "$ft_python" && echo "$ft_python" && ans=$ft_python + test -z "$ft_python" && read ans + test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_python='#define PYTHON' +fi +if [ "$def_python" = '#undef PYTHON' ]; then + libpython= + incpython= +fi + +def_redirect='#undef REDIRECT' +unset ans +echo $ac_n "Command output redirect? ................... [Y/n] "$ac_c +test "$ft_redirect" && echo "$ft_redirect" && ans=$ft_redirect +test -z "$ft_redirect" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_redirect='#define REDIRECT' + +def_seen='#undef SEEN' +unset ans +echo $ac_n "Seen support? .............................. [y/N] "$ac_c +test "$ft_seen" && echo "$ft_seen" && ans=$ft_seen +test -z "$ft_seen" && read ans +test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_seen='#define SEEN' + +def_session='#undef SESSION' +unset ans +echo $ac_n "Session support? ........................... [Y/n] "$ac_c +test "$ft_session" && echo "$ft_session" && ans=$ft_session +test -z "$ft_session" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_session='#define SESSION' + +def_stats='#undef STATS' +unset ans +echo $ac_n "Statistics support? ........................ [Y/n] "$ac_c +test "$ft_stats" && echo "$ft_stats" && ans=$ft_stats +test -z "$ft_stats" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_stats='#define STATS' + +def_tcl='#undef TCL' +unset ans +echo $ac_n "Tcl scripting support? ..................... [y/N] "$ac_c +if [ "$has_tcl" = no ]; then + echo 'no (unsupported)' +else + test "$ft_tcl" && echo "$ft_tcl" && ans=$ft_tcl + test -z "$ft_tcl" && read ans + test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_tcl='#define TCL' +fi +test "$def_tcl" = '#undef TCL' && libtcl='' + +def_telnet='#undef TELNET' +unset ans +echo $ac_n "Telnet support? ............................ [Y/n] "$ac_c +test "$ft_telnet" && echo "$ft_telnet" && ans=$ft_telnet +test -z "$ft_telnet" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_telnet='#define TELNET' + +def_trivia='#undef TRIVIA' +unset ans +echo $ac_n "Trivia support? ............................ [y/N] "$ac_c +test "$ft_trivia" && echo "$ft_trivia" && ans=$ft_trivia +test -z "$ft_trivia" && read ans +test "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_trivia='#define TRIVIA' + +def_uptime='#undef UPTIME' +unset ans +echo $ac_n "Uptime support? ............................ [Y/n] "$ac_c +test "$ft_uptime" && echo "$ft_uptime" && ans=$ft_uptime +test -z "$ft_uptime" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_uptime='#define UPTIME' + +def_wingate='#undef WINGATE' +unset ans +echo $ac_n "WinGate support? ........................... [Y/n] "$ac_c +test "$ft_wingate" && echo "$ft_wingate" && ans=$ft_wingate +test -z "$ft_wingate" && read ans +test -z "$ans" -o "$ans" = y -o "$ans" = Y -o "$ans" = yes -o "$ans" = YES -o "$ans" = Yes && def_wingate='#define WINGATE' + +echo '' +echo "Creating src/Makefile" + +O_FLAGS="$cc_optimize_flag $cc_arch_flag $cc_march_flag $cc_ofp_flag" +W_FLAGS="$cc_wall_flag $cc_wshadow_flag $cc_fnostrictalias" +libflags="$libcrypt $libtcl $libnsl $libsocket $libresolv $libpython" +lflags="$cc_g_flag -o" +cprof="$cc_pg_flag $cc_pg_define" +lprof="$cc_pg_flag $cc_pg_define $libpgdl" + +echo '# This file is generated from Makefile.in' > src/Makefile +sed " +s%@CC@%$CCshort%; +s%@ldscript@%$ldscript%; +s%@pipeflag@%$cc_pipe_flag%; +s%@gdbflag@%$cc_g_flag%; +/@cprof@/ { s,@cprof@,$cprof,; }; +/@lprof@/ { s,@lprof@,$lprof,; }; +/@lflags@/ { s,@lflags@,$lflags,; }; +/@libflags@/ { s,@libflags@,$libflags,; }; +/@W_FLAGS@/ { s/@W_FLAGS@/$W_FLAGS/; }; +/@O_FLAGS@/ { s/@O_FLAGS@/$O_FLAGS/; }; +/@PYINCLUDE@/ { s,@PYINCLUDE@,$incpython,; }; +s|@MD5_O@|$MD5_O|; +s%@oc@%$objcomment%; +s%@sz@%$sizecomment%; +s/[ ]*\$//g; +" < src/Makefile.in >> src/Makefile || exit 1 + +echo "Creating src/config.h" + +CRYPT_HAS_MD5='#undef CRYPT_HAS_MD5' +CRYPT_HAS_DES='#undef CRYPT_HAS_DES' +[ "$has_md5" = yes ] && CRYPT_HAS_MD5='#define CRYPT_HAS_MD5' +[ "$has_des" = yes ] && CRYPT_HAS_DES='#define CRYPT_HAS_DES' + +echo '/* This file is generated from config.h.in */' > src/config.h +sed " + s|@DEF_ALIAS@|$def_alias|; + s|@DEF_BOTNET@|$def_botnet|; + s|@DEF_BOUNCE@|$def_bounce|; + s|@DEF_CHANBAN@|$def_chanban|; + s|@DEF_CTCP@|$def_ctcp|; + s|@DEF_DCCFILE@|$def_dccfile|; + s|@DEF_DEBUG@|$def_debug|; + s|@DEF_DYNCMD@|$def_dyncmd|; +s|@DEF_DYNAMODE@|$def_dynamode|; + s|@DEF_GREET@|$def_greet|; + s|@DEF_IDWRAP@|$def_idwrap|; +s|@DEF_IRCD_EXT@|$def_ircd_ext|; + s|@DEF_MD5@|$def_md5|; + s|@DEF_NEWBIE@|$def_newbie|; + s|@DEF_NOTE@|$def_note|; + s|@DEF_NOTIFY@|$def_notify|; + s|@DEF_PERL@|$def_perl|; + s|@DEF_PYTHON@|$def_python|; + s|@DEF_RAWDNS@|$def_rawdns|; +s|@DEF_REDIRECT@|$def_redirect|; + s|@DEF_SEEN@|$def_seen|; + s|@DEF_SESSION@|$def_session|; + s|@DEF_STATS@|$def_stats|; + s|@DEF_TCL@|$def_tcl|; + s|@DEF_TELNET@|$def_telnet|; + s|@DEF_TOYBOX@|$def_toybox|; + s|@DEF_TRIVIA@|$def_trivia|; + s|@DEF_UPTIME@|$def_uptime|; + s|@DEF_WEB@|$def_web|; + s|@DEF_WINGATE@|$def_wingate|; + +s|@DEF_CRYPT_FUNCTION@|$CRYPT_FUNCTION|; +s|@CRYPT_HAS_MD5@|$CRYPT_HAS_MD5|; +s|@CRYPT_HAS_DES@|$CRYPT_HAS_DES|; +s|@IDWRAP_PATH@|$IDWRAP_PATH|; +s|@PTSIZE_DEFINE32@|$PTSIZE_DEFINE32|; +s|@PTSIZE_DEFINE64@|$PTSIZE_DEFINE64|; +s|@UNALIGNED_MEM@|$UNALIGNED_MEM|; +" < src/config.h.in >> src/config.h + +echo '' +echo 'All done. You can now "make clean install"' +echo '' +echo 'Send your bugreports to ' +echo '' +echo 'You have read the README file I hope?' +echo '' diff --git a/default.bigchars b/default.bigchars new file mode 100644 index 0000000..bd796d5 --- /dev/null +++ b/default.bigchars @@ -0,0 +1,254 @@ +fontname energymech-capitals-icy-height8-v3 +spacewidth 4 +charheight 8 +kerning 1 + +chars ! +,--, +| | +| | +| | +| | + \/ +,--, +\__/ + +chars . + + + + + + +,--, +\__/ + +chars | +,--. +| | +| | +| | +| | +| | +| | +`--' + +chars aA + ,-----, + / ,---,\ +| |___| | +| ,--'| | +| | | | +| | | | +| ,-` `-' +|/ + +chars bB +,------, +| ,-. \ +| |_/ ; +| ,-. \ +| | \ | +| `--' | +| / +`/~~~~~~' + +chars cC + ,-----; + / ,---' +| | +| | +| | ,-, +| `--' | +`\ | + `~~~`\/ + +chars dD + ,----, +| ,, \ +| | \ \ +| | | | +| | | | +| `--' | +| | +`/~~~~~~' + +chars eE + ,-----; + / ,---' +| |___ +| ,--' +| |__,-, +| | +| | +`~~~~~`\/ + +chars fF + ,-----; + / ,---' +| |__ +| ,--' +| | +| | +| | +`/~~' + +chars gG + ,----, + / ,-. \ +| | `~' +| | ,--, +| | \ | +| `~' | +| | +`/~~~~~~' + +chars hH + ,--. ,---. + | | | | + | |_| | +,' ,-. | +| | | | +| | | | +| ,-` | | +|/ `~~~' + +chars iI + ,--, + | | +,' | +| | +| | +| | +| | +`/~~' + +chars jJ + ,--, + | | + | | + | `. +/~\__| | +\ | + \ | + `~~~~~~´ + +chars kK +,---, ,-. +| | / / +| |/ / +| ,-. ~\ +| | | | +| | | | +| | | / +`---' `/ + +chars lL +,---, +| | +| | +| | +| |_,--, +| | +| | +`~~~~~~`/ + +chars mM +,---, ,---, +| \ / | +| \/ | +| |\ /| | +| | \/ | | +| | | | +| | | | +`---' `---' + +chars nN +,---, ,---, +| \ | | +| \ | | +| |\ \| | +| | \ | +| | \ | +| | | | +`---' `---' + +chars oO + ,------, + / ,--, | +| | | | +| | | | +| | | | +| `--' | +| / +`/~~~~~' + +chars pP +,-------, +| ,--, \ +| |__| ; +| |----' +| | +| | +| | +`---' + +chars rR +,-------, +| ,--, \ +| |__| ; +| |--; < +| | | | +| | | | +| | | / +`---' `/ + +chars sS + ,-------, + / ,----' +| `---, +`-------, \ +,---, | | +| `---' | +| | +`----------' + +chars tT +,-------, +\____, \ + | | + | | + | | + | | + | | + `/~~' + +chars uU + ,-. ,-, + / | | | +| | | | +| | | | +| | | | +| `--' | +`\ | + `~~~~\/ + +chars wW + ,-. ,-, + / | | | +| | |\ | | +| | | | | | +| | | | | | +| `-' `-' | +`\ /\ | + `~~' `~\/ + +chars yY + , ,-. + /| | \ + / | | | +/ `-' | +\___. | + \ | + | | + |,--' diff --git a/help/8BALL b/help/8BALL new file mode 100644 index 0000000..cf7e7f8 --- /dev/null +++ b/help/8BALL @@ -0,0 +1,5 @@ + +8Ball command randomly gives a positive or negative answer. +The ! char is also recognized as commandchar. + +Example: !8ball will I get laid tonight? diff --git a/help/ACCESS b/help/ACCESS new file mode 100644 index 0000000..b2097de --- /dev/null +++ b/help/ACCESS @@ -0,0 +1,10 @@ + +Show someones access level. If no arguments are given, the bot +will display your own access level. You may also query the bot +for the access level required for commands, using: +ACCESS + +For example: ACCESS -ACCESS +(If your command char is -) + +See also: userlist diff --git a/help/ALIAS b/help/ALIAS new file mode 100644 index 0000000..6e5b1ae --- /dev/null +++ b/help/ALIAS @@ -0,0 +1,21 @@ + +Create or delete a command alias. Arguments like $1 will +be replaced with the corresponding argument from input. + +$1 The first argument. +$1-2 The first and second argument. +$2-3 The second and third argument. +$1- All arguments starting with the first. +$2- All arguments starting with the second. + +Example: +ALIAS MEEP SAY $2 $1 $3- + +Would be translated in the following manner: +MEEP one two three four -> SAY two one three four + +Aliases may call other aliases and aliases can be used +to replace built in commands. Aliases can recurse a +maximum of 20 times (prevents infinite loops). + +See also: unalias diff --git a/help/AWAY b/help/AWAY new file mode 100644 index 0000000..65ec84c --- /dev/null +++ b/help/AWAY @@ -0,0 +1,3 @@ + +Sets the bot away. If no message is specified, previous +away status and message (if any) is removed. diff --git a/help/BAN b/help/BAN new file mode 100644 index 0000000..0e3dcf8 --- /dev/null +++ b/help/BAN @@ -0,0 +1,5 @@ + +Ban a user on a channel. If a mask is given, a ban using +the mask will be placed. The user will not be kicked. + +See also: unban, siteban, kb, screw diff --git a/help/BANLIST b/help/BANLIST new file mode 100644 index 0000000..9a26f63 --- /dev/null +++ b/help/BANLIST @@ -0,0 +1,4 @@ + +Show the banlist for a channel. + +See also: ban, unban diff --git a/help/BYE b/help/BYE new file mode 100644 index 0000000..2af1719 --- /dev/null +++ b/help/BYE @@ -0,0 +1,4 @@ + +End your own DCC or telnet connection. + +See also: chat diff --git a/help/CCHAN b/help/CCHAN new file mode 100644 index 0000000..6082576 --- /dev/null +++ b/help/CCHAN @@ -0,0 +1,5 @@ + +Set the current channel. With no argument, +the currently set channel is displayed. + +See also: join, part, channels diff --git a/help/CHACCESS b/help/CHACCESS new file mode 100644 index 0000000..4c82c71 --- /dev/null +++ b/help/CHACCESS @@ -0,0 +1,6 @@ + +Changes the level needed to do a command. + +If "disable" is used instead of the level, +the command will be permanently disabled +(for as long as the bot remains). diff --git a/help/CHANBAN b/help/CHANBAN new file mode 100644 index 0000000..e69de29 diff --git a/help/CHANNELS b/help/CHANNELS new file mode 100644 index 0000000..865c3d2 --- /dev/null +++ b/help/CHANNELS @@ -0,0 +1,4 @@ + +Lists the channels the bot is active on. + +See also: join, part, forget diff --git a/help/CHAT b/help/CHAT new file mode 100644 index 0000000..69af186 --- /dev/null +++ b/help/CHAT @@ -0,0 +1,4 @@ + +Make the bot DCC chat you. + +See also: bye diff --git a/help/CLEARSHIT b/help/CLEARSHIT new file mode 100644 index 0000000..0b8c997 --- /dev/null +++ b/help/CLEARSHIT @@ -0,0 +1,4 @@ + +Clears the shitlist, removing all shit records. + +See also: shit, rshit diff --git a/help/CMD b/help/CMD new file mode 100644 index 0000000..dee1615 --- /dev/null +++ b/help/CMD @@ -0,0 +1,6 @@ + +Send a command to linked bots. You can specify a single +bot to execute the command. Authentication is done on +target bot(s), not the bot you send the command from. + +See also: link diff --git a/help/CORE b/help/CORE new file mode 100644 index 0000000..2d87987 --- /dev/null +++ b/help/CORE @@ -0,0 +1,4 @@ + +Shows core information about the bot. + +See also: ver, uptime, ontime diff --git a/help/CSERV b/help/CSERV new file mode 100644 index 0000000..023eaa1 --- /dev/null +++ b/help/CSERV @@ -0,0 +1,4 @@ + +Shows current server the bot is connected to. + +See also: server, nextserver diff --git a/help/CTCP b/help/CTCP new file mode 100644 index 0000000..b9b828d --- /dev/null +++ b/help/CTCP @@ -0,0 +1,5 @@ + +Send a CTCP request to a user or channel. +The reply is displayed in the "STATUS" spy channel. + +See also: spy diff --git a/help/CYCLE b/help/CYCLE new file mode 100644 index 0000000..a8ee8cb --- /dev/null +++ b/help/CYCLE @@ -0,0 +1,4 @@ + +Quickly part and rejoin a channel. + +See also: join, part diff --git a/help/DEOP b/help/DEOP new file mode 100644 index 0000000..d2c2d2c --- /dev/null +++ b/help/DEOP @@ -0,0 +1,6 @@ + +Deop a user or users matching the given mask on +a certain channel. With a mask, only users with +access below the MAL setting are affected. + +See also: down, op, mode diff --git a/help/DIE b/help/DIE new file mode 100644 index 0000000..fc5958c --- /dev/null +++ b/help/DIE @@ -0,0 +1,7 @@ + +Kills the bot. If session file is in use, use the +SHUTDOWN command instead to preserve the configurations +for all bots. If DIE is used, the killed bot(s) will +not be saved to the session file. + +See also: reset, shutdown diff --git a/help/DNS b/help/DNS new file mode 100644 index 0000000..7df9e58 --- /dev/null +++ b/help/DNS @@ -0,0 +1,3 @@ + +Resolve the hostname given or the host of the nick give. +If the hostname is an IP, a reverse resolve will be attempted. diff --git a/help/DNSSERVER b/help/DNSSERVER new file mode 100644 index 0000000..d730e28 --- /dev/null +++ b/help/DNSSERVER @@ -0,0 +1,8 @@ + +Add or remove DNS servers from the internal list. ++ip to add a new host, -ip to remove an existing host. +Only IP's can be used, hostnames will not work +(the resolver cannot resolve itself). With no arguments +a list of the current known DNS resolver addresses are +shown, the next host to be used is marked with underline. +Use `dnsserver -0' to remove all known hosts. diff --git a/help/DO b/help/DO new file mode 100644 index 0000000..192a8ff --- /dev/null +++ b/help/DO @@ -0,0 +1,4 @@ + +Sends raw commands to the server. + +Example: DO PRIVMSG #eggdrop :yer all lame, except guppy =) diff --git a/help/DOWN b/help/DOWN new file mode 100644 index 0000000..9c46aca --- /dev/null +++ b/help/DOWN @@ -0,0 +1,4 @@ + +Deop you on a channel. + +See also: deop, up, mode diff --git a/help/ECHO b/help/ECHO new file mode 100644 index 0000000..acdd07b --- /dev/null +++ b/help/ECHO @@ -0,0 +1,4 @@ + +Turns on and off partyline echoing of your own messages. + +See also: chat, user diff --git a/help/ESAY b/help/ESAY new file mode 100644 index 0000000..050b4fa --- /dev/null +++ b/help/ESAY @@ -0,0 +1,13 @@ + +Works like SAY with some enhancements. As the bot parses the text +it replaces predefined variables with their current value: + $access Your own access on the current channel + $cc Current channel + $channels Channels + $on Ontime (same as ontime command) + $time Current time + $var(x) Show current setting for "x" + $up Uptime (same as uptime command) + $ver EnergyMech version + +See also: say diff --git a/help/FORGET b/help/FORGET new file mode 100644 index 0000000..61f9dfd --- /dev/null +++ b/help/FORGET @@ -0,0 +1,4 @@ + +Removes an inactive channel from memory. + +See also: join, part diff --git a/help/GREET b/help/GREET new file mode 100644 index 0000000..801efb2 --- /dev/null +++ b/help/GREET @@ -0,0 +1,9 @@ + +When a matching user joins a channel, send a text as a greeting +to the channel or the user. Normal greetings and random line +greetings are sent to the channel joined as a message. All +greetings are sent line buffered. + +greeting Send "greeting". +@greetfile Send the entire contents of greetfile as private NOTICEs. +%greetfile Pick a random line from greetfile as greeting. diff --git a/help/HELP b/help/HELP new file mode 100644 index 0000000..3059995 --- /dev/null +++ b/help/HELP @@ -0,0 +1,12 @@ + +Online help system. + + Shows help about a specific topic. + Displays help entry for specified command. + Displays all commands available at a specified level. + Shows all help entries matching the pattern. + +If no argument is given, all available commands available are +listed grouped by access level. + +See also: usage diff --git a/help/IDLE b/help/IDLE new file mode 100644 index 0000000..e44e055 --- /dev/null +++ b/help/IDLE @@ -0,0 +1,4 @@ + +Show how long a person has been idle. + +See also: showidle diff --git a/help/INSULT b/help/INSULT new file mode 100644 index 0000000..04c62c8 --- /dev/null +++ b/help/INSULT @@ -0,0 +1,4 @@ + +Send a random insult to a user or channel. + +See also: pickup diff --git a/help/INVITE b/help/INVITE new file mode 100644 index 0000000..aee6945 --- /dev/null +++ b/help/INVITE @@ -0,0 +1,3 @@ + +Invite someone to a certain channel. If no nick is given, +it defaults to inviting you. diff --git a/help/JOIN b/help/JOIN new file mode 100644 index 0000000..26bbb7d --- /dev/null +++ b/help/JOIN @@ -0,0 +1,4 @@ + +Makes the bot join a channel + +See also: cycle, part diff --git a/help/KB b/help/KB new file mode 100644 index 0000000..fe81bd8 --- /dev/null +++ b/help/KB @@ -0,0 +1,5 @@ + +Kickban a user from a channel. If no reason is given, +a random kick message will be used. + +See also: ban, kick, screw, shit diff --git a/help/KICK b/help/KICK new file mode 100644 index 0000000..62bab3a --- /dev/null +++ b/help/KICK @@ -0,0 +1,5 @@ + +Kick a user from a channel. If no reason is given, +a random kick message will be used. + +See also: kb, screw, shit diff --git a/help/KS b/help/KS new file mode 100644 index 0000000..4239dbb --- /dev/null +++ b/help/KS @@ -0,0 +1,12 @@ + +Add a kicksay. The bot kicks users when they say the words. +The optinal action is a number from 0 to 3; + 0 on first offence, send the user a warning, + on second offence, kick them. + 1 kick the user. + 2 kickban the user. + 3 kickban and shitlist the user. + +If no arguments are given, the current kicksays will be listed. + +See also: rks diff --git a/help/LAST b/help/LAST new file mode 100644 index 0000000..b69a8be --- /dev/null +++ b/help/LAST @@ -0,0 +1,3 @@ + +Shows the last commands done, and who did them +Default is 5, showing up to 20. diff --git a/help/LEVELS b/help/LEVELS new file mode 100644 index 0000000..7507442 --- /dev/null +++ b/help/LEVELS @@ -0,0 +1,34 @@ +Built in levels +Level 0: Anonymous user. + User is ignored by the bot. +Level 10: Partyline user. + Users with this level can verify themselves and communicate at + a basic level (help, usage, echo, ...) with the bot including + using the DCC partyline. +Level 20: Informed user. + User is allowed to query a few status informations. +Level 40: Channel operator. + User can op/deop/ban/unban other channel users and set topics. +Level 50: Senior Channel operator. + User can manipulate the shitlist. +Level 60: Channel Maintainer. + User can view the bot userlist. +Level 70: Bot operator. + User can add/remove channels and manipulate the bad words list. +Level 80: Bot master. + User may change protected topics, enforced modes, is not checked + for massmodes/masskicks/clones/revenge kick, can remove passwords + with SETPASS, always gets ctcp ping replies and is not affected + by PROT levels of other users. User may force bot to load/save + setting to/from files. +Level 90: Trusted bot master. + User may change the bot's nick, spy on channels/messages, change + channel settings and commandlevels. User is allowed to add/remove + links to other bots. +Level 100: Superuser. + Superuser, what can he *not* do? +Level 200: Bot. + Bots can not execute commands by /msg or in any other way. + Is also unaffected by massmodes/kicks/clones/revenge, etc... + Only bots added with botlevel (200) will be autoopped as + a responce to NEEDOP across network links with other bots. diff --git a/help/LINK b/help/LINK new file mode 100644 index 0000000..ffa6fc1 --- /dev/null +++ b/help/LINK @@ -0,0 +1,18 @@ + +Add/remove and establish botnet links. + +Add a new bot to the list: + LINK +guid password [host port] +Host and port is optional, required for making connections to +the remote bot, but if they are left out, the local bot can still +accept links incoming from the remote bot. + +Remove a bot from the list: + LINK -guid + +Attempt to establish a link: + LINK guid + +With no arguments it will list all known bots. + +See also: cmd, setautolink diff --git a/help/LOAD b/help/LOAD new file mode 100644 index 0000000..62d28db --- /dev/null +++ b/help/LOAD @@ -0,0 +1,4 @@ + +Loads everything that can be loaded + +See also: save diff --git a/help/LUSERS b/help/LUSERS new file mode 100644 index 0000000..76a5315 --- /dev/null +++ b/help/LUSERS @@ -0,0 +1,4 @@ + +Same as the irc command /lusers + +See also: stats diff --git a/help/ME b/help/ME new file mode 100644 index 0000000..3a58a02 --- /dev/null +++ b/help/ME @@ -0,0 +1,4 @@ + +Make the bot do an action on a certain channel + +See also: say, esay, msg diff --git a/help/MODE b/help/MODE new file mode 100644 index 0000000..a9c7bcf --- /dev/null +++ b/help/MODE @@ -0,0 +1,5 @@ + +Set or unset channel modes. Can also be used +to change the modes of the bot. + +See also: op, deop, voice, unvoice diff --git a/help/MSG b/help/MSG new file mode 100644 index 0000000..76f8383 --- /dev/null +++ b/help/MSG @@ -0,0 +1,3 @@ +Usage: MSG + Send a message to a person or channel. + See also: SAY, ESAY, ME diff --git a/help/NAMES b/help/NAMES new file mode 100644 index 0000000..d46b171 --- /dev/null +++ b/help/NAMES @@ -0,0 +1,3 @@ +Usage: NAMES [channel] + Shows names on the specified channel + See also: WHO diff --git a/help/NEXTSERVER b/help/NEXTSERVER new file mode 100644 index 0000000..ca8a30d --- /dev/null +++ b/help/NEXTSERVER @@ -0,0 +1,5 @@ +Usage: NEXTSERVER + Makes the bot go to the next server in the serverlist + You can also do 'kill -USR1 ' in the shell + to make it switch server. + See also: SERVER, ADDSERVER, SERVERLIST diff --git a/help/NICK b/help/NICK new file mode 100644 index 0000000..907ad38 --- /dev/null +++ b/help/NICK @@ -0,0 +1,4 @@ +Usage: NICK + Changes the nick of the bot. If the nick given is already + in use, the bot will not change its nickname until the + occupied nick is available. diff --git a/help/NOTIFY b/help/NOTIFY new file mode 100644 index 0000000..0c1adce --- /dev/null +++ b/help/NOTIFY @@ -0,0 +1,10 @@ + +Show people online in the notify list. + +Options can be specified as follows: + +-all Show all nicks, online aswell as offline. +-nomatch Show online nicks even if they do not match the known mask(s). +-reload Reload the notify configuration file. +-full Show full details of each nick. +-seen Also show nicks that have been seen even if they are currently not online. diff --git a/help/ONTIME b/help/ONTIME new file mode 100644 index 0000000..0296944 --- /dev/null +++ b/help/ONTIME @@ -0,0 +1,3 @@ +Usage: ONTIME + Shows time connected to current server. + See also: UPTIME diff --git a/help/OP b/help/OP new file mode 100644 index 0000000..d9959ee --- /dev/null +++ b/help/OP @@ -0,0 +1,4 @@ +Usage: OP [channel] [nick|mask] + Op someone on a given channel. Defaults to opping + you in the channel where the command is given. + See also: UP, DEOP, MODE diff --git a/help/PART b/help/PART new file mode 100644 index 0000000..c58e41f --- /dev/null +++ b/help/PART @@ -0,0 +1,6 @@ + +Makes the bot part a certain channel. To purge the channel +completely from memory, use the FORGET command once it has +parted. + +See also: join, forget diff --git a/help/PASSWD b/help/PASSWD new file mode 100644 index 0000000..63d2926 --- /dev/null +++ b/help/PASSWD @@ -0,0 +1,6 @@ +Usage: PASSWD [oldpassword] + Changes your password on the bot. The oldpassword parameter only + has to be given if a password is already set. + Note: Passwords are case-sensitive and are encrypted so not even + bot runners will know your password. + See also: SETPASS diff --git a/help/PROTECTION b/help/PROTECTION new file mode 100644 index 0000000..55e85d0 --- /dev/null +++ b/help/PROTECTION @@ -0,0 +1,19 @@ + +Protection level can be 0 through 4 where the specific levels +are as follows: + + 0 No protection. + 1 Reop/unban, do nothing to offender. + 2 Reop/unban, deop offender. + 3 Reop/unban, kick offender. + 4 Reop/unban, kickban offender. + +The channel protection level limits the highest protection +level that will be enforced. For example, if channel protection +level is set to 2 and a user has protection level 4, that user +will still be treated as having only level 2 on that channel. + +If channel protection level is set to 0 (zero), protection +will not be enforced at all. + +See also: setprot, user, userlist diff --git a/help/PYTHON b/help/PYTHON new file mode 100644 index 0000000..387cf57 --- /dev/null +++ b/help/PYTHON @@ -0,0 +1,6 @@ + +Execute a Python command. This command is only available if +the PLEASE_HACK_MY_SHELL option was enabled during the +compiling. It is an extremely risky command to allow. + +============ CONSIDER YOURSELF WARNED ============ diff --git a/help/PYTHONSCRIPT b/help/PYTHONSCRIPT new file mode 100644 index 0000000..78c7035 --- /dev/null +++ b/help/PYTHONSCRIPT @@ -0,0 +1,2 @@ + +Load a Python script into the energymech. diff --git a/help/QSHIT b/help/QSHIT new file mode 100644 index 0000000..c3aac72 --- /dev/null +++ b/help/QSHIT @@ -0,0 +1,3 @@ +Usage: QSHIT [reason] + Quick shit. Uses preset defaults for channel and shitlevel. + See also: SHIT, RSHIT, SHITLIST diff --git a/help/RESET b/help/RESET new file mode 100644 index 0000000..48cddb6 --- /dev/null +++ b/help/RESET @@ -0,0 +1,6 @@ + +Restarts the bot completely. Same as killing the bot +and restarting it from the shell. The bot will attempt +to keep network connections alive (IRC server, DCC). + +See also: die, shutdown diff --git a/help/RKS b/help/RKS new file mode 100644 index 0000000..0308979 --- /dev/null +++ b/help/RKS @@ -0,0 +1,3 @@ +Usage: RKS + Removes a kicksay + See also: KS, KSLIST diff --git a/help/RSHIT b/help/RSHIT new file mode 100644 index 0000000..b7d926a --- /dev/null +++ b/help/RSHIT @@ -0,0 +1,3 @@ +Usage: RSHIT + Removes someone from the shitlist + See also: SHIT, SHITLIST diff --git a/help/RSPY b/help/RSPY new file mode 100644 index 0000000..01e72aa --- /dev/null +++ b/help/RSPY @@ -0,0 +1,5 @@ +Usage: RSPY [nick|channel] + Stop spying on a certain channel + Note: If a nick or channel is given at the end...that person/channel + will be removed from spying + See also: SPY, SPYMSG, SPYLIST diff --git a/help/RT b/help/RT new file mode 100644 index 0000000..1d388aa --- /dev/null +++ b/help/RT @@ -0,0 +1,3 @@ +Usage: RT + Sets a random topic. + See also: TOPIC diff --git a/help/SAVE b/help/SAVE new file mode 100644 index 0000000..5301ea8 --- /dev/null +++ b/help/SAVE @@ -0,0 +1,4 @@ + +Saves the entire enchilada. + +See also: load diff --git a/help/SAY b/help/SAY new file mode 100644 index 0000000..14dc9a0 --- /dev/null +++ b/help/SAY @@ -0,0 +1,3 @@ +Usage: SAY + Send a message to a channel. + See also: ME, ESAY, MSG diff --git a/help/SCREW b/help/SCREW new file mode 100644 index 0000000..336042d --- /dev/null +++ b/help/SCREW @@ -0,0 +1,3 @@ +Usage: SCREW [channel] [reason] + Kickban a user on a channel and place two bans using + randomized masks. diff --git a/help/SEEN b/help/SEEN new file mode 100644 index 0000000..66e808d --- /dev/null +++ b/help/SEEN @@ -0,0 +1,2 @@ +Usage: SEEN + Information on when someone was last seen by the bot. diff --git a/help/SERVER b/help/SERVER new file mode 100644 index 0000000..9726e12 --- /dev/null +++ b/help/SERVER @@ -0,0 +1,7 @@ + +Add or remove a server. If +/- is left out, the bot will +also attempt to connect to the server. If no argument +is provided, the list of known servers is displayed. +Added servers will be put into the current servergroup. + +See also: cserv, nextserver, servergroup diff --git a/help/SERVERGROUP b/help/SERVERGROUP new file mode 100644 index 0000000..505ed75 --- /dev/null +++ b/help/SERVERGROUP @@ -0,0 +1,13 @@ + +Add or remove a servergroup. If no argument is given, a list +of the servergroup identifiers will be displayed including the +current one. If the identifier already exists it will make it +the currently active one. Servers that are added, are added to +the current servergroup. At startup the default servergroup +is "default". The "default" servergroup cannot be removed. +By setting the SERVERGROUP setting on a bot, that bot will only +connect to servers with that same SERVERGROUP. If a bot has no +SERVERGROUP setting, it will connect to any server regardless +of which servergroup the server belongs to. + +See also: server, setservergroup diff --git a/help/SET b/help/SET new file mode 100644 index 0000000..e5f947a --- /dev/null +++ b/help/SET @@ -0,0 +1,24 @@ + +Change a setting. For channel settings you can use * to change +a setting for all known channels. + +If no setting and/or channel is specified, show all current +settings affecting the current/selected channel and bot. + +For more help on each setting displayed, use "HELP SETxxx" +where the "xxx" is the setting in question. + +Channel settings: +ABK, AOP, AUB, AVOICE, CHANBAN, CKL, CTL, DYNLIMIT, ENFM, +ENFMODES, FL, FPL, IKT, KS, MAL, MBL, MDL, MKL, MPL, NCL, +PROT, PUB, RK, SD, SHIT, SO, STATS, TOP + +Bot settings: +AAWAY, ALTNICK, CC, CMDCHAR, CTCP, DCCANON, DCCFILES, DCCUSER, +ENFPASS, IDENT, IRCNAME, ISONDELAY, MODES, NETUSERS, NOIDLE, +NOTIFYFILE, ONOTICE, RF, RV, SERVERGROUP, SPY, UMODES, +USERFILE, VIRTUAL, WINGATE, WINGPORT + +Process settings: +AUTOLINK, BNCPORT, CTIMEOUT, LINKPASS, LINKPORT, QCHAR, QDELAY, +QFILE, SEENFILE, UPHOST, UPNICK, UPPORT, WEBPORT diff --git a/help/SETAAWAY b/help/SETAAWAY new file mode 100644 index 0000000..99c9b76 --- /dev/null +++ b/help/SETAAWAY @@ -0,0 +1,6 @@ +Usage: SET AAWAY + +Setting for how long in minutes the bot waits idle before setting +AWAY status and a random away message. Any privmsg sent by the bot +resets the idle-timer to zero. +To disable auto-away set a value of 0. diff --git a/help/SETAOP b/help/SETAOP new file mode 100644 index 0000000..3e1c4ca --- /dev/null +++ b/help/SETAOP @@ -0,0 +1,3 @@ +Usage: TOG [channel] AOP [0|1|on|off] + +Toggles the auto-opping of users if verified. diff --git a/help/SETAUB b/help/SETAUB new file mode 100644 index 0000000..75de660 --- /dev/null +++ b/help/SETAUB @@ -0,0 +1,4 @@ +Usage: SET [channel] AUB + +Erases old bans automatically, this sets how many minutes old they have to be. +To disable auto-unbanning set a value of 0. diff --git a/help/SETAVOICE b/help/SETAVOICE new file mode 100644 index 0000000..d17b826 --- /dev/null +++ b/help/SETAVOICE @@ -0,0 +1,6 @@ +Usage: SET [channel] AVOICE + +Sets the autovoice level for a channel. + 0 - disable autto-voice for everyone + 1 - voice +AV users as they join + 2 - voice everyone as they join diff --git a/help/SETCC b/help/SETCC new file mode 100644 index 0000000..9d1c166 --- /dev/null +++ b/help/SETCC @@ -0,0 +1,2 @@ +Usage: TOG CC [0|1|on|off] + Toggles the necessity of the command character for doing commands. diff --git a/help/SETCHANBAN b/help/SETCHANBAN new file mode 100644 index 0000000..9729381 --- /dev/null +++ b/help/SETCHANBAN @@ -0,0 +1,6 @@ +Level needed: 40 +Usage: SET [channel] CHANBAN [0|1|on|off] + +Enable or disable the chanban feature on a given channel. + +See also: chanban diff --git a/help/SETCKL b/help/SETCKL new file mode 100644 index 0000000..32c8dd5 --- /dev/null +++ b/help/SETCKL @@ -0,0 +1,6 @@ +Usage: SET [channel] CKL + +Sets the number of lines of caps allowed within a 5 second +period before the user will be kicked + +Note: a line with 60% or more is considered to be caps diff --git a/help/SETDCC b/help/SETDCC new file mode 100644 index 0000000..0aa6416 --- /dev/null +++ b/help/SETDCC @@ -0,0 +1,6 @@ +Usage: TOG DCC [0|1|on|off] + Toggles the requirement of a user needing to be on the userlist + before he/she can DCC CHAT the bot. + In other words: + When this is on, only users can DCC. + When this is off, anyone can DCC. diff --git a/help/SETENFM b/help/SETENFM new file mode 100644 index 0000000..fc13d93 --- /dev/null +++ b/help/SETENFM @@ -0,0 +1,3 @@ +Usage: TOG [channel] ENFM [0|1|on|off] + Toggles mode enforcement on a certain channel. + See also: SETENFM diff --git a/help/SETENFMODES b/help/SETENFMODES new file mode 100644 index 0000000..8b47d5c --- /dev/null +++ b/help/SETENFMODES @@ -0,0 +1,4 @@ +Usage: SET [channel] ENFM + Enforces modes on a channel if a server changes them or + if a non-user changes them. + See also: TOGENFM diff --git a/help/SETENFPASS b/help/SETENFPASS new file mode 100644 index 0000000..2bde6fe --- /dev/null +++ b/help/SETENFPASS @@ -0,0 +1,5 @@ +Usage: SET [channel] ENFPASS [0|1|on|off] + +Require users to have a password set to perform +commands. Commands attempted by un-passworded users +will be ignored. diff --git a/help/SETFL b/help/SETFL new file mode 100644 index 0000000..af39a44 --- /dev/null +++ b/help/SETFL @@ -0,0 +1,3 @@ +Usage: SET [channel] FL + Sets the number of lines by the same person within a 10-second + period before it is considered a flood diff --git a/help/SETFPL b/help/SETFPL new file mode 100644 index 0000000..5a85b80 --- /dev/null +++ b/help/SETFPL @@ -0,0 +1,8 @@ +Usage: SET [channel] FPL + Sets the protection level against floods. + 0 - no action against offender + 1 - kick offender + 2 - kickban offender + Note: After 3rd kick within 10 minutes, the person will be + sitekickbanned. After the 4th time, the person will + be shitlisted. diff --git a/help/SETIKT b/help/SETIKT new file mode 100644 index 0000000..645c2f6 --- /dev/null +++ b/help/SETIKT @@ -0,0 +1,9 @@ +Usage: SET [channel] IKT + +The maximum number of minutes users are allowed to be idle +before being kicked off the channel. + +Default is 0 minutes (idle kick disabled). +Valid range is 0 to 40320 minutes (4 weeks). + +See also: idle diff --git a/help/SETKS b/help/SETKS new file mode 100644 index 0000000..39439ba --- /dev/null +++ b/help/SETKS @@ -0,0 +1,2 @@ +Usage: TOG [channel] KS [0|1|on|off] + Toggles kicking on kicksays. diff --git a/help/SETMAL b/help/SETMAL new file mode 100644 index 0000000..0457558 --- /dev/null +++ b/help/SETMAL @@ -0,0 +1,3 @@ +Usage: SET [channel] MAL + Sets the mass-action level (the level massdeop, masskick, + and masskickban will have no effect on). diff --git a/help/SETMBL b/help/SETMBL new file mode 100644 index 0000000..99c10fd --- /dev/null +++ b/help/SETMBL @@ -0,0 +1,3 @@ +Usage: SET [channel] MBL + Sets the the number of bans allowed during a 10 sec time + period before it is considered a massban diff --git a/help/SETMDL b/help/SETMDL new file mode 100644 index 0000000..4529290 --- /dev/null +++ b/help/SETMDL @@ -0,0 +1,3 @@ +Usage: SET [channel] MDL + Sets the the number of deops allowed during a 10 sec time + period before it is considered a massdeop diff --git a/help/SETMKL b/help/SETMKL new file mode 100644 index 0000000..09638a7 --- /dev/null +++ b/help/SETMKL @@ -0,0 +1,3 @@ +Usage: SET [channel] MKL + Sets the the number of kicks allowed during a 10 sec time + period before it is considered a masskick diff --git a/help/SETMODES b/help/SETMODES new file mode 100644 index 0000000..c6c864b --- /dev/null +++ b/help/SETMODES @@ -0,0 +1,6 @@ +Usage: SET MODES + +Number of MODE operations that can be sent to the IRC server +in a single line. This value can be auto-detected on server +connect if the bot is compiled with support for 005 replies +and the server sends them (most big networks today). diff --git a/help/SETMPL b/help/SETMPL new file mode 100644 index 0000000..21f9c39 --- /dev/null +++ b/help/SETMPL @@ -0,0 +1,6 @@ +Usage: SET [channel] MPL + Sets the protection level against massmoders. + 0 - no action taken against offender + 1 - kick offender + 2 - kickban offender + 3 - kickban and shitlist offender diff --git a/help/SETNCL b/help/SETNCL new file mode 100644 index 0000000..1e234d2 --- /dev/null +++ b/help/SETNCL @@ -0,0 +1,3 @@ +Usage: SET [channel] NCL + Sets the number of nick changes within a 10 second period + before it is considered nickflooding diff --git a/help/SETPASS b/help/SETPASS new file mode 100644 index 0000000..1face3d --- /dev/null +++ b/help/SETPASS @@ -0,0 +1,5 @@ + +Sets password for the specified user. +To remove a password use the string 'none' for password. + +See also: verify, passwd diff --git a/help/SETPROT b/help/SETPROT new file mode 100644 index 0000000..9c01bbb --- /dev/null +++ b/help/SETPROT @@ -0,0 +1,10 @@ +Usage: SET [channel] PROT + +Max enforced protection level for a channel. +Valid levels are 0 through 4. + +Users with a protection level higher than the level +set for the channel will be treated as if their protection +level was the same as the level set for the channel. + +See also: protection diff --git a/help/SETPUB b/help/SETPUB new file mode 100644 index 0000000..77255f3 --- /dev/null +++ b/help/SETPUB @@ -0,0 +1,2 @@ +Usage: TOG [channel] PUB [0|1|on|off] + Toggles the allowance of public commands. diff --git a/help/SETRK b/help/SETRK new file mode 100644 index 0000000..e4a386f --- /dev/null +++ b/help/SETRK @@ -0,0 +1,2 @@ +Usage: TOG [channel] RK [0|1|on|off] + Toggles revenge kicking. diff --git a/help/SETSD b/help/SETSD new file mode 100644 index 0000000..4bb0e01 --- /dev/null +++ b/help/SETSD @@ -0,0 +1,2 @@ +Usage: TOG [channel] SD + Toggles the deopping of non-users who are serveropped diff --git a/help/SETSERVERGROUP b/help/SETSERVERGROUP new file mode 100644 index 0000000..c382479 --- /dev/null +++ b/help/SETSERVERGROUP @@ -0,0 +1,6 @@ +Level needed: 80 +Usage: SET SERVERGROUP + +Set which group of servers to use for the current bot. + +See also: servergroup diff --git a/help/SETSHIT b/help/SETSHIT new file mode 100644 index 0000000..917e54c --- /dev/null +++ b/help/SETSHIT @@ -0,0 +1,2 @@ +Usage: TOG [channel] SHIT [0|1|on|off] + Toggles the kicking of shitlisted users. diff --git a/help/SETSO b/help/SETSO new file mode 100644 index 0000000..3beca17 --- /dev/null +++ b/help/SETSO @@ -0,0 +1,3 @@ +Usage: TOG [channel] SO [0|1|on|off] + Toggles strict opping. If on, if people not in the userlist + are opped, they will be deopped. diff --git a/help/SETSTATS b/help/SETSTATS new file mode 100644 index 0000000..046af5f --- /dev/null +++ b/help/SETSTATS @@ -0,0 +1,6 @@ +Level needed: 80 +Usage: SET [channel] STATS + +Set filename to where statistics for the channel is saved. + +See also: stats diff --git a/help/SETTOP b/help/SETTOP new file mode 100644 index 0000000..24c1afe --- /dev/null +++ b/help/SETTOP @@ -0,0 +1,2 @@ +Usage: TOG [channel] TOP + Toggles the enforcement of the channel topic diff --git a/help/SHIT b/help/SHIT new file mode 100644 index 0000000..708c900 --- /dev/null +++ b/help/SHIT @@ -0,0 +1,12 @@ + +Shitlists someone on all channels (*) or a certain channel +Level - can be 1 through 3 + 1 - doesn't let the person be opped or voiced + 2 - will kb the person + 3 - rebans the person when unbanned by anyone + 4 - add a channel ban (see CHANBAN), requires chanban support +Expire - the number of days the entry will be good for (default is 30) +Reason - reason for the shitlist...displays it on kickban +Note: The bot checks for shitlisted users on join, nick switch, and + when the bot is opped +See also: rshit, qshit, shitlist diff --git a/help/SHITLIST b/help/SHITLIST new file mode 100644 index 0000000..ab90dff --- /dev/null +++ b/help/SHITLIST @@ -0,0 +1,3 @@ +Usage: SHITLIST + Shows the bots shitlist. + See also: SHIT, RSHIT, QSHIT, SHITLVL diff --git a/help/SHOWIDLE b/help/SHOWIDLE new file mode 100644 index 0000000..da473f7 --- /dev/null +++ b/help/SHOWIDLE @@ -0,0 +1,5 @@ +Usage: SHOWIDLE [seconds] + This will show how long people are idle + If a number of seconds is given, it will only show people + idle for more than that amount of time + See also: IDLE diff --git a/help/SHUTDOWN b/help/SHUTDOWN new file mode 100644 index 0000000..a303e79 --- /dev/null +++ b/help/SHUTDOWN @@ -0,0 +1,10 @@ + +Kills all bots and exits. The mech will have to be restarted +manually hand if you want it to return. If a crontab entry +exists to keep the bot running, it will restart the bot once +cron runs it. +If a session file is in use, SHUTDOWN is the proper method +to kill the process. DIE will delete the killed bot(s) from +the session file. + +See also: die, reset diff --git a/help/SITEBAN b/help/SITEBAN new file mode 100644 index 0000000..22887b5 --- /dev/null +++ b/help/SITEBAN @@ -0,0 +1,3 @@ +Usage: SITEBAN [channel] + Sitebans someone on a certain channel + See also: BAN, SCREW, SHIT diff --git a/help/SITEKB b/help/SITEKB new file mode 100644 index 0000000..72d0943 --- /dev/null +++ b/help/SITEKB @@ -0,0 +1,3 @@ +Usage: SITEKB [channel] [reason] + Sitekickbans someone on a certain channel + See also: KB, SCREW, SHIT diff --git a/help/SPY b/help/SPY new file mode 100644 index 0000000..56d062b --- /dev/null +++ b/help/SPY @@ -0,0 +1,22 @@ + +Spy on a certain source of messages. When you join DCC chat, +the STATUS source is added by default as a spy source for you. +If no arguments are given, the current list of active spy +channels is shown. Output is not line buffered and can cause +excess flood if not careful. + + (sources) + STATUS Status messages. + MESSAGE Pivate messages that the bot receives. + RAWIRC Lines received from irc server before processing. + guid: Messages from a bot specified by guid. + botnick: Messages from a bot specified by nick. + channel Activities on the specified channel. + + (destinations) + (none) Send output to you (default). + channel Send output to the specified channel. + >file Send output to file. Lines are appended to the end of the file. + If the file does not exist, it is created. + +See also: rspy diff --git a/help/STATS b/help/STATS new file mode 100644 index 0000000..6c52b58 --- /dev/null +++ b/help/STATS @@ -0,0 +1,3 @@ +Usage: STATS [servername] + Show server status lines. + See also: LINKS, LUSERS diff --git a/help/TCL b/help/TCL new file mode 100644 index 0000000..dee974a --- /dev/null +++ b/help/TCL @@ -0,0 +1,6 @@ + +Execute a TCL command. This command is only available if +the PLEASE_HACK_MY_SHELL option was enabled during the +compiling. It is an extremely risky command to allow. + +============ CONSIDER YOURSELF WARNED ============ diff --git a/help/TCLSCRIPT b/help/TCLSCRIPT new file mode 100644 index 0000000..b16e753 --- /dev/null +++ b/help/TCLSCRIPT @@ -0,0 +1,2 @@ + +Load a TCL script into the energymech. diff --git a/help/TIME b/help/TIME new file mode 100644 index 0000000..d6cb03f --- /dev/null +++ b/help/TIME @@ -0,0 +1,3 @@ +Usage: TIME + Show current time (where the bot is) + See also: UPTIME, ONTIME diff --git a/help/TOPIC b/help/TOPIC new file mode 100644 index 0000000..0649d37 --- /dev/null +++ b/help/TOPIC @@ -0,0 +1,3 @@ +Usage: TOPIC [channel] + Sets the topic on a certain channel + See also: RT, MODE diff --git a/help/UNALIAS b/help/UNALIAS new file mode 100644 index 0000000..266c161 --- /dev/null +++ b/help/UNALIAS @@ -0,0 +1,4 @@ + +Remove an alias. + +See also: alias diff --git a/help/UNBAN b/help/UNBAN new file mode 100644 index 0000000..894a8b8 --- /dev/null +++ b/help/UNBAN @@ -0,0 +1,4 @@ +Usage: UNBAN [channel] [nick|userhost] + Unbans someone on a certain channel + Note: If no nick/userhost is given, you are unbanned + See also: BAN, BANLIST diff --git a/help/UNVOICE b/help/UNVOICE new file mode 100644 index 0000000..9e3950a --- /dev/null +++ b/help/UNVOICE @@ -0,0 +1,4 @@ +Usage: UNVOICE [channel] [nick|mask [...]] + Removes voice from user or users matching the given + nick!user@host mask. + See also: VOICE, MODE diff --git a/help/UP b/help/UP new file mode 100644 index 0000000..3ebdb23 --- /dev/null +++ b/help/UP @@ -0,0 +1,4 @@ + +Ops you on a channel + +See also: op, down, mode diff --git a/help/UPTIME b/help/UPTIME new file mode 100644 index 0000000..0724261 --- /dev/null +++ b/help/UPTIME @@ -0,0 +1,3 @@ +Usage: UPTIME + Shows how long the bot has been running. + See also: ONTIME diff --git a/help/USAGE b/help/USAGE new file mode 100644 index 0000000..78320cd --- /dev/null +++ b/help/USAGE @@ -0,0 +1,3 @@ +Usage: USAGE + Show the usage syntax for a specified command. + See also: HELP diff --git a/help/USER b/help/USER new file mode 100644 index 0000000..c3fb48a --- /dev/null +++ b/help/USER @@ -0,0 +1,41 @@ + +Add/remove users or change their settings. + +Add a user: + USER + <*|channel> [password] + +Remove a user: + USER - + +Change a users level: + USER + + +Add/remove a channel: + USER +chan #channel + USER -chan #channel + +Add/remove a host mask: + USER +host *!*my@*example.com + USER -host *!*my@*example.com + +Only one channel or one mask can be modified with each USER command. + +Prefix flags with a "-" to disable them or a "+" to enable them. + +User flags: + AO auto-op on/off + AV auto-voice on/off + BNC enable a user to use the bouncer feature + ECHO partyline echo on/off + P change protection level (0-4) + If disabling (-P), level is not needed. + +Botnet user flags: + NS not shared, user is not set to botnet but may be + overwritten by incoming shares + RO read only, user is not overwritten by incoming shares + +Example: USER owner +ao -av + Enables user autoop and disables autovoice for user "owner". + +See also: protection, userlist, setaop, setavoice, setprot diff --git a/help/USERHOST b/help/USERHOST new file mode 100644 index 0000000..c5c7c5f --- /dev/null +++ b/help/USERHOST @@ -0,0 +1,3 @@ +Usage: USERHOST + Returns the userhost of a person. + See also: WHOIS diff --git a/help/USERLIST b/help/USERLIST new file mode 100644 index 0000000..00e25be --- /dev/null +++ b/help/USERLIST @@ -0,0 +1,12 @@ + +Shows the userlist. + +Options: + +minlevel List users with access minlevel or higher + -maxlevel List users with access maxlevel or lower + channel List users with access on channel + usermask List users matching usermask + -B List only bots + -C List users with channel access (no global access) + +See also: access, user diff --git a/help/VER b/help/VER new file mode 100644 index 0000000..24e1482 --- /dev/null +++ b/help/VER @@ -0,0 +1,2 @@ +Usage: VER + Returns the version of EnergyMech that is running diff --git a/help/VERIFY b/help/VERIFY new file mode 100644 index 0000000..cb43f12 --- /dev/null +++ b/help/VERIFY @@ -0,0 +1,4 @@ + +Authenticate yourself with the bot. + +See also: passwd, setpass diff --git a/help/VOICE b/help/VOICE new file mode 100644 index 0000000..68ac0e8 --- /dev/null +++ b/help/VOICE @@ -0,0 +1,4 @@ +Usage: VOICE [channel] [nick|mask [...]] + Give voice to a user or users matching the given + nick!user@host mask. Defaults to giving you voice + on the channel where the command is given. diff --git a/help/WALL b/help/WALL new file mode 100644 index 0000000..8dfd13b --- /dev/null +++ b/help/WALL @@ -0,0 +1,2 @@ + +Send a message to all ops on a channel. diff --git a/help/WHO b/help/WHO new file mode 100644 index 0000000..728a9f3 --- /dev/null +++ b/help/WHO @@ -0,0 +1,7 @@ +Usage: WHO [-ops|-nonops] [pattern] + Lists people in a channel that the bot is in now, + or was in. The optional parameters can be used to + list only ops or only non ops respectively. + If a pattern is given, only users matching the pattern + will be listed. + See also: NAMES diff --git a/help/WHOIS b/help/WHOIS new file mode 100644 index 0000000..ce9a38c --- /dev/null +++ b/help/WHOIS @@ -0,0 +1,2 @@ +Usage: WHOIS + Same as the irc command. diff --git a/help/WHOM b/help/WHOM new file mode 100644 index 0000000..565bebd --- /dev/null +++ b/help/WHOM @@ -0,0 +1,2 @@ +Usage: WHOM + Show who are connected to the partyline. diff --git a/messages/8ball.txt b/messages/8ball.txt new file mode 100644 index 0000000..58d9cdd --- /dev/null +++ b/messages/8ball.txt @@ -0,0 +1,37 @@ +As I see it, yes. +Definitly! +I'm leaning toward yes. +It is certain. +It is decidedly so. +It's a chance. +It's likely. +Most likely. +My sources say yes. +Outlook good. +Signs point to yes. +Without a doubt. +Yes +Yes +Yes definitly. +Yes definitly. +You may rely on it. +Dont count on it. +I'm leaning toward no. +My reply is no. +My reply is no. +My sources say no. +No +No +No chance at all! +No way! +Outlook not so good. +Outlook not so good... +Ask again later. +Can not predict now. +Concentrate and ask again. +Dont ask me, ask yourself that one. +I better not tell you now. +I think you should ask yourself that question. +Maybe +Reply hazy try agian. +Reply is hazy please try again. diff --git a/messages/away.txt b/messages/away.txt new file mode 100644 index 0000000..5c48d02 --- /dev/null +++ b/messages/away.txt @@ -0,0 +1,109 @@ +* <- tribble * <- his evil tribble twin, skippy +* <- tribble <*<- grinning tribble +* <- tribble {{:-(*) - klingon eating a tribble +* bwave/qwk v2.30 * offline e-mail from cutting edge +... but, the cats were cool and never said a mumblin' word! +I'm so fucking lagged I might as well be gone +a.a.u.w. -- the american association of ugly wimmin +aardvark: strenuous labor. +abducted by aliens +alaskan visuals by a. roaring boring alice +alternate zucchini use #12: club open-mindedness into conservatives. +and now, radio four will explode. +and the general sat and the lines on the map moved from side to side +another liberal lemming leaps off the cliff to oblivion! +as funny asa brain tumor. +at microsoft, quality it job 1.1! +bother! said pooh, as he put a pin in the piglet doll. +bowman hesitates. you're not ibm compatible. +brothel : new zealand sheep mustering yard. +c programmers continue it. +chelsea clinton is worth a thousand condom commercials. +click click click damn, out of ferret taglines! +clinton defense #1: hey, i just do what the wife says. +contradiction: stubbornness versus stupidity. +courage is the complement of fear. -- heinlein +cows may come and cows may go, but the bull goes on forever. +cthulhu is a h.p. lovecraft thing... +dubious metrics: 10^21 piccolos = 1 gigolo +error in reality.sys, lter reality, un fantasy.com intead? +ew! there are mice on the table! -arthur dent +feminizt's minds are closed for repairs. +for sale: galaxy class starship, one owner. contact quark. +get away from the dynamite, tom said explosively. +getting a blow job... +give us more responsibility - b. clinton +gone to the park bbl +good idea: kissing a loved one. bad idea: kissing a total stranger. +good: hot outdoor sex. bad: you're arrested. worse: by your husband. +has your wife's hair-do ever been ruined by a ceiling fan? +hate to drag this out but i need the screen time - tom +hello. 1-900-spank-me - crow as adam west on phone +how do you know when you run out of invisible ink? +how many minbari does it take to screw in a lightbulb? sheridan +how to stay poor and enjoy it less by i.r.s. +i love my windows 95! ummmm errrr 96! umm nope that's not it 97! +i opened the book of enlightenment and saw only myself! +i put my hand on her belly, and she said would you like to fill me? +i see my signal's turned from green to red. -- hendrix +i thought they'd cured that in 1940. said the doctor. +i wish i was someone else. then i could kiss myself. -- cat +i'll eat you for lunch any day. +i'm all over that like a cheap suit. +i've seen centipedes and snakes and my head is full of aches..... +if i hold you any closer i'll be in back of you. - groucho marx +if u keep a can of raid on the kitchen table.. you might be a redneck. +im fucking my sister!$% +insert foot in mouth, chew thoroughly, echo internationally... +kate smith - it's over. i sang. +kosh??? - sheridan +life's a piece of shit, when you look at it. +lumptadump: stack of trash bags sitting by the curb. +may your daughters be like the flowers in the field -- wither away and f +mechanical engineers do it with fluid dynamics +memory chip makers love windows! +murphy's law of sex: this won't hurt, i promise. +my one regret in life is that i'm not someone else. +national directory of irish aa members - short book +nixon had watergate, clinton has whitewater. +novel, baldrick...not navel. i sent him my novel. -edmund +now i have 1 less incident to tease you about! - dragonrider +on their knees the war pigs crawling... +only two things can top sex: whipped cream and a cherry. +or worse, not to have a mind -dan quayle +oxymoron: humane society +people of stature: people who don't need status. +please Leave a Message or something... +potty emergency! wakko warner +profanity -- not just for windows users. +quarks -- the cheers of the 24th century, and beyond... +rampart how do you read? uh, never got past 3d grade -tom +scully: i don't know, i've never had the pleasure. +she is so flat her baby had to nurse with a straw. +size matters not. judge me by my size, do you? --yoda +slogan for pentium: 3.998245917 division considered harmful. +someone could get hurt. lore +something would have to be done... something irresponsible... +stay tuned as the canadiens smoke roger's team. +steers like a dream! - yakko +taco bell grandee: taco's owner. +testing this new script +the gf is here, gotta run errr get busy +the high school bigshot! - tom sings +the letter `n' could be a vowel if someone stood up for it. +w a r n i n g!! recipes are v e r y addictive!! +wait'll my young life meeting hears - tom +we are all driven into the same fold. +we haven't met before. exactly. kirk re moreau +werd.. away +what the hell do you want? go away +when should i telephone my lawyer? +windows error #3214: operator out of patience! +wishing you and yours a very merry christmas and a happy new year!! +worf! still struggling up that evolutionary ladder?--q +you are someone else i am still right here +you call this a storm??!! lt. dan on hurricane carmen +you idiots! you've captured they're stunt doubles! +you see the nipples on that dog?!? - tom +you're stinking on purpose, anrn't you? +your mother is so fat, she jumped for joy and got stuck. diff --git a/messages/insult.txt b/messages/insult.txt new file mode 100644 index 0000000..a8029a1 --- /dev/null +++ b/messages/insult.txt @@ -0,0 +1,56 @@ +And tell me, are you still making Nightly installments on your new car? +Any similarity between you and a human is purely coincidental. +Are you always this stupid or are you making a special effort today? +Can I borrow your face for a few days? My ass is going on holiday. +Congratulations; you're a perfect argument against brother-sister marriages. +Do YOU ever get tired of having yourself around? +Do you have your easygoing nature because you're too heavy to run, or just too fat to fight? +Don't I know you from high school, back when you only had one stomach and one chin? +Don't let you mind wander - it's far too small to be let out on its own. +Don't tell me - I know who you are! Yeah, you're the reason they made birth control... +Follow Cobain's footsteps, blow your brains out. It's not like you've got much to lose... +For a minute there I didn't recognize you. It was the happiest minute of my life. +Go fart peas at the moon! +Hi! I'm a human! What are you? +I can tell that you are lying - your lips are moving. +I can't remember your name, but your nasty attitude is kinda familar... +I don't know what I'd do without you, but I'd like to try. +I don't know what makes you tick, but I hope it's a time bomb. +I just figured something out: if I bought you for what *I* thought you were worth, and sold you for what *you* thought you were worth, I'd be the richest guy in the world... +I like you better the more I see you less. +I thought of you today. I was at the zoo. +I would have liked to insult you, but the sad truth is that you wouldn't understand me. +I'd smack the shit out of you if I didn't think it would fill up the room +I'll swear eternal friendship to anyone who hates you as much as I do. +I'm sure you'll be alright when the marijuana wears off. +If I want any shit from you I'll squeeze your head +If Moses had've seen YOUR face, he would've written an 11th commandment. +If the old saying "What you don't know won't hurt you" is true, you must be invulnerable... +If you ever get into an elevator, It better be going DOWN... +Is that your face, or did you block a kick? +Let's play horse. I'll be the front end, you be yourself. +May the fleas of a thousand camels infest your armpits. +May your bollocks turn cubical and fester at the corners. +Pertaining to your physiognomy, it suffices to say that, since it occupies a position on the front of your head, it must be a face. +Sit down, give your mind a rest - it obviously needs it. +Tell me, how many Peeping Toms has your mother cured? +The inbreeding is certainly obvious in your family. +The only reason people look up to you is because it's easier than trying to look AROUND you. +The only thing that will ever make you look good is DISTANCE! +There's nothing wrong with you that a miracle couldn't cure... +Well, you're proof that reincarnation can happen. I mean, who else could get THAT ugly in only one lifetime? +Weren't you the one who sent your picture to the Lonely Hearts Club and got the reply "Sorry, we're not THAT lonely..." ? +What a combination: IQ of a Ph.D., Intelligence of a mentally retarded termite. +What sexual position produces the ugliest children? Ask your mom. +What's on your mind? if you'll please excuse the exaggeration... +When I look into your eyes, I see straight through to the back of your head. +You are living proof that manure can sprout legs and walk. +You are not as bad as people say - you are much, much worse. +You are so stupid you couldn't pour piss out of a boot if the instructions were written on the bottom of the heel. +You could make a good living renting yourself out to scare people with hiccups.. +You'd make a good violinist, if you could figure out which CHIN to put the violin under... +You're not yourself today. I noticed the improvement immediately. +Your a few fries short of a happy meal! +Your as sharp as the leading edge of a BB. +Your underarms are so hairy, you look like Buckwheat in a headlock. +Yours is a prima facie case of ugliness. Your body is damned ugly, too. diff --git a/messages/kick.txt b/messages/kick.txt new file mode 100644 index 0000000..9039e06 --- /dev/null +++ b/messages/kick.txt @@ -0,0 +1,28 @@ +lamer lamer lamer lamer lamer lamer +luser luser luser luser luser luser +oink oink oink oink oink +bork bork bork bork bork +Go away we don't server your kind here! +We don't tolerate your kind around here! +You are not welcomed in this channel leave. +Sorry, Please rejoin so I can kick you again. +I love you, you love me, can't we be one big happy family? +I'm gonna kick your fat cheesy ass! again! +You look dumb, get out! +Lets play kick the can! +Can't we all be friends? +I do not like you, simple as that +Do not meddle in the affairs of Ops, for they are subtle and quick to anger! +I have a problem with your attitude, don't come back again! +You are very indithscreet and stuphid too LisP KiCk 1.0ª +fuck you dipshit mother fucking piece of jurassic defacate! +dewd whut r u stewpid er sumtin'? +eat my scrotum you little fuck +Inferiority Complex Error +Dork Limit Exceeded +Random act of violence +eye nub ewww +scroo yew +phuc j00 +piss off + I LovE Yu  diff --git a/messages/nick.txt b/messages/nick.txt new file mode 100644 index 0000000..10aba59 --- /dev/null +++ b/messages/nick.txt @@ -0,0 +1,79 @@ +AphexTwin +Orbital +NiN +Ministry +Acid +Resistant +Orbital_2 +P_Window +Ambience +HardFloor +DJ_ESP +Trance +Atlantic +Two +Trance +Four +Mations +Artif +icial +Intel +ligence +Lords +of_Acid +Piggy +Happi +ness +in +Slavery +Head +Like +A +Hole +Delta +Source +I +Will +Crush +You +All +Someday +It`s +Snowing +Outside +As +I_Write +This +HAL9000 +SAL9000 +Discovery +Universe +Galaxy +Light +Speed +Gravity +Eternity +Einstein +Maser +Laser +Vaser +Xaser +Zaser +Mazer +Lazer +Vazer +Xazer +Zazer +Sphere +Cube +Line +Wrinkle +Hooger +Groper +Sleuth +Fatso +Pops +Later +WeShall +GoGet +Tanked diff --git a/messages/pickup.txt b/messages/pickup.txt new file mode 100644 index 0000000..e9345b1 --- /dev/null +++ b/messages/pickup.txt @@ -0,0 +1,36 @@ +Is it hot in here, or is it just you? +If I told you you had a beautiful body, would you hold it against me? +You look like the type of guy who's heard every line in the book, so what's one more? +I'd like to see you're hard drive? I bet it isn't 3.5 inches and it ain't floppy. +Can I make ur software turn into hardware? +Those are nice pants...could I talk you out of them? +Why don't I sit on your lap, and we'll talk about the first thing that pops up? +Do you know what would look absolutely terrific on you? Me!!! +Sex is a killer...want to die happy? +I looked up your name in the Thesaurus today and it was included under hot stuff! +Was your dad a king for a day? He must have been to make a prince like you! +Do you have a fever? 'Cause you look pretty hot from here. +Do you believe in love at first sight...or do I have to walk by again? +Are your legs tired? 'Cause you've been running through my mind all night! +Do you have a map? Because I keep getting lost in your eyes. :) +Sit on my lap and let's get things straight between us. +If you've lost your virginity, can I be the box it came in? +Those pants looks good on you, but they would look better on my bedroom floor. +If you are what you eat, I could be you by morning. +What do you think of this dress? I like nothing better. +Do you sleep on your stomach? If not, Can I? +That's a nice smile you've got, it's a shame it's not all you're wearing! +I love every bone in your body. Especially the one I'd like in mine! +Are you free tonight, or will it cost me? +Screw me if I'm wrong, but you want to kiss me don't you? +Can I look at the tag on ur shirt? I want to check see if you were made in heaven. +Hey baby, are you wearing space underwear tonight? Because your ass is out of this world! +Do you have mirrors in your pockets? Because I can see myself in your pants! +I want to melt in your mouth, not in your hand. +Excuse me, do you mind if I stare at you for a minute? I want to remember your face for my dreams. +I hope the word of the day is legs, because I would sure like to spread the word. +Was your father a thief? 'Cause someone stole the stars from the sky and put them in your eyes! +Your daddy must be a baker, 'cause you've got a nice set of buns. +Do you know the essential difference between sex and conversation? If not, Wanna go upstairs and talk? +Do you mind if I ask you a personal question? Would you mind licking my belly-button? From the inside? +I know milk does a body good, but DAMN...How much have you been drinking? diff --git a/messages/say.txt b/messages/say.txt new file mode 100644 index 0000000..d972e7e --- /dev/null +++ b/messages/say.txt @@ -0,0 +1,984 @@ +"A wok is what you throw at a wabbit." +"Alright, but Shamu wouldn't work under these conditions!" +"Are we clear?" -- Teacher "No, we're opaque." -- Yakko Warner +"But we'll never survive!" "Nonsense. You're only saying that because no one ever has." +"Grey! Did I hear you say grey?" "Light black. From pole to pole." +"Here's what I know," said the physicist. "If we lived in a microscopic world, trucks would crash into walls, fly apart and then reassemble perfectly on the other side of the wall. This has been proven." +"How do you get all those coins?" "IN PAIRS." +"How many tentacles has Great Cthulhu got?" "Too many." +"I guess the joke's on me," she said +"I only live about ten percent in this reality." "So where's your summer cottage?" +"So, we're doing some restructuring and looking into changes to our pay progression. How is the progression at your firm set up?" "Generally speaking, the people at the top tend to make more than the people lower down." +"What did Medieval people do before television?" "Had tea, I suppose." +"Yes..but I'm a CHOSEN Heathen!" +'Tis an ill wind that blows no minds. +'Twas Brillig, and the slithey toves did gyre and gimble in the wabe; all mimsy were the borogoves, and the mome raths outgrabe. +... is exhibited in the radical endosemic character of the sign as such. +:-X My lips are sealed, @FN@. +@FN@, I do not understand the threat I bring to you. +A conservative is a liberal who has been mugged. +A dirty mind is a terrible thing to waste. +A few thousand rads never hurt anybody. +A foolish consistency is the hobgoblin of little minds. +A good fight is like a stick of broccoli, but different. +A good hot dog feeds the hand that bites it. +A good man has few enemies. A ruthless man has none. +A good memory does not equal pale ink. +A hard drive is more satisfying than a soft floppy! +A hundred thousand lemmings can't be wrong. +A king's castle is his home. +A lack of leadership is no substitute for inaction. +A liberal is a conservative who has been arrested. +A little inaccuracy sometimes saves tons of explanation. -- H. H. Munroe +A little madness now and then is relished by the wisest men. +A little rudeness and disrespect can elevate a meaningless interaction to a battle of wills and add drama to an otherwise dull day. +A live body and a dead body contain the same number of particles. Structurally, there's no discernible difference. Life and death are unquantifiable abstracts. Why should I be concerned? +A man who fishes for marlin in ponds will put his money in Etruscan bonds. +A man who turns green has eschewed protein. +A motion to adjourn is always in order. +A much-discussed alternative to homogeneous big-bang nucleosynthesis has been the first-order quark-hadron phase-transition-inspired inhomogeneous model. +A nudist is just a person in a one-button suit. +A poet who reads his verse in public may have other nasty habits. +A small mind is a tidy mind. +A sucking chest wound is just nature's way of telling you to slow down. +A thousand lips, a thousand tongues, a thousand throats, a thousand lungs... a thousand ways to make it true, I want to do terrible things to you +A truly wise man never plays leapfrog with a Unicorn. +A virus is a Daemon with an attitude problem! +A woman is only a woman, but a good cigar is a smoke. +A woman without a man is like a fish without a bicycle. +Abortion kills, but so does McDonalds. +Above all things, revere yourself. +Ack!! +Acting without thinking can be awfully entertaining. +Actors will happen in the best-regulated families. +Ad astra per aspera. +Admiration: Our polite recognition of another's resemblance to ourselves. +After all, we only go around once. There's really no time to be afraid. +Ah-ah, Mr. Wiiilson. Ah-ah, Mr. Heath. +All Hail Discordia. +All Wood? Oh! AL Wood. I didn't see the missing "L" at first. +All biography is ultimately fiction. +All great ideas are controversial, or have been at one time. +All in all it's just another brick in the wall... +All in favor of telekinesis, raise my hands. +All men are created unequal. +All men have the right to dig their own graves, and I have the right to sell them the shovels. +All requests for sick leave must be approved two weeks in advance. +All suspects are innocent until proven Discordian in a Court of Chaos. +All that glitters has a high refractive index. +All you can eat, shrimp +Always store beer in a dark place. +Always tell her she is beautiful, especially if she is not. +An idle mind is worth two in the bush. +Anarchy -- it's not the law, it's just a good idea. +And I alone am returned to wag the tail. +And so it begins that some things last forever... +And the Lord spake unto Elvis, "Thou hast spoken against me Elvis, and I curse thee to have thy blue suede shoes trod upon for all time..." +And the first one said to the second one there, "I hope you're having fun." +And the less I seek my source for some definitive, closer I am to fine... +And where you are ain't no good unless you can get away from it +Animals can be driven crazy by placing too many in too small a pen. +Another damned, thick, square book! Always scribble, scribble, scribble! Eh, Mr. Gibbon? +Antibiotics, she said. +Any given program, when running, needs debugging. Any debugged program is obsolete. +Any priest or shaman must be presumed guilty until proved innocent. +Any technology distinguishable from magic is insufficiently advanced. +Anybody can win, unless there happens to be a second entry. +Anybody got any itch cream? +Anybody who cannot comprehend mathematics is not fully human. At best he is a tolerable subhuman who has learned to wash, cook food, and not make messes on the floor. +Anything I do is purely coincidental. +Anything not nailed down is mine. Anything I can pry loose is not nailed down. +Are we there yet, Papa Smurf? +Art is dangerous +As goatherd learns his trade by goat, so writer learns his trade by wrote. +As long as the answer is right, who cares if the question is wrong? +As long as the music's loud enough, we won't hear the world falling apart. +As the Euclideans would have it, irrationality is the square root of all evil. +Ashes to ashes, dust to dust, if you don't take it out and use it it's going to rust. +Atoms of the World, UNITE (whoops, too late) +Attention all planets of the Solar Federation: We have assumed control. +Ave, Imperator, morituri te salutant... +Awright, you people! Who stole my strawberry preserves? +Bad things come to those who wait too. +Bad times for virii -< +Ban Censorship! +Be careful, or I'll include you in my plans. +Be excellent to each other +Be my brother or I'll kill you. +Be wary of strong drink. It can make you shoot at tax collectors -- and miss. +Because it's there. +Being a doormat gets you walked on. +Being generous is inborn; being altruistic is a learned perversity. +Being good at being stupid doesn't count. +Bela Lugosi's dead. How about you? +Believe nothing, dare all. +Better to have thirty minutes of wonderful than a lifetime of nothing special. +Beware of a dark-haired man with a loud tie. +Beware of a tall dark man with a spoon up his nose. +Beware of all clothes that require new enterprises. +Beware of all enterprises that require new clothes. +Beyond good and evil lies North Dakota. +Big donkeys, small donkeys, all good to eat. +Bite me, it's fun. +Blessed are they that run around in circles, for they shall be known as wheels. +Bonzo the Demon Dog from Hell with Handlebars for Ears. +Bored people are boring people. +Boxing is a lot like ballet, except that they don't dance, there isn't any music, and they hit each other. +Brain fried -- Core dumped +Bugger off. +Bullshit makes the flowers grow and that's beautiful. +But I don't like Spam!!! +But she looked good in ribbons... +C Error #009: FATAL! Portable code found +CChheecckk yyoouurr dduupplleexx sswwiittcchh.. +Can't find his ass with two hands and a periscope/ +Cannibalism is a small price to pay for popularity. +Censorship? We don't have any censorship here. If we did, I couldn't say ---- or ------ ------! +Change your mind, it's starting to smell. +Change your thoughts and you change your world. +Chaste makes waste. +Chill out... everything's under control... +Climate is what we expect, weather is what we get. +Coffee sweetened with NO-DOZ...Programmers petrol! +Cogito cogito ergo cogito sum: I think I think, therefore I think I am. +Coitus ergo sum. +Colorless green ideas sleep furiously. +Common sense is what tells you that the world is flat. +Computers, like cats, can operate crossdimensionally; the trick is in getting them to do what you want. +Confidence: a feeling peculiar to the stage just before full comprehension of the problem. +Confucious says man with no legs bums around. +Congregation, please be seated +Continental Life. Why do you ask? +Convictions cause convicts. +Could I interest you in a pet rock? +Crazee Eddeee, his prices are INSANE!!! +Cthulhu Saves. He might get hungry later. +Cthulhu for President -- for when you're tired of choosing the _lesser_ of the two evils. +Cthulhu saves our souls and redeems them for valuable coupons later. +Cut the conversation, just open your mouth +Cyberspace is where you are when you're on the telephone. +D'oh! +DON'T call me dude, dude. +Damn the torpedoes. +Dare to be stupid. +Darwin's Law of Carcinogens: Cancer cures smoking. +Dating processes are dependent on the analysis of rocks. +Dead puppies aren't much fun. +Death is an illusion. Life is a hallucination. Taxes are objective reality. +Death is just the ultimate expression of radical solipsism. +Death without pain is like a sundae without sprinkles. +Decadent Capitalist and proud of it! +Deceive boys with toys, but men with oaths. +Deity... he's come for me +Del Mar: Where poverty is neither a philosophy nor a way of life. +Delicious and nutritious, tastes like chicken! +Deprive a mirror of its silver and even the Czar won't see his face. +Der Gedanke an den Selbstmord ist ein starkes Trostmittel. +Despite the high cost of living, it remains a popular item. +Did you exchange... a walk-on part in the war... for a lead role in a cage? +Did you hear about Quasimodo? I had a hunch he was back. +Did you know that if you were a kangaroo, you couldn't be a mailman? +Did you know that lithium builds up in the fatty tissues of the body? +Ding a ding-dang my dang-a-long ling-long +Do I *amuse* you? Am I a *clown* to you? +Do it for the Widow's Son. +Do it, or you'll be fishing yak cheese from your nostrils! +Do not call up that which you cannot put on hold. +Do not merely believe in miracles, rely on them. +Do not remove tag under penalty of law. +Do not take life too seriously; you will never get out of it alive. +Do not taunt the Happy Fun Ball. If Happy Fun Ball starts to smoke, run away and take cover. +Do not underestimate the power of the Force. +Do what keepeth thou from wilting shall be the law of the Hole. +Do you ever feel like putting your fist through a window just so you can feel something? +Do you ever get feelings of Deja Vu? +Do you ever get feelings of Deja Vu? +Don't believe in the Uzi, it just went off in my hand. +Don't believe them, don't believe them, question everything you're told +Don't eat yellow snow. +Don't force it, use a bigger hammer. +Don't give up, push a pawn +Don't have a cow, man. +Don't knock masturbation; isn't sex with someone you love the best kind? +Don't look now, but the man in the moon is laughing at you. +Don't look now, but there is a multi-legged creature on your shoulder +Don't open your eyes, you won't like what you see; the devils of truth steal the souls of the free... don't open your eyes, take it from me -- I have found you can find happiness in slavery... +Don't panic. +Don't speak about Time, until you have spoken to him. +Don't worry. Baldrick has a cunning plan. +Dont'cha hate it when that happens? +Drawing on my fine command of language, I said nothing. +Drive carefully. 90% of the people in the world are caused by accidents. +Drive defensively; buy a tank. +Drive your cart and plow over the bones of the dead. +Driving makes you stupid; the more you drive, the stupider you get. +Duct tape is like the Force. It has a light side and a dark side, and it binds the universe together. +Due to circumstances within our control, tomorrow will be cancelled. +During the Asparagus Season, members are requested not to relieve themselves in the hatstand. +Dying can be hazardous to your health. +Dying is easy. Comedy is hard. +Eagles may soar, but weasels don't get sucked into jet engines. +Early to bed and early to rise makes a man stupid and blind in the eyes. +Earn cash in your spare time, blackmail your friends. +Eek! +Eggs on top, canned goods on the bottom... +Et sic patet quod totus mundus est sicut unum speculum plenum luminibus praesentantibus divinam sapientam, et sicut carbo effundens lucem. +Eureka! +Even the boldest zebra fears the hungry lion. +Every absurdity has a champion to defend it. +Everybody is equal here. It's just some people are more equal than others. +Everybody looks brave holding a machine gun. +Everybody needs a little love sometime; stop mudding and fall in love! +Everybody ought to have a friend. +Everybody ought to have a maid. +Everyone is gifted. Some just open their packages sooner. +Everything in excess! To enjoy the flavor of life, take big bites. +Everything is true -- everything is permissible! +Everything you know is wrong! +Evolution doesn't take prisoners. +Ewige Blumenkraft! +Excuse me, what did you say? Oh, I thought you said "Whop". +Express yourself through supreme bovinity. +FIVE TONS OF FLAX! +Fait accompli... +Far duller than a serpent's tooth it is to spend a quiet youth. +Few things work up an appetite like good, wholesome, sadomasochistic sex. +Fidelity: A virtue peculiar to those who are about to be betrayed. +Fight crime. Shoot back. +Fine, DON'T have a nice day, see if I care. +First I must sprinkle you with fairy dust... +Fish heads, fish heads, roly-poly fish heads +Five is a demon. +Flashs in the Pan: Pet Rocks & Rush Limbaugh +Flee at once, all is discovered. +Fliegende Kinderscheisse! +Flixtipix make flossing the cat fun! +Fnord is the second spoon in your Whizz Fizz. +Fnord! +Fnord. +Fnord? +Foolish little minds are consistent among hobgoblins. +For every new foolproof invention there is a new and improved fool. +For f**ks sake, use a condom! +For further information, consult your pineal gland. +For him to get a clue would require heroic implant surgery. +For some reason the concept of sacrificing accuracy to increase efficiency seems inherently wrong. +For such a cute kid, you sure turned out ugly. +Fred and Daphne were swingers. It's true! Look at the scarves they wore! +Freedom begins when you tell Mrs. Grundy to go fly a kite. +Freedom is just a hallucination created by a pathological lack of paranoia. +Fresh of the Grill: same talk different company! +Frogs are my favorite vegetable. +From the halls of Montezuma to the shores of Tripoli, we are all tools of the Conspiracy... +Frumble! Frumble! Istharcity! +Futility is futile. +Fuzzy Wuzzy wuz a bear. +G'day! Ewige Blumenkraft, Mate! +GARDEN;BOTAN;FLOWER;TREE +Get a hat! Get a hairy hat! +Get up Dog Boy! Show them where you come from, show them where you're going! +Girls will be boys and boys will be girls +Give a monkey a brain and he'll swear he's the center of the universe. +Give me a place to stand and I will move the earth. +Give your child mental blocks for Christmas. +Go Speed Racer Go Speed Racer Go Speed Racer Go +Good, bad, *I'm* the guy with the gun! +Government is a burden on the people. +Greatness is a transitory experience. It is never consistent. +Green potatoes and green potato chips are to be distinguished from brown potato chips, which are another matter entirely. +Greetings, lowly citizen. I seek your PEZ! +Gridlock to the max, Jack +Grumplicious Grumplicious Grumplicious!!! +Guns don't kill people, I do. +Guns don't kill people. Bullets do. +Hail / Praise / Ia / Fuck / Grep / Eat Eris / "Bob" / Cthulhu / The Conspiracy / Kibo / Spam +Hail Bob! +Hail Eris! +Hail Stones! +Half the trick is knowing the CyberPunks from the CyberPinks. +Happiness comes in packages marked 'Batteries Not Included'. +Happiness is mandatory. Are you happy? +Hard drugs made me a better person. +Hardware hackers have sensitive probes. +Hastur Hastur Hastur Hastur Hastur Hastur Hastur Hastur Hast... urrrrrrrk! +Have you ever danced with the devil in the pale moonlight? +He buy me a soda... he buy me a soda and tried to molest me in the parking lot, yep yep yep yep +He can't be a man cause he does not smoke the same cigarettes as me +He doesn't like me. HE NEVER LIKED ME! +He sells contact lenses for a cubist world +He who has a shady past knows that nice guys finish last. +He who hates vices hates mankind. +He who laughs, lasts. +He who lives without folly is less wise than he believes. +He who marries the Spirit of his Age, soon becomes a widower. +He's a tragic figure in a paisley shirt +He's a trash-culture king on a four-color throne +Head of Security: Barb Dwyer +Hellllllooooooooo, Nurse! +Help! There's a cow in my esophagus! +Here he comes -- here comes Speed Racer -- he's a demon on wheels +Heute die Welt, Morgens das Sonnensystem! +Hey, go buy a plane ticket to another state of mind, okay? +Hey, that's just the way the cookie gets completely stomped on and obliterated. +Hey, wow man, they've got codes for mazurkas! +Hey, you've got a third eye! Uh, never mind... it's just a spot of dirt in the middle of your forehead. +His philosophy was a mixture of three famous schools -- the Cynics, the Stoics and the Epicureans - and summed up all three of them in his famous phrase, "You can't trust any bugger further than you can throw him, and there's nothing you can do about it, +History: an account mostly false, of events mostly unimportant, brought about by rulers mostly knaves and soldiers mostly fools. +Hold on to freedom as long as you can, 'cause the girl in your cell might think she's a man +Hold! Cease thy stultiloquy! Oh, not daggers or swords or all the spears in the world stung me greater than this rubbish you throw to the heavens!" +Homosexuality is the best all around cover an agent ever had. +Honesty is the best policy, but insanity is a better defense. +Honi soit la vache qui rit. +Horniness is a quintessential example of hope. +How can you be in two places at once when you're not anywhere at all? +How can you tell if a man is sexually excited? He's breathing. +How do you make a cat go moo? Ask it "Does a dog have the Buddha-nature?" +How many times do I have to tell you that drilling holes in your head will not relieve stress? +How sharper than a hound's tooth it is to have a thankless serpent. +How untasteful can you get? +Humans sure have a strange way of dying. +Humans: Bet you can't eat just one. +I *almost* stole a tagline! I'm so ashamed! +I admit that I am powerless over fish innards. +I am GOD here! +I am Zorkon, this is my brother Zenon, and this is my other brother Zenon. +I am always right. Except when I'm left, or bluffing. +I am feeling so grey today... +I am living in denial. +I am sooo DE-VO! How long can this go on?! +I am still waiting for the advent of the computer science groupie. +I am the Electric Messiah; an AC/DC god. +I am woman! Hear me whine! +I believe that everyone is entitled to my opinion. +I call her Attila, 'cuz she's my Hun... +I came, I saw, she conquered. +I dance around and sing a song and know that I can do no wrong. +I didn't claw my way to the top of the food chain to eat vegetables! +I didn't yield to temptation. I came to a complete stop. +I don't believe in reason, objective reality or collective farming. +I don't have a drinking problem... I drink, I get drunk, I fall down. No problem. +I don't want anybody else; when I think about you I touch myself +I hate myself to sleep at night +I hate people who think it's clever to take drugs -- like customs officials. +I hate prejudiced people. +I have come to chew bubble gum and kick ass, and I'm all out of gum! +I have nothing to declare, except my genius. +I haven't lost my mind; it's back up on tape somewhere. +I hear music! +I intend to live forever or die trying. +I just like to say quark, quark, quark, quark, quark, quark... +I just walk right through the door... +I know the road, I know where it goes; that's why I'm going so slow... +I know your secret. +I like winter. I like the cold. I like the sound of flowers dying. +I like work; it fascinates me; I can sit and look at it for hours. +I like you, Sali. That's why I'm going to kill you last. +I live like this 'cuz I like it; I've seen too much to pretend +I looked up at her and asked how much is that gonna cost? +I love the absolution of all jocks and preps -- Skate or die!!! +I may have my faults, but being wrong isn't one of them. +I must have a prodigious quantity of mind; it takes me as long as a week sometimes to make it up. +I must have slipped a disk -- my pack hurts +I must not fear. Fear is the mind-killer. Fear is the little death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past I will turn the inner eye to see its path. Where the f +I need someone real bad. Are you real bad? +I never met a repo man who didn't do speed. +I never remember a face, but I always forget a name. +I once waxed the floors of a nursing home, pulled off all the rubber feet on the walkers, and yelled "FIRE!" +I only kill to know I'm alive. +I prefer to think of them as the Ten Suggestions. +I salivate at the sight of mittens. +I saw a subliminal advertising executive for a second. +I seem to be having tremendous difficulty with my lifestyle. +I started to correct my mistakes, but realized what I was doing in time. +I think we're all Bozos on this bus. +I think you had better start lining your hat with tinfoil. +I used to be self-actualized, now I'm just confused. +I wanna reach up and touch the sky; I wanna touch the sun but I don't wanna fry +I will never lie to you. +I will not celebrate meaningless milestones [100th "special" episode] +I wish you humans would leave me alone. +I wouldn't know how to act if I weren't in trouble. +I wouldn't recommend sex, drugs and insanity for everyone, but they've always worked for me. +I'd give it a 10, but you can't do the batoosie to it. +I'd give my right arm to be ambidextrous. +I'd rather have a bottle in front of me than a frontal lobotomy. +I'll be back. +I'll get all of you someday. I'll wipe out your whole family. +I'll heal your wounds, I'll set you free; I'm Jesus Christ on ecstasy +I'm a Fun Guy from Yuggoth. +I'm a solipsist, I don't see why other people aren't that way... +I'm fond of children -- except for boys. +I'm just a revved up youth on a thrill-kill rampage. +I'm looking for a cynical girl +I'm not as think as you stoned I am. +I'm not dead yet! +I'm not dead. +I'm not sure of what I should do -- when every thought I'm thinking of is you +I'm not wearing any underwear +I'm not wearing any underwear. +I'm sorry -- my karma ran over your dogma. +I'm sorry, I become inaudible when I am shy. +I'm sorry, did I assassinate your penguin? +I'm the one from the registry office. +I've always maintained that one should never tangle with anything that has more teeth than the Osmond family. +I've come a long way since I've believed in anything +I've given up the search for reality; now I'm just looking for a good fantasy. +I've seen the future and I leave it all behind +I... I believe in love +I... have become... comfortably numb +IBM: It's Become Monolithic +If Cthulhu calls... let the machine pick it up. +If Goddess had intended humans to smoke, She would have set them on fire. +If I let you go, do you think you could fly? +If I was a woman I'd stay at home all day and play with my breasts. +If I were you I'd dance naked in the middle of the street just to embarrass you. +If at first you don't succeed, quit; don't be a nut about success. +If at first you don't succeed, try following the instructions. +If infinite rednecks fired infinite shotguns at an infinite number of road signs, they'd eventually create all the great literary works of the world in braille. +If it looks like a duck, acts like a duck, and quacks like a duck it is probably just a tool of the conspiracy. +If it weren't for lawyers, I think we could have invented a universal symbolic representation of reality. +If it's comprehensible, it's obsolete. +If opportunity knocks, go to bed with your pants on. +If she doesn't scare ya, no evil thing will... +If the shoe fits, its ugly. +If this is paradise, I wish I had a lawnmower. +If vegetarians eat vegetables, I guess that makes me a humanitarian. +If you act enthusiastic, you'll be enthusiastic! +If you are sitting, just sit. If you are walking, just walk. Above all, don't wobble. +If you are what you eat, I could be you by morning. +If you can't see the fnords, they can't eat you. +If you do that you have a chance of dying +If you don't care where you are, then you ain't lost. +If you feel like you've got to lead, at least get out of my way. +If you have to ask, you're not allowed to know. +If you hit a man over the head with a fish, he'll have a headache for a day. But if you teach a man to hit himself over the head with a fish, he'll have headaches for the rest of his life. +If you only want to go 500 miles, can you begin with a halfstep? +If you think before you speak the other guy gets his joke in first. +If you want to be a Raelet, you gotta let Ray. +If you were everyone but one person i would listen to everyone else. +If you're so special, why aren't you dead? +If your face were a tree, it would be different. +Ignorance can be cured -- but stupid is forever. +Imagery is All In The Mind. +Impressive; but how big's your dick? +In empty spaces I twist and talk, and come down with the roar of thunder +In marriage, as in war, it is permitted to take every advantage of the enemy. +In the not too distant future, next Sunday A.D.... +In this world, truth can wait; she's used to it. +In-con-CEIvable! +Initiative comes to those who wait. +Is it just me or are all uncles either really cool, or child molestors? +It is better to die on your feet than to live on your knees. +It is better to have loved and lost than to have just lost. +It is by caffeine alone that I set my mind in motion. It is by the Coca-Cola that the thoughts acquire speed, the lips acquire stains, the stains become a warning. It is by caffeine alone that I set my mind in motion. +It is easier to fight for one's principles than to live up to them. +It is easy to be tolerant when you do not care. +It is hard for me to find the right thing to say when I find myself in a room full of cockroaches. +It is my firm belief that it is a mistake to hold firm beliefs. +It is not down on any map; true places never are. +It is not enough merely to succeed: one's friends must also fail. +It is the wise bird who builds his nest in a tree. +It isn't the fall that kills the child, it is the splattering of the brain against the inside of the skull. +It was a book to kill time for those who liked it better dead. +It was such a lovely day I thought it a pity to get up. +It would be absurd to leave the Suez Canal and the Panama Canal to the unfettered control of the populations through whose territory they pass. +It would have worked, too, if it hadn't been for those meddling kids and their fucking dog! +It would seem that no matter how you slice it, it still comes up Velveeta. +It's all in the mind, ya know. +It's always easier to just fall than to try to climb. +It's an Elder Thing -- you wouldn't understand. +It's an IBM, it's got an excuse. +It's called EVIL, kid. +It's later than you think. +It's not for you or I, Carruthers; nor for the regiment: it's for the Empire. +It's not that life is too short, it's that death is too long. +It's not that they die, but that they die like sheep. +It's now tomorrow +Ivana used to be Donald's trompe l'oeil. +JOEY! JOEY! JOEY THE LEMUR! +JUMPIN' JESUS ON A POGO STICK! +Jake liked his women how he liked his kiwi fruit; firm yet yielding, sweet yet tart, and covered with short fuzzy brown hair. +Javacise - Burst of motion after spilling coffee in one's lap. +Je suis un chou-fleur sous un echequier enorme. +Jesus loves me, but he can't stand you. +Jesus loves you. Everyone else thinks you're an asshole. +Join the Cthulhu Corps; it's not just a job, it's a higher position under an elder god. +Just a simple 'Yes' or 'No' is all I understand +Just as Schopenhauer predicted, absolutely nothing is happening. +Just say NO to single-menu BBS software! +Kill two birds with one stone. Feed the homeless to the hungry. +Klein bottle for sale. Inquire within. +Know new taxes! says President Bush... +Kryten, unpack Rachel and get out the puncture repair kit! +Kzin Zen Master: "What is the sound of one paw slashing?" +LSD melts in your mind, not in your hands. +La-la la-la-la-la la-la-la-la-la! +Last time I was stoned, I tried to eat an airport. +Laugh, and the world ignores you. Crying doesn't help either. +Let a fool hold his tongue and he will pass for a sage. +Let him who takes the Plunge remember to return it by Tuesday. +Let me know when something normal happens. +Let not the sands of time get in your lunch. +Let's all get together and throw flingbobs +Liberals don't believe they deserve anything they own; conservatives think they're entitled to everything they've stolen. +Life is a game of bridge -- and you've just been finessed. +Life is like pudding; soft and squishy. +Life is ours. We live it our way. +Life is sometimes like a pizza round: hot, greasy, and delivered by a guy named 'Tony'. +Life is the stuff that happens to you while you're making other plans. +Like many of the finer things in life, sex often comes with a side of fries. +Lions: 5, Christians: 0. +Little girls, like butterflies, need no excuse. +Live Long and Perspire. +Living right doesn't really make you live longer, it just SEEMS like longer. +Look out, you're gonna craaash. +Loot, pollute and scoot +Love as it exists in society is merely the mingling of two fantasies and the contact of two skins. +Love is a many-splintered thing +Love is in the offing, said the homicidal maniac. +Love's as good as soma! +MITCHELL! Even his name says, "Uh, is that a beer?" +Macintosh error message: "Weird disk error" +Make it so! +Make your own mistakes, not somebody else's. +Man steps in with a terminal grin... blue skies turn to grey... +Man who falls in blast furnace is certain to feel overwrought. +Mankind has been on a bad trip for a long time now. +Mars needs women. +Mashed potatoes can be your friend. +Mate, this parrot wouldn't VOOM if you put four million volts through it! +Maximum capacity eight persons. +May the road rise up and fall on you. +Megaton Man on patrol! +Men are more sentimental than women. It blurs their thinking. +Men still remember the first kiss after women have forgotten the last. +Mind if I rape your daughter +Mind your own business, Spock. I'm sick of your half-breed interference. +Mmm. I forgot about that. +Moderation is for monks. +Mommy's all right, Daddy's all right, they just seem a little weird +Momomoto, Famous Japanese, can swallow his nose. +Monday is an awful way to spend one-seventh of your life. +Money can't buy happiness, but poverty can't buy ANYTHING. +Money is truthful. When a man speaks of honor, make him pay cash. +Most people do not know how to behave in a massacre. +Mouse not found: Boot cat? (Y/N) +Moving parts in rubbing contact require lubrication to avoid excessive wear. +Mow Mow Boppa Mow Mow Mow +My biology I know. You I worry about. +My father was a good woman. +My favorite food is squirrel, but I want to fight with glory. +My favorite weapon is the look in your eyes +My friend Goo has a real time too; she always knows just what to do +My inner child can beat up your inner child. +My karma ran over my dogma +My moral standing is lying down. +My oh my! Life sure is easier with my new Salad Shooter (R) ! +My powers are beyond your understanding! +My question is simply this: Are you menstruating? +My whole life is a darkroom, one... big... dark... room. +My, don't we all look _smashing_ in red. +Nemo me impune lacessit. +Never appeal to a man's "better nature". He may not have one. +Never attempt to distract a masturbating gorilla. +Never eat at a place called Mom's. +Never eat more than you can lift. +Never give an inch! +Never go up against a Sicilian when death's on the line! +Never judge a man till you have walked a mile in his shoes, 'cuz by then, he's a mile away, you've got his shoes, and you can say whatever the hell you want to. +Never rent a car to a racecar driver...! +Never sign a contract including any of the phrases "sort of", kind of", or "and stuff". +Never trust a cop with a rubber glove. +Never try to outstubborn a cat. +Never underestimate the power of human stupidity. +New York, Tokyo, Paris, Hood River +Nice computers don't go down... +Nid yw Cymru ar werth! +Nietzsche thought he was the Antichrist or Dionysus, but that was after the syphilis crossed the blood-brain barrier. +Nil illegitimo carborundum. +No amount of genius can overcome a preoccupation with detail. +No girdle ever cured a pregnancy. +No man is an island, but then no man is a potato salad, either. +No matter how bad it is, there's always worse. +No matter how good it is, there's always better. +No matter where you are, there you go? Wait a second. +No matter where you go... there you are. +No you don't understand, see you don't understand... +No, just the nipple-cones, officer, just the nipple-cones. +Nobody expects the Spanish Inquisition! +Nothing is true. Everything is permitted. +Nothing quite like the feel of something new... +Occam was never the target of a conspiracy. +Of all forms of caution, caution in love is the most fatal. +Of course I still love you. Now stop whining and let me shoot you. +Of course it belongs to me, I'm a communist. +Of course it's the murder weapon. Who would frame someone with a fake? +Of course you realize this means war. +Often I think I am a special agent of Billy Graham. +Ogden's Law: The sooner you fall behind, the more time you have to catch up. +Oh dear, I've gone and inflated my ego. +Oh for the wings of any bird, other than a battery hen... +Oh, Aunty Em, it's so good to be home! +Oh, I wish I were an Oscar Meyer weiner... +Oh, fuck! You did it just like i told you to! +Oh, my God! ARTHUR, I HAVE POCKETS! +Oh, no, not again. +Oh, squirt. +Okay, everybody in this room who's telekinetic, raise my hand. +Okay, everyone out of the gene pool! +Okay, something here's really fucked up. +Okay, you guys having sex here, let's see some skill rolls! +On Earth there is no reckoning. +On a scale of 1 to 4, what are your feelings about the colour green? +One man's theology is another man's belly laugh. +One should not act and speak as if one were asleep. +Only the lavender prairie dogs know the key to unlock the secret of the tumble-weeds. +Only you can prevent forest fires. +Ooooooeee! They sure serve up them four-year-olds good, don't they? +Operator halted! Startrek's on! +Our cause is a secret within a secret, a secret that only another secret can explain; it is a secret about a secret veiled by a secret. +Our god's the FUN god! Our god's the SUN god! Ra! Ra! Ra! +Outside of a dog, a book is a man's best friend. Inside of a dog it's too dark to read. +Overall, there was a smell of fried onions. +PERSEPHONE (Per-SEH-fun-ee) n: Greek goddess of bills +Pain looks good on other people; that's what they're for. +Paranoia doesn't mean the whole world really isn't out to get you. +Parking lot: A place where arguments start from scratch... +Pass the pickles. +Pay careful attention to the Tao-Jones Average. +Penguin lust! +People are blinky +People humiliating a salami! +People who emit Cherenkov radiation make me nervous. +People will buy anything that's one to a customer. +Percepi et esse. +Pig's in zen... +Please go away. +Portions of the preceding were recorded. As for the rest of it, I'm very much afraid it was all in your mind. +Power corrupts. Absolute power is kinda neat. +Profanity: the single language in which all programmers are expert. +Progress is the exchange of one nuisance for another nuisance +Proletarian revolutions are notably ineffective when the ruling class is composed of gods. +Promptness is its own reward, if one lives by the clock instead of the sword. +Prudence is a rich, ugly old maid courted by incapacity. +Quack! +Quantum Express: When you absolutely, positively, don't know where it's going or when it needs to be there. +Quantum particles: the dreams that stuff is made of. -- David Moser +Que sera, serotonin. +Question Authority -- and the authorities will question you. +Question reality. +Quite content to sit on this fence... quite content, now a little bit older... +RAAAIIID!!!??? +Rainy days and Mondays always get me down. +Real punk rockers don't smile. +Reality is the temporary resultant of the struggle between rival gangs of programmers. +Remember, we have to get the baby out of the oven today. +Reverend! +Rose tint my world and keep me safe from the trouble and pain... +Roses are red, she said, and violents are blue +Rub her feet. +Rush Limbaugh: Radio's answer to Professional Wrestling +SLACK: Get it while you can. +SPOON! +Sarcasm helps you avoid telling people what you really think of them. +Sarcasm is a sign of genius. +Save the whales! Collect the whole set! +Say what you mean and say it mean... +Seattle Rain Festival - Jan. 1 to Dec. 31 +Second star on the right, and onward 'till dawn +Secret to long life and prosperity: Don't spin too fast. +See side panel for exciting recipe ideas. +Semper Non Sequitur! +Sha-ZAM sirrah! +She has a really opulent body; just ripe for dissecting. +She's a virgin child with deviant dreams. +Shoes for industry! +Sin that pays its way can travel freely, and without a passport; whereas virtue, if a pauper, is stopped at all frontiers. +Sinister Dexter has a broken spirometer. +Sir, you have tasted two whole worms; you have hissed all my mystery lectures and been caught fighting a liar in the quad; you will leave Oxford by the next town drain. +Sleep is nature's way of telling you to go to bed. +Small change can often be found under seat cushions. +Smile or I'll kill you. +Smile, and the whole world thinks you're laughing at them. +SnaKES! snakES... SnakeS... SnAKes! +So close, no matter how far... couldn't be much more from the heart. Forever trusting who we are, and nothing else matters. +So long, and thanks for all the fish. +So... so you think you can tell... heaven from hell +Some girls are bigger than others. +Some men are discovered; others are found out. +Sometimes I become spell-bound in the middle of Wal-Mart. +Sometimes a cigar is just a cigar. +Spacetime isn't curved, it's positively bent. +Spam was, Spam is and Spam shall be. After summer is winter, and after winter, summer. It ruled once where Man rules now; where Man rules now, it shall rule again. As a foulness shall ye know it. +Specialization is for insects. +Stability itself is nothing else than a more sluggish motion. +Stamp out the post office! Mail electronically. +Standing on head makes smile of frown, but rest of face also upside down. +Stop searching forever. Happiness is just next to you. +Stop searching forever. Happiness is unattainable. +Stop! I'm a girl whose fingernails are made of a mother's pearl... +Strike any user to continue. +Study demonology with an enemy this Sunday. +Sturgeon's Law: Ninety percent of everything is crud. +Sum id quod sum et id totum est quod sum. +Support bacteria. They're the only culture some people have. +Sure I believe in peace. Peace through superior firepower. +Surrealism aims at the total transformation of the mind and all that resembles it. +TEACHERS do it repeatedly +TWISTER FOR MONEY, TWISTER FOR BLOOD! +Take care of the cojones and the frijoles will take care of themselves. +Take care of the luxuries and the necessities will take care of themselves. +Take no prisoners, we can't feed them. +Take the skin and peel it back... now doesn't it make you feel better? +Take thee this thing covered with that stuff and give it unto that guy, that he may do things with it. +Telecommunications is a bit far fetched. +Thaaaaaat's niiiiiice. +Thank you for shopping at S-Mart. +That be some righteous dub mon. +That must be wonderful! I don't understand it at all. +That which does not kill me had better run pretty damn fast. +That which is not forbidden is mandatory. +That's what she said. +The Bible contains six admonishments to homosexuals and three hundred sixty-two admonishments to heterosexuals. That doesn't mean that God doesn't love heterosexuals. It's just that they need more supervision. +The Buck stops here, the Doe just visits.. +The Californians are an idle thriftless people, and can make nothing for themselves. +The Czech's in the mail. Sending Frenchman by FAX. +The Delta-United Ring Formation Theory states that the rings of Saturn are composed entirely of lost airline luggage. +The Earth Will Shake. +The Eleventh Commandment: Thou shalt not get caught. +The Greasy Spoon: Chris Coe +The Heirophant is Disguised and Confused. +The Jetdillo Project: 2000 pounds of steel, 20,000 pounds of thrust. One pair of very dark shades. The world's first fully cybernetic, SSTO-capable armadillo. Coming soon from Armadillo Labs. The Dasypian Future begins tommorrow... +The Rule of Fives states that all things happen in fives, or are divisible by or are multiples of five, or are somehow directly or indirectly related to five. The Rule of Fives is never wrong. +The backbone of this country is the independent trucks. +The best teddy bears are the live kind. +The charms of a woman are directly related to the speed of her passing. +The classical Greeks were not influenced by the classical Greeks. +The concept is simply staggering. Pointless, but staggering. +The decision doesn't have to be logical, it was unanimous. +The denizens of this sizzling-hot, freezing-cold, rocky, rotten island were monsters -- huge or miserably stunted, fat or scraggly, dry or slimy, with scales, warts, pimples, tentacles, talons, fangs, extra arms, eyes, legs, tails, and even heads, all in +The door is the key. +The early worm gets the bird. +The first step to a person's heart is to confuse the fuck out of 'em. +The following statement is not true: +The future ain't what it used to be. +The hipbone's connected to the... jawbone? +The honest way to money is through aardvark. +The important thing is not to stop questioning. +The man who makes no mistakes does not usually make anything. +The meek will inherit the earth; the rest of us will go to the stars. +The moral of the story is: Kill the parents kill the children. +The more it stays the same, the less it changes. +The more people I meet, the more I love my battleaxe. +The most useful tool for dealing with management types is, of course, an automatic weapon. +The mutant alien skull man has a smile for all smiles, an answer for all answers. +The next person to pass us will die within a fortnight. +The only difference between a rut and a grave is their dimensions. +The only dumb question is one you haven't asked yet. +The only good Smurf is a dead Smurf. +The only real problem in life is what to do next. +The only way to amuse some people is to slip and fall on an icy pavement. +The optimist thinks this is the best of all possible worlds. The pessimist is afraid that it is. +The plural of spouse is spice. +The president's weird. He's got a burgundy beard. It's a ripoff. +The problem with troubleshooting is that real trouble shoots back. +The quickest way to a man's heart is through his ribcage. +The rain, it raineth on the Just and the Unjust fella. But chiefly on the Just because the Unjust steals the Just's umbrella. +The rarity of genuine feeling in human society sometimes makes me stop in the street to watch a dog gnawing a bone. +The reason people get lost in thought is because it is, to many, rather unfamiliar territory. +The secret of the universe is @*&^^^ NO CARRIER +The three greatest men who ever lived were Eleanor Roosevelt. +The time for bickering has past. Now is the time to drop our gloves. +The truth of a proposition has nothing to do with its credibility. +The two most common things in the universe are hydrogen and stupidity. +The universe is laughing behind your back. +The universe is like a grapefruit -- it's yellow and dimply, and some people have half of one for breakfast. +The wind is loud and sometimes pungent. +The wind is plowed and sometimes sturgeon. +The wise shepherd never trusts his flock to a smiling wolf. +The world is coming to an end. Please log off. +The world laughs at Bill Clinton. +The world owes all its onward impulses to men ill at ease. The happy man confines himself within ancient limits. +There ain't no justice +There are three magical words that make everyone feel happy... 'no compilation errors'. +There are trivial truths and there are great truths. The opposite of a trivial truth is plainly false. The opposite of a great truth is also true. +There are two major products that come out of Berkeley; LSD and BSD Unix. We don't believe this to be a coincidence. +There is a skeleton in every old house. +There is no "A" in "KERNEL"! +There is no heavier burden than a great potential. +There is no time like the pleasant. +There were Arabs with knives at the foot of the bed. +There's a party in my mind, and I can never leave. +There's nothing wrong with you a shotgun blast to the face couldn't fix. +There's someone in my head, but it's not me +Therefore, knowing that you are merely human, do not assign yourself merit. +These are the pale deaths which men miscall their lives; for all the scents of green things growing, each breath is but an exhalation of the grave. Bodies jerk like puppet corpses, and hell walks laughing -- +They are a puckish band of rapscallions +They just buzzed and buzzed.....buzzed. +Think "HONK" if you're a telepath! +This is my steakhouse. +This is precisely the sort of thing that people who like this sort of thing will like. +This is the most amazing loaf of bread I've ever owned. +This is where I get off... +This side up. +This statement is false. +This statement is false. +This town needs an enema. +Those students who have become one with the universe will be allowed to go on and become two with the universe. +Those who can, do; those who can't, simulate. +Those who play with the Stone get screwed by the Stone. +Thou shalt not misspell thine neurotransmitters. +Though it all looks different now, I know it's still the same... +Tiger Tim the Tightrope Walker, tried to steal the drag queen's daughter. +Time Warner: bringing you the finest in cradle-to-grave thought management. +Time flies like an arrow; fruit flies like a banana. +Time is a great teacher, but it kills all its pupils. +Time is a plaything for children and fools. +Time is a spiral, space is a curve, I know you get dizzy but try not to lose your nerve +Time is an illusion perpetrated by the manufacturers of space. +Time is an illusion. Lunchtime doubly so. +Time is but the stream I go a-fishing in. +Time is nature's way of making sure that everything doesn't happen at once. +To be happy living in this world, there are sides of the soul one must entirely paralyze. +To criticize the incompetent is easy; to criticize the competent is difficult. +To err is human; to forgive is not company policy. +To have too much and not enough is like a boat person with sideburns. +Today is a good day to bribe a high ranking public official. +Today is an excellent day to become a missing person. +Today is the first day of the rest of my death. Tommorrow will be the next day of it. Bwwaahh. +Tongue-tied and twisted, just an earthbound misfit, I. +Tony Orlando and DAWN! +Troglodytism does not necessarily imply a low cultural level. +Troubles are like babies; they only grow by nursing. +Truly great madness cannot be achieved without significant intelligence. +Truly, the road to enlightenment is like unto half a mile of broken glass. +Trust I seek and I find in you, everyday to eat something new. +Try enjoying yourself. Everyone else has. +Try the Moo Shu Pork. It is especially good today. +Tyger, tyger, burning bright, in the forests of the night; what immortal hand or eye could frame thy fearful symmetry? +Uh, Josh, you have to do a QC before you say "go away" for it to be a banishing. +Um. No. It's not mayonnaise. +Until, one by one... HE DESTROYED THEM ALL! +Use no hooks. +Use only as directed. +Useless Invention: Flame-retardant gasoline. +Vacuum packed for freshness. +Vee off ze KGB are not ez slow ez you tink, comrade. +Vegetables are murder. +Virginity can be cured. +WARNING! COLDBEER.CAN found, programmer probably loaded. +WARNING: The vomitous nature of certain sequences may be emotionally hazardous for the squeamish or those with weak hearts. +WINDOWS MULTITASKS! (in a DesqView window) +Wake up, the whole world's gone... +Walk in shadow, walk in dread, Loosefish walk as like one dead +Was it worth it? +Was uns nicht umbringt macht mir bis 1500 naechsten nachmittag schlafen. +Watch me as I swing my hips... around. +We all know that no one understands anything that isn't funny. +We are all Children of Cthulhu -- especially the ones with lots of tentacles. +We are sorry, you have reached an imaginary number. Please rotate your phone ninety degrees and try again. +We are what we pretend to be, so we must be careful what we pretend to be. +We asked for Mojo Nixon, they said "He don't work here" +We come in peace... shoot to kill... +We could do that, but it would be wrong, that's for sure. +We don't hate vegetarians, we just think they're funny. +We don't morally censure you. We just want the money. +We don't take no shit from a machine. +We find the defendant innocent by reason of being generally clueless. +We intend to destroy all dogmatic verbal systems. +We learn from history that we do not learn anything from history. +We should build an Intel processor out of penguins. +We're gonna go to the mall and window shoplift... +We're just two lost souls swimmin' in a fishbowl, year after year +We're not doing scientific research, we're killing people. +Weather's here, wish you were beautiful. +Wedding rings: the world's smallest handcuffs. +Welcome to Borger King. Your way will be assimilated. +Welcome to the Federal Bureau for Reducing Bureaucracy! +Well, Katrina likes it. +Well, it looks like blind, screaming hedonism won out. +Whadya know? Not much, you? +What a useless scroll, all it says is "Hastur Hastur Hastur" over and over again. +What do you call a well-hung bear? Genital Ben. +What do you get if you cross an elephant with a grape? |grape| * |elephant| * sin(theta). +What do you want me to do, learn to stutter? +What garlic is to salad, insanity is to art. +What happens when you cut back the jungle? It recedes. +What is worth doing is worth the trouble of asking somebody to do it. +What poor gods we do make. +What rolls down T3s, boosts connect fees, and makes your throughput drag? Makes it tough to hack, won't get off your back, it's lag lag lag! +What's blue and square? An orange in disguise... +What's so funny 'bout Peace, Love, and Understanding? +Whatever happened to Rodney Allen Rippy? Check your Jumbo Jack. +When Cthulhu calls, he calls collect. +When I die, I want to go peacefully in my sleep like my father did, not screaming in terror like his passengers. +When in doubt, fuck it. When not in doubt... get in doubt! +When life gives you a lemon, say 'Lemons? I like lemons. What else have you got?' +When the going gets weird, the weird turn pro. +When the naive man admits his naivete, he is no longer naive. Thus, all people are regarded by society as either ignorant or a liar. +When we talk about property, state, masters, government, laws, courts, and police, we say only that we don't want any of them. +When you have had all that you can take, put the rest back. +When you reach the crossroads, take it. - Yogi Berra +When you're a god, you don't have to have reasons. +Whenever I hear the word "culture", it makes me want to reach for my revolver. +Where is the ANY key when you want it. +Which caused little rhythmic darts of red to pulsate gently in the morning sun. +Which part of the chicken is the noodle from? +Who is W. O. Baker, and why is he saying those terrible things about me? +Who loathes you, and who do you love? CTHULHU! +Why ask why, just shut up and die +Why is the cheese moving? +Win a live rat for your mother-in-law! +With a name like Mangled Baby Ducks, you know it's gotta be good! +Wonders never cease, as long as you never cease to wonder. +Wouldn't it be wonderful if everyone renounced violence forever? I could then conquer the whole stupid planet with just a butter knife. +Wow, man... like, totally Hidden Valley Ranch, dude. +Xyzzy doesn't mean shit. +YOU ALL FUCING SUCK +YOU kill it -- I'm bitter. +Yesterday a postal worker asked me what type of handgun he should buy. +Yield to oncoming traffic. +Yield to temptation; it may not pass your way again. +Yo! Ewige Blumenkraft, Dude! +You DON'T want me to translate that. No, really you don't. +You can never trust a woman; she may be true to you. +You can't be late until you show up. +You can't lick the system, but you can certainly give it a damn good fondling... +You cannot lie to me. I know everything. +You don't have a disease. You just live in New Jersey. +You grok? +You have been selected for a secret mission. +You killed Ted, you medieval dickweed! +You know it's a bad morning when you get up out of bed and miss the floor. +You know what I always say... +You know what, Stuart? I like you. You're not like the other people, here, in the trailer park. +You know, that's a pretty daring move for a weatherman. +You laugh just like the angles dancing on the head of the pin jabbed into my mind's eye. +You live and learn. Or you don't live long. +You lookin' at me? You lookin' at me?! +You may be redneck if people ask permission to hunt in the front yard. +You may be redneck if you've ever filled a deer tag on a golf course. +You mean you need drugs to hallucinate? +You now have Asian Flu. +You see, without that little doohicky, the universe stops. +You took my breath away and now I want it back. +You will find that the State is the kind of organization which, while it does big things badly, does small things badly too. +You wouldn't be so smug if you really knew what was going on. +You'll be the first ones up against the wall when the revolution comes. +You're a jerk, Dent. A complete kneebiter. +You're about as subtle as a tank. +You're not really drunk if you can lie on the floor without hanging on. +You're so hardcore, you're punk rock. +You're twisted, depraved, and rotten to the core... I like that in a person. +You've caught an even atom tan +You, you, you shouldn't, you shouldn't traumatize women with sexual intercourse. I should know, I'm a medieval doctor, I own a mansion and a yacht. +Your ignorance cramps my conversation. +Your mission, should you choose to accept it, is to flallop about wildly. +Your mother wears combat boots. But I like combat boots. +Your weakness is money in their bank. +_The Big U_ and _Snow Crash_ both center around cerebral bicamerality... but is that because Neal Stephenson has a Jaynes addiction? +pulled out a pair of pliers and pulled a bullet out of my chest +reality.sys corrupted. universe halted. reboot (y/n)? +ridiculous arrangements. +so let's have a drink." +werd,..... i love you.. diff --git a/messages/signoff.txt b/messages/signoff.txt new file mode 100644 index 0000000..25fd09c --- /dev/null +++ b/messages/signoff.txt @@ -0,0 +1,74 @@ +'The Geek shall inherit the earth.' - Linus 5:5 +665.9238429876 - Number of the Pentium Beast +A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1... +A hacker does for love what others would not do for money. +A program is a device used to convert data into error messages. +A)bort, R)etry, I)nfluence with large hammer. +ACK and you shall receive. +AFX Analogue Bubblebath +APATHY ERROR: Don't bother striking any key. +Acid Resistant is great +Adding manpower to a late software project makes it later. +All wiyht. Rho sritched mg kegtops awound? +Application has reported a 'Not My Fault' in module KRNL.EXE in line 0200:103F +As a computer, I find your faith in technology amusing. +Bad command or file name. Bad, bad command! Sit! Stay! Staaaay... +Behind every good computer -- is a jumble of wires 'n stuff. +C Programmers do it recursively. +C.O.B.O.L - Completely Obsolete Boring Old Language. +Calculating in binary code is as easy as 01,10,11. +Computer Science: solving today's problems tomorrow. +Computers make very fast, very accurate mistakes. +Core Dump, The shit has been purged +Drag me, drop me, treat me like an object! +Eh, damn dog always shits in my yard +Enter any 11-digit prime number to continue... +Epigram: Ada is the 400-pound gorilla of programming languages. +Error 69: Excess Orgasm +Error 86: Keyboard not found, press any key to continue +Error 95: Bad user input, replace user and try again +Error: Keyboard not attached. Press F1 to continue. +Error: Sector not found -- search behind couch? (Y/N) +Excess Flood Just Kidding +Excuse me for butting in, but I'm interrupt-driven... +File not found. Should I fake it? (Y/N) +Finish the project. We'll buy you a new family. +God is real... unless declared an integer. +Hey! It compiles! Ship it! +I feel like a genocidal maniac when emacs asks me if I want to kill 10789 characters. +I thought I had a back-up, but she refused to type it in again. +ICMP: The protocol that goes PING! +If I were an Apple I wouldn't be here +Intel: We put the 'um...' in Pentium. +Life's unfair - but root password helps! +Linux renders ships, NT is rendering ships useless. +Linux: Because rebooting is for adding new hardware. +Madcows on the loose, mooooo mooooo +Marketer to coder: "You start coding. I'll go find out what they want." +Mountain Dew and doughnuts... because breakfast is the most important meal of the day. +My operat~1 system unders~1 long filena~1 , does yours? +My software never has bugs. It just develops random features. +PCMCIA - People Can't Memorise Computer Industry Acronyms +Ping timeout Just Kidding +Press any key to continue or any other key to quit... +Programmers never die: They just GOSUB without RETURN. +RTFM: Not just an acronym, it's the LAW! +Real programmers don't document. If it was hard to write, it should be hard to understand. +Real_men_don't_need_spacebars. +Remaining time multiplied by distress is constant. +Say hello to Mr. MookaHuka for me +Segmentation Fault, bad fart +Sex, It's whats for dinner +Sleep: A completely inadequate substitute for caffeine. +Software isn't released, it's allowed to escape. +Standards are industry's way of codifying obsolescence. +The programmer's national anthem is 'AAAAAAAARRRRGHHHHH!!'. +The world is coming to an end... SAVE YOUR BUFFERS!! +There are two ways to write error-free programs. Only the third one works. +Three things are certain: Death, taxes, and lost data. Guess which has occurred... +To know recursion, you must first know recursion. +Universe.SYS corrupted. Reboot? [Y/N] +Unix, BASIC, C, PASCAL, APL, ADA, and PROFANITY spoken here. +VI VI VI The editor of the beast. +Whip me. Beat me. Make me maintain AIX. +vanishing diff --git a/messages/version.txt b/messages/version.txt new file mode 100644 index 0000000..3642951 --- /dev/null +++ b/messages/version.txt @@ -0,0 +1,51 @@ +BitchX-1.0c15+ by panasync - Linux 2.2.16 : Keep it to yourself! +BitchX-1.0c16+ by panasync - FreeBSD 4.1-STABLE : Keep it to yourself! +BitchX-75p2-6+ by panasync - IRIX 6.5 : Keep it to yourself! +2.3b47 US for the Power Macintosh 6100/60 #75. Reg. #00000000 +Ircle 3.0.4 US PPC 02/08/1999 03:40:04 AM. #267E45E342E DB32E036 +MacIRC v0.71a by WindWalkr +Microsoft Chat 2.5 (4.71.2302) (text mode) +PIRCH32:WIN 95/WIN NT:Beta Version 0.92:97.01.11 +PIRCH98:WIN 95/98/WIN NT:1.0 (build 1.0.1.1190) +PLuToNiuM's IRC Client - Accept no imitations :You gotta keep 'em seperated +PLuToNiuM's IRC Client - Accept no imitations :*** LiCe(v1.3pl1) By =:SrFrog:= (DurandalSK2.00) +PLuToNiuM's IRC Client - Accept no imitations :Sod off, fucknuts. +Running [PwR] PoWeR.irc 1.0 +Running PhoEniX v2.33 by Vassago +Running Phoenix v2.27 by Vassago +Running KVirc 2.0.0 'Phoenix' by Szymon Stefanek +Running KVirc 2.0.0 'Phoenix' by Szymon Stefanek, RealityX IRC 1.0.0 +VERSION - unknown command. +WSIRC 1.14e-G - CopyRight 1994 Caesar M Samsi csamsi@clark.net TEXT CHANNEL +X-Chat 1.2.1 By ZED - Linux 2.2.14 [i686/334MHz]: http://xchat.org +X-Chat 1.2.1 By ZED - Linux 2.2.16 [i386/301MHz]: http://xchat.org +Zircon 1.16 Pl: 0 *IX : tcl 7.4p2 tk 4.0p2 tclX V7.4a patch 1 tclX V7.4a patch 1 +[TextBox] version 6.21 by Crypt Keeper / -+ TEXT -+ +iNFeSTeD WiTH LiCe v1.2pl4 by =SrFrog= +ircII 2.2.9 *IX :ircII 2.2.7, SL0 Are we bug free yet? +ircII 2.8+PLuToNiuM.r1 SunOS 5.4 :You gotta keep 'em seperated +ircII 2.8+PLuToNiuM.r2 Linux 1.3.75 :/kill me! +ircII 2.8.2 *IX :ircii 2.8: almost there... +ircII 2.8.2 IRIX 5.3 :*** LiCe(v1.3pl1) By =:SrFrog:= (aigh) +ircII 2.8.2 Linux 1.3.75 :ircii 2.8.2: almost there... +ircII 2.8.2+CToolZ_v2.0R03.17.96 + cDCC_v1.2 Linux 1.2.13 : I'm Da Lamest Gangsta +ircII 2.8.2+CoMSTuDr6 BSD/386 1.1 :The ULTIMATE Client +ircII 2.8.2-EPIC3.004 SunOS 5.5.1 - All the things phone wont include..... +ircII 3.1 XLCD121 SunOS 4.1.3 :ircii 2.6: you are the perfect drug... +ircII EPIC4pre2.003 Linux 2.2.16 - whitecaps toy +ircII EPIC4pre2.508 Linux 2.2.16 - Accept no limitations. +ircle 2.2 for the Power Macintosh (#69) by Onno Tijdgat +ircle 2.2 for the Power Macintosh (#75) by Onno Tijdgat +mIRC v3.9 Windows IRC Client K.Mardam-Bey +mIRC16 v3.8 IRC Client K.Mardam-Bey +mIRC16 v3.92 IRC Client K.Mardam-Bey +mIRC32 v5.41 K.Mardam-Bey +mIRC32 v5.5 K.Mardam-Bey +mIRC32 v5.51 K.Mardam-Bey +mIRC32 v5.6 K.Mardam-Bey +mIRC32 v5.61 K.Mardam-Bey +mIRC32 v5.7 K.Mardam-Bey +mIRC32 v5.71 K.Mardam-Bey +rxIRC 2.0 VM/CMS :Survival package for VM-struck humans +xc! 1.4.0 Linux 2.2.14-5.0smp [i686/548MHz]: http://xchat.org +xchat 1.5.5 FreeBSD 4.1-RELEASE [i386] diff --git a/sample.conf b/sample.conf new file mode 100644 index 0000000..118f65b --- /dev/null +++ b/sample.conf @@ -0,0 +1,115 @@ +; +; server connection timeout limit = 60 seconds +; + +set ctimeout 60 + +; +; add some servers to the serverlist +; + +servergroup undernet +server amsterdam.nl.eu.undernet.org 6663 +server amsterdam2.nl.eu.undernet.org 6663 +server bucharest.ro.eu.undernet.org 6663 +server carouge.ch.eu.undernet.org 6663 +server ede.nl.eu.undernet.org 6669 +server elsene.be.eu.undernet.org 6669 +server fulda.de.eu.undernet.org 6663 +server geneva.ch.eu.undernet.org 6663 +server graz.at.eu.undernet.org 6663 +server graz2.at.eu.undernet.org 6663 +server helsinki.fi.eu.undernet.org 6669 +server lelystad.nl.eu.undernet.org 6668 +server london.uk.eu.undernet.org 6666 +server london2.uk.eu.undernet.org 6663 +server milan.it.eu.undernet.org 6663 +server oslo1.no.eu.undernet.org 6663 +server oslo2.no.eu.undernet.org 6663 +server stockholm.se.eu.undernet.org 6669 +server surrey.uk.eu.undernet.org 6669 +server zagreb.hr.eu.undernet.org 6666 +server panamacity.pa.undernet.org 6663 +server ashburn.va.us.undernet.org 6663 +server fairfax.va.us.undernet.org 6666 +server mesa.az.us.undernet.org 6666 +server miami.fl.us.undernet.org 6669 +server princeton.nj.us.undernet.org 6663 +server sanjose.ca.us.undernet.org 6663 +server sterling.va.us.undernet.org 6669 + +; +; this is sorta how a botnet configuration would look like... +; + +set linkport 5000 +set linkpass your_lame_password +set autolink 1 + +; +; what other bots are out there in cyberspace? +; +; link [host port] +; +; guid the other bots guid +; password the other bots password +; host internet host to connect to when linking the other bot +; port tcp port to connect to on remote host +; + +link 2 janes_password janes.host.com 5000 ; Jane Does EnergyMech +link 3 johns_password johns.host.com 5000 ; John Does EnergyMech + +; +; we cant connect -to- bot 4, but we can accept connections from it! +; + +link 4 ongabonga_pass + +; +; thats all the botnet stuff for now +; + +; +; create a new bot +; +; 1 the globally uniqe ID (guid), used for botnet linking +; emech3 the bots nick +; + +nick 1 emech3 +set altnick myemech3 monkey3 dummy3 fuzzy3 + +; +; set connection variables for this bot +; + +set userfile mech.passwd +set ident robot +set ircname EnergyMech 3: The Next Generation +set umodes +w +set cmdchar . +set modes 6 +set cc 1 + +; +; join a channel +; + +join #anychannel +set pub 1 +set aop 1 +set avoice 1 +set prot 4 + +; +; this is how you'd load a tcl on startup... +; + +tclscript does-not-exist.tcl + +; +; Can you guess? +; + +die You did not edit your configuation file! diff --git a/sample.py b/sample.py new file mode 100644 index 0000000..5bbf8e9 --- /dev/null +++ b/sample.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# +# Example python script +# + +import emech # you want this! + +def myCallback(strFrom, strRest): # requires two arguments + print( "from = " + strFrom ) + print( "rest = " + strRest ) + parts = strRest.split(' ') + print( "to = " + parts[0] ) + print( "text = " + parts[1][1:] ) + print( "currentnick = " + emech.currentnick() ) + print( "botnick = " + emech.botnick() ) + print( "wantnick = " + emech.wantnick() ) + print( "userhost = " + emech.userhost() ) + #print( "server = " + str(emech.server()) ) + print( "nextserver = " + str(emech.nextserver()) ) + print( "currentchan = " + emech.currentchan() ) + print( "userlevel = " + str(emech.userlevel(strFrom)) ) + emech.to_server("PRIVMSG #emech :Hello world !\n") + emech.debug("Debugging myself!") + #emech.sendfile('sample.py', strFrom) + return emech.OK # you must return this or emech.ERROR + +def myTimerCallback(strFrom, strRest): + print( "Timer called back!" ) + print( strFrom ) + print( strRest ) + +# create a parse hook: +emech.hook(emech.HOOK_PARSE, "PRIVMSG", myCallback) +# create a timer hook: +emech.hook(emech.HOOK_TIMER, "30 0 0 0", myTimerCallback) # in 30 sec diff --git a/sample.tcl b/sample.tcl new file mode 100644 index 0000000..84370b0 --- /dev/null +++ b/sample.tcl @@ -0,0 +1,40 @@ +# +# the most lame of all scripts... +# + +# +# to_server "" [number] +# +# text what to send to server (remember to include a newline) +# number max number of lines in the sendq +# +# * if number is not specified, the line will be sent immediately to the current bots server socket. +# to_server returns the number of bytes written or -1 on an error +# (in which case the server socket will have been closed) +# +# * if number is zero (0), the line will be added to the sendq for the current server socket. +# to_server returns the number of lines in the sendq +# +# * if number is a positive integer, the line will only be added to the sendq if the number +# of lines in the sendq is smaller than the given number. +# to_server returns the number of lines in the sendq +# +# ! when sending lines directly (number not specified), a newline must be supplied with the buffer. +# when sending through the sendq, the newline is not needed +# + +proc grab_notice {from rest} { + global mech_currentnick mech_version mech_srcdate mech_class mech_guid mech_nick mech_wantnick mech_server mech_nextserver mech_currentchan + to_server "PRIVMSG $mech_currentchan :($from) $rest\n" 50 + to_server "PRIVMSG $mech_currentchan :$mech_currentnick $mech_version $mech_srcdate $mech_class $mech_guid $mech_nick $mech_wantnick $mech_server $mech_nextserver $mech_currentchan\n" 50 + return 0 +} + +# +# parse_hook +# +# command command received from the server (for example, PRIVMSG, NOTICE, KICK, MODE, ...) +# handler name of the tcl procedure to call with the server input + +hook parse NOTICE grab_notice + diff --git a/sample.userfile b/sample.userfile new file mode 100644 index 0000000..2124064 --- /dev/null +++ b/sample.userfile @@ -0,0 +1,4 @@ +user example +mask *!*@*.example.com +chan * +opt u100 diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..f93cb55 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,222 @@ +# +# EnergyMech, IRC bot software +# Copyright (c) 1997-2009 proton +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +INSTALLNAME = energymech +INSTALLDIR = .. +INSTALLMODE = 0700 + +LDSCRIPT = @ldscript@ +CPROF = @cprof@ +LPROF = @lprof@ +LIBS = @libflags@ +PIPEFLAG = @pipeflag@ +GDBFLAG = @gdbflag@ +WARNFLAG = @W_FLAGS@ +OPTIMIZE = @O_FLAGS@ +PYINCLUDE = @PYINCLUDE@ + +CFLAGS = $(PIPEFLAG) $(GDBFLAG) $(WARNFLAG) $(OPTIMIZE) +LFLAGS = $(PIPEFLAG) $(GDBFLAG) + +CC = @CC@ +MV = mv -f +RM = rm -f +CHMOD = chmod + +INCS = config.h mcmd.h defines.h global.h h.h structs.h text.h + +OFILES = alias.o auth.o bounce.o chanban.o channel.o core.o \ + ctcp.o debug.o dns.o dynamode.o function.o greet.o help.o irc.o \ + kicksay.o main.o net.o net_chan.o note.o notify.o ons.o parse.o \ + perl.o prot.o python.o redirect.o reset.o seen.o shit.o socket.o \ + spy.o stats.o tcl.o telnet.o toybox.o trivia.o uptime.o \ + user.o vars.o web.o @MD5_O@ + +SRCFILES = alias.c auth.c bounce.c chanban.c channel.c core.c \ + ctcp.c debug.c dns.c dynamode.c function.c greet.c help.c irc.c \ + kicksay.c main.c net.c net_chan.c note.c notify.c ons.c parse.c \ + perl.c prot.c python.c redirect.c reset.c seen.c shit.c socket.c \ + spy.c stats.c tcl.c telnet.c toybox.c trivia.c uptime.c \ + user.c vars.c web.c + +all: $(INSTALLNAME) + +mcmd.h: gencmd.c config.h structs.h ; + $(CC) $(LFLAGS) -o gencmd gencmd.c + ./gencmd > mcmd.h + +install: $(INSTALLNAME) + $(CHMOD) $(INSTALLMODE) $(INSTALLNAME) + $(MV) $(INSTALLNAME) $(INSTALLDIR) + +clean: FORCE + $(RM) $(INSTALLNAME) gencmd mcmd.h core $(OFILES) + +$(INSTALLNAME): $(OFILES) + $(CC) $(LFLAGS) -o $(INSTALLNAME) $(OFILES) $(LPROF) $(LIBS) $(LDSCRIPT) +@oc@ objcopy -R .note -R .comment $(INSTALLNAME) +@sz@ size $(INSTALLNAME) + +mega: $(SRCFILES) $(INCS) usage.h + $(CC) $(CFLAGS) -o $(INSTALLNAME) mega.c $(LPROF) $(LIBS) $(LDSCRIPT) $(PYINCLUDE) +@oc@ objcopy -R .note -R .comment $(INSTALLNAME) +@sz@ size $(INSTALLNAME) + +# +# static targets +# + +$(INSTALLNAME)-static: $(OFILES) + $(CC) $(LFLAGS) -o $(INSTALLNAME) $(OFILES) $(LPROF) $(LIBS) $(LDSCRIPT) -static +@oc@ objcopy -R .note -R .comment $(INSTALLNAME) +@sz@ size $(INSTALLNAME) + +mega-static: $(SRCFILES) $(INCS) usage.h + $(CC) $(CFLAGS) -o $(INSTALLNAME) mega.c $(LPROF) $(LIBS) $(LDSCRIPT) $(PYINCLUDE) -static +@oc@ objcopy -R .note -R .comment $(INSTALLNAME) +@sz@ size $(INSTALLNAME) + +# +# +# + +alias.o: alias.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +auth.o: auth.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +bounce.o: bounce.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +chanban.o: chanban.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +channel.o: channel.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +core.o: core.c $(INCS) settings.h + $(CC) $(CFLAGS) -c $< $(CPROF) + +ctcp.o: ctcp.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +debug.o: debug.c $(INCS) settings.h + $(CC) $(CFLAGS) -c $< $(CPROF) $(PYINCLUDE) + +dns.o: dns.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +dynamode.o: dynamode.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +function.o: function.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +greet.o: greet.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +help.o: help.c $(INCS) usage.h + $(CC) $(CFLAGS) -c $< $(CPROF) + +irc.o: irc.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +kicksay.o: kicksay.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +main.o: main.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) $(PYINCLUDE) + +net.o: net.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +net_chan.o: net_chan.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +note.o: note.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +notify.o: notify.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +ons.o: ons.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +parse.o: parse.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +perl.o: perl.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +prot.o: prot.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +python.o: python.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) $(PYINCLUDE) + +redirect.o: redirect.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +reset.o: reset.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +seen.o: seen.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +shit.o: shit.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +socket.o: socket.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +spy.o: spy.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +stats.o: stats.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +tcl.o: tcl.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +telnet.o: telnet.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +toybox.o: toybox.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +trivia.o: trivia.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +uptime.o: uptime.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +user.o: user.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +vars.o: vars.c $(INCS) settings.h + $(CC) $(CFLAGS) -c $< $(CPROF) + +web.o: web.c $(INCS) + $(CC) $(CFLAGS) -c $< $(CPROF) + +md5/md5.o: md5/md5.c $(INCS) + $(CC) $(CFLAGS) -c $< -o $@ -Imd5 $(CPROF) + +FORCE: diff --git a/src/alias.c b/src/alias.c new file mode 100644 index 0000000..28ca734 --- /dev/null +++ b/src/alias.c @@ -0,0 +1,196 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define ALIAS_C +#include "config.h" + +#ifdef ALIAS +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +void afmt(char *copy_to, const char *src, const char *input) +{ +#define BUFTAIL (tmp+MSGLEN-1) /* avoid buffer overflows */ + char tmp[MSGLEN]; + const char *np; + char *dest; + int n,t,tu,spc; + + dest = tmp; + while(*src) + { +check_fmt: + if (*src == '$') + { + src++; + if (*src == '$') + goto copychar; + tu = t = n = 0; + while(attrtab[(uchar)*src] & NUM) + { + n = (n * 10) + (*(src++) - '0'); + } + if (n) + { + if (*src == '-') + { + tu = n; + src++; + while(attrtab[(uchar)*src] & NUM) + { + t = (t * 10) + (*(src++) - '0'); + } + } + n--; + spc = 0; + for(np=input;*np;) + { + if (*np == ' ') + { + if (!spc) + n--; + spc = 1; + } + else + { + spc = 0; + if (!n) + break; + } + np++; + } + spc = 0; + while(*np) + { + if (*np == ' ') + { + if (!tu || (t && tu >= t)) + goto check_fmt; + if (!spc) + tu++; + spc = 1; + } + else + { + spc = 0; + } + if (dest == BUFTAIL) + goto afmt_escape; + *(dest++) = *(np++); + } + } + goto check_fmt; + } +copychar: + if (dest == BUFTAIL) + goto afmt_escape; + *(dest++) = *(src++); + } +afmt_escape: + *dest = 0; + Strcpy(copy_to,tmp); +} + +/* + * + * associated commands + * + */ + +void do_alias(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + */ + Alias *alias; + char *cmd; + + if (!*rest) + { + /* list all aliases */ + if (!aliaslist) + to_user(from,TEXT_NOALIASES); + else + { + if (dcc_only_command(from)) + return; + to_user(from,"\037Alias\037 \037Format\037"); + for(alias=aliaslist;alias;alias=alias->next) + { + to_user(from,"%-18s %s",alias->alias,alias->format); + } + } + return; + } + + cmd = chop(&rest); + if (!*rest) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + for(alias=aliaslist;alias;alias=alias->next) + { + if (!Strcasecmp(alias->alias,cmd)) + { + Free(&alias->format); + set_mallocdoer(do_alias); + alias->format = Strdup(rest); + to_user(from,"Replaced alias: %s --> %s",cmd,rest); + return; + } + } + set_mallocdoer(do_alias); + alias = (Alias*)Calloc(sizeof(Alias)+strlen(cmd)); + Strcpy(alias->alias,cmd); + set_mallocdoer(do_alias); + alias->format = Strdup(rest); + alias->next = aliaslist; + aliaslist = alias; + to_user(from,"Added alias: %s --> %s",cmd,rest); +} + +void do_unalias(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + CARGS + */ + Alias *alias,**ap; + + for(ap=&aliaslist;*ap;ap=&(*ap)->next) + { + if (!Strcasecmp(rest,(*ap)->alias)) + { + alias = *ap; + *ap = alias->next; + to_user(from,"Removed alias: %s (--> %s)",alias->alias,alias->format); + Free(&alias->format); + Free((void*)&alias); + return; + } + } + to_user(from,"Couldnt find matching alias"); +} + +#endif /* ALIAS */ diff --git a/src/auth.c b/src/auth.c new file mode 100644 index 0000000..f814bdf --- /dev/null +++ b/src/auth.c @@ -0,0 +1,449 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define AUTH_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#ifndef MD5CRYPT + +/* + * simple password encryption + */ + +char pctab[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuwvxuz0123456789+-"; + +#define CIPHER(a1,a2,a3,a4,b1,b2,b3,b4) \ +{ \ + a2 = a2 ^ a1; \ + b2 = b2 ^ b1; \ + a3 = a2 ^ a1; \ + b3 = b2 ^ b1; \ + b3 >>= 2; \ + b3 |= ((a3 & 3) << 30); \ + a3 >>= 2; \ + a2 = a3 ^ a2; \ + b2 = b3 ^ b2; \ + a4 = ~a4 ^ a2; \ + b4 = -b4 ^ b2; \ + a2 = a4 ^ ~a2; \ + b2 = b4 ^ -b2; \ + b1 >>= 1; \ + b1 |= ((a1 & 1) << 31); \ + a1 >>= 1; \ +} + +char *cipher(char *arg) +{ + static char res[40]; + ulong B1a,B2a,B3a,B4a; + ulong B1b,B2b,B3b,B4b; + uchar *ptr; + ulong R1; + int i; + + if (!arg || !*arg) + return(NULL); + + B1a = B2a = B3a = B4a = 0; + B1b = B2b = B3b = B4b = 0; + ptr = arg; + + while(*ptr) + { + R1 = *ptr; + for(i=0;i<8;i++) + { + if (R1 & 1) + { + B1a |= 0x80008000; + B1b |= 0x80008000; + } + R1 >>= 1; + CIPHER(B1a,B2a,B3a,B4a,B1b,B2b,B3b,B4b); + } + ptr++; + } + while((B1a) || (B1b)) + { + CIPHER(B1a,B2a,B3a,B4a,B1b,B2b,B3b,B4b); + } + + for(i=0;i<10;i++) + { + res[i] = pctab[(B4b & 0x3f)]; + B4b >>= 6; + B4b |= ((B4a & 0x3f) << 26); + B4a >>= 6; + } + res[i] = 0; + return(res); +} + +char *makepass(char *plain) +{ + return(cipher(plain)); +} + +int passmatch(char *plain, char *encoded) +{ + return(!Strcmp(cipher(plain),encoded)); +} + +#else /* MD5CRYPT */ + +/* + * use MD5 to hash passwords + */ + +char *CRYPT_FUNC(const char *, const char *); + +char *makepass(char *plain) +{ + char salt[8]; + + sprintf(salt,"$1$%04x",(rand() >> 16)); + return(CRYPT_FUNC(plain,salt)); +} + +int passmatch(char *plain, char *encoded) +{ + char *enc; + + if (matches("$1$????$*",encoded)) + return(FALSE); + enc = CRYPT_FUNC(plain,encoded); + return(!Strcmp(enc,encoded)); +} + +#endif /* MD5CRYPT */ + +/* + * + */ +void delete_auth(char *userhost) +{ + Auth *auth,**pp; + + pp = ¤t->authlist; + while(*pp) + { + if (!Strcasecmp(userhost,(*pp)->nuh)) + { + auth = *pp; + *pp = auth->next; + Free((char**)&auth); + /* + * dont return yet, there might be more auths + */ + continue; + } + pp = &(*pp)->next; + } +} + +void remove_auth(Auth *auth) +{ + Auth **pp; + + pp = ¤t->authlist; + while(*pp) + { + if (*pp == auth) + { + *pp = auth->next; + Free((char**)&auth); + /* + * when removing a DCC/telnet auth the connection should also be removed + */ + return; + } + pp = &(*pp)->next; + } +} + +/* + * register nick-changes in auths + */ +void change_authnick(char *nuh, char *newnuh) +{ + Auth *auth,*oldauth,**pp; + char *n1,*n2; + + pp = ¤t->authlist; + while(*pp) + { + auth = *pp; + if (!nickcmp(nuh,auth->nuh)) + { + for(n1=nuh;*n1 != '!';n1++); + for(n2=newnuh;*n2 != '!';n2++); + if ((n1 - nuh) >= (n2 - newnuh)) + { +#ifdef DEBUG + debug("(change_authnick) auth->nuh = `%s'; was `%s' (Strcpy) (L1 = %i, L2 = %i)\n", + newnuh,nuh,(int)(n1 - nuh),(int)(n2 - newnuh)); +#endif /* DEBUG */ + Strcpy(auth->nuh,newnuh); + } + else + { +#ifdef DEBUG + debug("(change_authnick) auth->nuh = `%s'; was `%s' (re-alloc)\n",newnuh,nuh); +#endif /* DEBUG */ + /* + * de-link the old auth record + */ + oldauth = auth; + *pp = auth->next; + + set_mallocdoer(change_authnick); + auth = (Auth*)Calloc(sizeof(Auth) + strlen(newnuh)); + auth->user = oldauth->user; + auth->active = now; + auth->next = current->authlist; + current->authlist = auth; + Strcpy(auth->nuh,newnuh); + Free((char**)&oldauth); + } + return; + } + pp = &(*pp)->next; + } +} + +LS User *au_user; +LS char *au_userhost; +LS char *au_channel; +LS int au_access; + +__attr(CORE_SEG, __regparm (1) ) +void aucheck(User *user) +{ + Strp *ump; + + if (au_channel) + { + for(ump=user->chan;ump;ump=ump->next) + { + if (*ump->p == '*' || !Strcasecmp(au_channel,ump->p)) + break; + } + if (!ump) + return; + } + + for(ump=user->mask;ump;ump=ump->next) + { + if (!matches(ump->p,au_userhost)) + { + if (au_access < user->x.x.access) + { + au_access = user->x.x.access; + au_user = user; + } + return; + } + } +} + +User *get_authuser(char *userhost, char *channel) +{ + User *user; + Auth *auth; + + au_user = NULL; + au_userhost = userhost; + au_channel = channel; + au_access = 0; + + /* + * people who are authenticated + */ + for(auth=current->authlist;auth;auth=auth->next) + { + if (au_access < auth->user->x.x.access) + { + if (!Strcasecmp(userhost,auth->nuh)) + { + aucheck(auth->user); + } + } + } + if (current->setting[TOG_ENFPASS].int_var) + return(au_user); + /* + * people who dont need a password + */ + for(user=current->userlist;user;user=user->next) + { + if (!user->pass && (au_access < user->x.x.access)) + { + aucheck(user); + } + } + return(au_user); +} + +int get_authaccess(char *userhost, char *channel) +{ + User *user; + Strp *ump; + + if (userhost == CoreUser.name) + return(100); + if (CurrentDCC && CurrentDCC->user->name == userhost) + { + user = CurrentDCC->user; + if (!channel) + return(user->x.x.access); + for(ump=user->chan;ump;ump=ump->next) + { + if (*ump->p == '*' || !Strcasecmp(ump->p,channel)) + return(user->x.x.access); + } + return(0); + } + if (is_bot(userhost)) + return(BOTLEVEL); + + get_authuser(userhost,channel); + return(au_access); +} + +int make_auth(const char *userhost, const User *user) +{ + Auth *auth; + Chan *chan; + ChanUser *cu; + + for(auth=current->authlist;auth;auth=auth->next) + { + if ((auth->user == user) && !Strcasecmp(auth->nuh,userhost)) + return(TRUE); + } + +#ifdef DEBUG + debug("(make_auth) %s = %s\n",userhost,user->name); +#endif /* DEBUG */ + /* + * there is no matching auth for this user, we add one + */ + set_mallocdoer(make_auth); + auth = (Auth*)Calloc(sizeof(Auth) + strlen(userhost)); + auth->user = (User*)user; + auth->active = now; + Strcpy(auth->nuh,userhost); + + auth->next = current->authlist; + current->authlist = auth; + + for(chan=current->chanlist;chan;chan=chan->next) + { + if (!chan->bot_is_op) + continue; + if (!chan->setting[TOG_AOP].int_var) + continue; + if ((cu = find_chanuser(chan,userhost)) == NULL) + continue; + if (cu->user && cu->user->x.x.aop) + { + send_mode(chan,120,QM_CHANUSER,'+','o',(void*)cu); + } + } + return(FALSE); +} + +/* + * + * authentication commands + * + */ + +/* + * Usage: VERIFY + */ +void do_auth(COMMAND_ARGS) +{ +#ifdef BOTNET + char *checksum; +#endif /* BOTNET */ + Strp *ump; + User *user; + char *pass; + int hostmatch; + + if ((pass = chop(&rest)) == NULL) + return; + + /* + * chop chop + */ + if (strlen(pass) > MAXPASSCHARS) + pass[MAXPASSCHARS] = 0; + + hostmatch = FALSE; + /* + * find a matching password + */ + for(user=current->userlist;user;user=user->next) + { + for(ump=user->mask;ump;ump=ump->next) + { + if (!matches(ump->p,from)) + { + if (user->pass && passmatch(pass,user->pass)) + goto listcheck; + hostmatch = TRUE; + } + } + } + + /* + * if there was a matching host, they get a message... + */ + if (hostmatch) + to_user(from,TEXT_PASS_INCORRECT); + return; + +listcheck: +#ifdef BOTNET + checksum = ""; + if (user->pass) + { + sprintf(gsockdata,"%s %s %s",from,user->name,user->pass); + checksum = makepass(gsockdata); + } + botnet_relay(NULL,"PA%s %s %s\n",user->name,from,checksum); +#endif /* BOTNET */ + if (make_auth(from,user)) + { + to_user(from,"You have already been authorized"); + return; + } + to_user(from,"You are now officially immortal"); +} diff --git a/src/bounce.c b/src/bounce.c new file mode 100644 index 0000000..b19a534 --- /dev/null +++ b/src/bounce.c @@ -0,0 +1,396 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2005 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define BOUNCE_C +#include "config.h" + +#ifdef BOUNCE +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#define INTERNAL_NICK "bounce" +#define INTERNAL_SOURCE ":bounce!bounce@bounce" +#define TEXT_ASK_HANDLE INTERNAL_SOURCE " PRIVMSG %s :" TEXT_ENTERNICKNAME "\n" +#define TEXT_ASK_PASSWORD INTERNAL_SOURCE " PRIVMSG %s :" TEXT_ENTERPASSWORD "\n" +#define TEXT_ASK_SERVER INTERNAL_SOURCE " PRIVMSG %s :Which server would you like to connect to?\n" +#define TEXT_USING_VHOST INTERNAL_SOURCE " PRIVMSG %s :Using virtual host %s\n" +#define TEXT_NOW_CONNECTING INTERNAL_SOURCE " PRIVMSG %s :Now connecting to %s:%i ...\n" + +#define BNC_LOGIN 0 +#define BNC_ASK_HANDLE 1 +#define BNC_ASK_PASSWORD 2 +#define BNC_ASK_SERVER 3 +#define BNC_CONNECTING 4 +#define BNC_ACTIVE 5 +#define BNC_DEAD 6 + +#ifdef IDWRAP + +#define USE_VHOST 2 + +#else /* not IDWRAP */ + +#define USE_VHOST TRUE + +#endif /* IDWRAP */ + +void bounce_parse(ircLink *irc, char *message) +{ + Mech fakebot; + User *user; + char *cmd,*server,*aport,*virtual; + int iport; + + if (irc->status == BNC_LOGIN) + { + cmd = chop(&message); + if (!Strcasecmp(cmd,"USER")) + { + if (irc->userLine) + return; + set_mallocdoer(bounce_parse); + irc->userLine = Strdup(message); + } + else + if (!Strcasecmp(cmd,"NICK")) + { + if (irc->nickLine) + return; + set_mallocdoer(bounce_parse); + irc->nickLine = Strdup(message); + if ((cmd = chop(&message)) == NULL) + { + irc->status = BNC_DEAD; + return; + } + set_mallocdoer(bounce_parse); + irc->nick = Strdup(cmd); + } + if (irc->userLine && irc->nickLine) + { + to_file(irc->usersock,TEXT_ASK_HANDLE,irc->nick); + irc->active = now; + ++irc->status; + } + return; + } + + /* "PRIVMSG bounce :" */ + /* "PRIVMSG bounce :" */ + /* "PRIVMSG bounce : [port [virtual]]" */ + + server = chop(&message); + cmd = chop(&message); + + if (!cmd || Strcasecmp(server,"PRIVMSG") || nickcmp(cmd,INTERNAL_NICK) + || (*message != ':')) + { + irc->status = BNC_DEAD; + return; + } + + message++; + + if (irc->status == BNC_ASK_HANDLE) + { + for(current=botlist;current;current=current->next) + { + if ((user = find_handle(message))) + { + set_mallocdoer(bounce_parse); + irc->handle = Strdup(user->name); + break; + } + } + to_file(irc->usersock,TEXT_ASK_PASSWORD,irc->nick); + ++irc->status; + /* + * dont bomb out even if the username is invalid + * guessing of valid usernames would be possible otherwise + */ + } + else + if (irc->status == BNC_ASK_PASSWORD) + { + irc->status = BNC_DEAD; + for(current=botlist;current;current=current->next) + { + if (irc->handle && (user = find_handle(irc->handle))) + { + if (user->x.x.bounce && user->pass + && passmatch(message,user->pass)) + { + to_file(irc->usersock,TEXT_ASK_SERVER,irc->nick); + irc->status = BNC_ASK_SERVER; + irc->active = now; + return; + } + } + } + } + else + if (irc->status == BNC_ASK_SERVER) + { + server = chop(&message); + aport = chop(&message); + virtual = chop(&message); + + irc->status = BNC_DEAD; + + if (!server) + return; + + iport = 6667; + if (aport) + { + iport = a2i(aport); + if (errno || (iport < 1) || (iport > 65536)) + return; + } + + current = &fakebot; + current->vhost_type = 0; + current->setting[STR_VIRTUAL].str_var = virtual; /* may be NULL, is OK */ +#ifdef WINGATE + current->setting[STR_WINGATE].str_var = NULL; +#endif /* WINGATE */ +#ifdef IDWRAP + current->identfile = NULL; + current->setting[STR_IDENT].str_var = irc->handle; +#endif /* IDWRAP */ + + if (virtual) + { + to_file(irc->usersock,TEXT_USING_VHOST,irc->nick,virtual); + } + to_file(irc->usersock,TEXT_NOW_CONNECTING,irc->nick,server,iport); + if ((irc->servsock = SockConnect(server,iport,USE_VHOST)) >= 0) + { + irc->status = BNC_CONNECTING; + irc->active = now + 60; /* 120 second timeout */ +#ifdef IDWRAP + irc->idfile = current->identfile; +#endif /* IDWRAP */ + } + } +} + +#ifdef IDWRAP + +void bounce_cleanup(void) +{ + ircLink *irc; + + for(irc=bnclist;irc;irc=irc->next) + { + if (irc->idfile) + unlink(irc->idfile); + } +} + +#endif /* IDWRAP */ + +void new_port_bounce(const struct Setting *no_op) +{ + if (bounce_sock != -1) + close(bounce_sock); + bounce_sock = -1; +} + +void select_bounce(void) +{ + ircLink *irc; + + if ((bounce_sock == -1) && (bounce_port > 0)) + { + bounce_sock = SockListener(bounce_port); +#ifdef DEBUG + if (bounce_sock >= 0) + { + debug("(select_bounce) {%i} irc proxy socket is active (%i)\n", + bounce_sock,bounce_port); + } +#endif /* DEBUG */ + } + + if (bounce_sock != -1) + { + chkhigh(bounce_sock); + FD_SET(bounce_sock,&read_fds); + } + + for(irc=bnclist;irc;irc=irc->next) + { + if (irc->servsock != -1) + { + chkhigh(irc->servsock); + if (irc->status == BNC_CONNECTING) + FD_SET(irc->servsock,&write_fds); + else + FD_SET(irc->servsock,&read_fds); + } + /* + * dont read data from client when connecting to server + */ + if (irc->status != BNC_CONNECTING) + { + chkhigh(irc->usersock); + FD_SET(irc->usersock,&read_fds); + } + } +} + +void process_bounce(void) +{ + char message[MSGLEN]; + ircLink *irc,**pp; + char *p; + int s; + + if ((bounce_sock != -1) && FD_ISSET(bounce_sock,&read_fds)) + { + if ((s = SockAccept(bounce_sock)) >= 0) + { + set_mallocdoer(process_bounce); + irc = (ircLink*)Calloc(sizeof(ircLink)); /* sets all to zero */ + irc->next = bnclist; + bnclist = irc; + irc->active = now; + irc->usersock = s; + --irc->servsock; /* == -1 */ + } + } + + for(irc=bnclist;irc;irc=irc->next) + { + /* + * client socket has data to read + */ + if (FD_ISSET(irc->usersock,&read_fds)) + { + while((p = sockread(irc->usersock,irc->usermem,message))) + { + if (irc->status == BNC_ACTIVE) + { + if (to_file(irc->servsock,FMT_PLAINLINE,message) < 0) + /* fall into switch() with a valid errno */ + break; + } + else + { + bounce_parse(irc,message); + } + } + switch(errno) + { + default: + irc->status = BNC_DEAD; + case EAGAIN: + case EINTR: + break; + } + } + + if (irc->servsock < 0) + continue; + + /* + * server socket just got connected + */ + if (FD_ISSET(irc->servsock,&write_fds)) + { +#ifdef DEBUG + debug("(process_bounce) {%i} servsock connected\n",irc->servsock); +#endif /* DEBUG */ + irc->status = BNC_ACTIVE; + irc->active = now; + to_file(irc->servsock,"USER %s\n",irc->userLine); + if (to_file(irc->servsock,"NICK %s\n",irc->nickLine) < 0) + { + /* + * write error of some kind, dump both client and server + */ + irc->status = BNC_DEAD; + } + Free((char**)&irc->userLine); + Free((char**)&irc->nickLine); + } + /* + * server socket has data to read + */ + if (FD_ISSET(irc->servsock,&read_fds)) + { + irc->active = now; + while((p = sockread(irc->servsock,irc->servmem,message))) + { + if (to_file(irc->usersock,FMT_PLAINLINE,message) < 0) + /* fall into switch() with a valid errno */ + break; + } + switch(errno) + { + default: + irc->status = BNC_DEAD; + case EAGAIN: + case EINTR: + break; + } + } + } + + /* + * clean out dead and time outed sockets + */ + pp = &bnclist; + while((irc = *pp)) + { + if (irc->status == BNC_DEAD || ((irc->status != BNC_ACTIVE) && ((now - irc->active) > 60))) + { +#ifdef DEBUG + debug("(process_bounce) {%i} {%i} BNC_DEAD or timeout, removing...\n",irc->usersock,irc->servsock); +#endif /* DEBUG */ + *pp = irc->next; + if (irc->servsock >= 0) + close(irc->servsock); + if (irc->usersock >= 0) + close(irc->usersock); + Free((char**)&irc->userLine); + Free((char**)&irc->nickLine); + Free((char**)&irc->nick); + Free((char**)&irc->handle); +#ifdef IDWRAP + if (irc->idfile) + { + unlink(irc->idfile); + Free((char**)&irc->idfile); + } +#endif /* IDWRAP */ + Free((char**)&irc); + continue; + } + pp = &irc->next; + } +} + +#endif /* BOUNCE */ diff --git a/src/chanban.c b/src/chanban.c new file mode 100644 index 0000000..5ffbd11 --- /dev/null +++ b/src/chanban.c @@ -0,0 +1,161 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define CHANBAN_C +#include "config.h" + +#ifdef CHANBAN + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" + +/* + +if the chanban setting is on, make a whois for all that join #mychannel +if #bannedchannel is in their list of channels, kickban them with appropriate message +check people randomly, if their whois is old and sendq is empty, do a new whois on them +never kickban people on the same channel as the bot +TODO: never kickban authenticated people + +*/ + +/* + * called from main doit() loop + * current is not set + */ +void process_chanbans(void) +{ + Strp *sp,**pp; + Chan *anychan; + ChanUser *cu,*selcu; + + for (current=botlist;current;current=current->next) + { + if (current->lastchanban > (now - 10)) + { +#ifdef DEBUG + debug("(process_chanbans) skipping %s (%i), (lastchanban (%lu) > now - 10 (%lu)\n", + current->nick,current->guid,current->lastchanban,(now - 10)); +#endif /* DEBUG */ + continue; + } + if (current->sendq) // only do chanbans on empty queue + { +#ifdef DEBUG + debug("(process_chanbans) skipping %s (%i), sendq not empty\n",current->nick,current->guid); +#endif /* DEBUG */ + continue; + } + + selcu = NULL; + for(anychan=current->chanlist;anychan;anychan=anychan->next) + { + if (anychan->modelist || anychan->kicklist) // only do chanbans on empty queue + goto has_queue; + if (anychan->setting[TOG_CHANBAN].int_var && anychan->bot_is_op) + { + for(cu=anychan->users;cu;cu=cu->next) + { + if (cu->user) + { + if (!selcu || cu->lastwhois < selcu->lastwhois) + { + selcu = cu; + } + } + } + } + } + + if (selcu && selcu->lastwhois < (now-30)) + { + selcu->flags &= ~CU_CHANBAN; + selcu->lastwhois = now; + current->lastchanban = now; + + pp = ¤t->sendq; + while(*pp) + pp = &(*pp)->next; + set_mallocdoer(process_chanbans); + *pp = sp = (Strp*)Calloc(sizeof(Strp) + 6 + strlen(selcu->nick)); + sprintf(sp->p,"WHOIS %s",selcu->nick); + /* Calloc sets to zero sp->next = NULL; */ + } +has_queue: + ; + } +} + +void chanban_action(char *nick, char *channel, Shit *shit) +{ + ChanUser *cu; + char *nuh; + + // the channel is shitted and the user is on it... + // 1, make sure the bot isnt on the channel + // 2, kb the user on all channels where the shit is active and i am op + + // check all current channels + for(CurrentChan=current->chanlist;CurrentChan;CurrentChan=CurrentChan->next) + { + if (!Strcasecmp(channel,CurrentChan->name)) // if the bot is on the channel, skip it + { +#ifdef DEBUG + debug("(chanban_action) skipping %s: bot is on channel\n",channel); +#endif /* DEBUG */ + return; + } + // is the shit for this channel? + if (!Strcasecmp(shit->chan,CurrentChan->name)) + { + // if chanban is turned on && if bot is op (pretty pointless otherwise) + if (CurrentChan->setting[TOG_CHANBAN].int_var && CurrentChan->bot_is_op) + { +#ifdef DEBUG + debug("(chanban_action) %s: Tog is on, I am op\n",CurrentChan->name); +#endif /* DEBUG */ + cu = find_chanuser(CurrentChan,nick); + if (!(cu->flags & CU_CHANBAN)) + // dont kickban the same user multiple times from the same channel + { + nuh = get_nuh(cu); // clobbers nuh_buf +#ifdef DEBUG + debug("(chanban_action) slapping %s on %s for being on %s (mask %s): %s\n", + nick,CurrentChan->name,channel,shit->mask,shit->reason); +#endif /* DEBUG */ + cu->flags |= CU_CHANBAN; + format_uh(nuh,1); // returns mask in 'nuh' buffer (nuh_buf) + send_mode(CurrentChan,90,QM_RAWMODE,'+','b',(void*)nuh); + send_kick(CurrentChan,nick,"%s (%s)",shit->reason,channel); // clobbers gsockdata + } + } + } + } +} + +/* + * + * commands + * + */ + +#endif /* CHANBAN */ diff --git a/src/channel.c b/src/channel.c new file mode 100644 index 0000000..bf8a901 --- /dev/null +++ b/src/channel.c @@ -0,0 +1,1105 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define CHANNEL_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * runs ever RESETINTERVAL (90) seconds from update() + */ +void check_idlekick(void) +{ + ChanUser *cu; + Chan *chan; + time_t timeout; + int limit; + + for(chan=current->chanlist;chan;chan=chan->next) + { + limit = chan->setting[INT_IKT].int_var; + timeout = (now - (60 * limit)); + for(cu=chan->users;cu;cu=cu->next) + { + cu->flags &= ~CU_KSWARN; /* remove KS warnings */ + if (!chan->bot_is_op || limit == 0) + continue; + if (cu->flags & CU_CHANOP) + continue; + if (timeout < cu->idletime) + continue; + if (get_useraccess(get_nuh(cu),chan->name)) + continue; + send_kick(chan,cu->nick,"Idle for %i minutes",limit); + } + } +} + +__attr (CORE_SEG, regparm(2)) +Chan *find_channel(char *name, int anychannel) +{ + Chan *chan; + uchar ni; + + ni = tolowertab[(uchar)(name[1])]; + + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->active < anychannel) + continue; + if (ni != tolowertab[(uchar)(chan->name[1])]) + continue; + if (!Strcasecmp(name,chan->name)) + return(chan); + } + return(NULL); +} + +__attr(CORE_SEG, __regparm (1)) +Chan *find_channel_ac(char *name) +{ + return(find_channel(name,CHAN_ACTIVE)); +} + +__attr(CORE_SEG, __regparm (1)) +Chan *find_channel_ny(char *name) +{ + return(find_channel(name,CHAN_ANY)); +} + +void remove_chan(Chan *chan) +{ + Chan **pp; + + pp = ¤t->chanlist; + while(*pp) + { + if (*pp == chan) + { + *pp = chan->next; + purge_banlist(chan); + purge_chanusers(chan); + delete_vars(chan->setting,CHANSET_SIZE); + Free(&chan->name); + Free(&chan->key); + Free(&chan->topic); + Free(&chan->kickedby); + Free((char **)&chan); + return; + } + pp = &(*pp)->next; + } +} + +void join_channel(char *name, char *key) +{ + Chan *chan; + + if (!ischannel(name)) + return; + + if ((chan = find_channel_ny(name)) == NULL) + { + set_mallocdoer(join_channel); + chan = (Chan*)Calloc(sizeof(Chan)); + set_mallocdoer(join_channel); + chan->name = Strdup(name); + if (key) + { + set_mallocdoer(join_channel); + chan->key = Strdup(key); + } + copy_vars(chan->setting,current->setting); + chan->next = current->chanlist; + chan->rejoin = TRUE; + chan->active = FALSE; + current->chanlist = chan; + current->rejoin = TRUE; + if (current->sock == -1) + { + current->activechan = chan; + chan->sync = TRUE; + } + else + { + to_server("JOIN %s %s\n",name,(key && *key) ? key : ""); + chan->sync = FALSE; + } + return; + } + if (key && (key != chan->key)) + { + Free(&chan->key); + set_mallocdoer(join_channel); + chan->key = Strdup(key); + } + if (chan->active) + { + current->activechan = chan; + return; + } + /* + * If its not CH_ACTIVE, its CH_OLD; there are only those 2 states. + */ + if (current->sock >= 0 && chan->sync) + { + to_server("JOIN %s %s\n",name,(key) ? key : ""); + chan->sync = FALSE; + } + chan->rejoin = TRUE; + current->rejoin = TRUE; +} + +void reverse_topic(Chan *chan, char *from, char *topic) +{ + if ((chan->setting[TOG_TOP].int_var) && + (get_useraccess(from,chan->name) < ASSTLEVEL)) + { + if (chan->topic && Strcasecmp(chan->topic,topic)) + to_server("TOPIC %s :%s\n",chan->name,chan->topic); + return; + } + + Free((char**)&chan->topic); + set_mallocdoer(reverse_topic); + chan->topic = Strdup(topic); +} + +void cycle_channel(Chan *chan) +{ + if (!chan->sync) + return; + chan->rejoin = TRUE; + to_server("PART %s\nJOIN %s %s\n",chan->name, + chan->name,(chan->key) ? chan->key : ""); +} + +int reverse_mode(char *from, Chan *chan, int m, int s) +{ + char buffer[100]; + char *ptr,*ptr2; + char mode,sign; + + if (!chan->bot_is_op || !chan->setting[TOG_ENFM].int_var || + ((ptr = chan->setting[STR_ENFMODES].str_var) == NULL)) + return(FALSE); + + mode = (char)m; + sign = (char)s; + + if (STRCHR(ptr,mode) && (sign == '+')) + return(FALSE); + if (!STRCHR(ptr,mode) && (sign == '-')) + return(FALSE); + if (get_useraccess(from,chan->name) >= ASSTLEVEL) + { + ptr2 = buffer; + if (sign == '-') + { + while(*ptr) + { + if (*ptr != mode) + *ptr2++ = *ptr; + ptr++; + } + *ptr2 = 0; + } + else + { + buffer[0] = mode; + buffer[1] = 0; + Strcat(buffer,ptr); + } + set_str_varc(chan,STR_ENFMODES,buffer); + return(FALSE); + } + return(TRUE); +} + +void chan_modestr(Chan *chan, char *dest) +{ + *(dest++) = '+'; + if (chan->private) + *(dest++) = 'p'; + if (chan->secret) + *(dest++) = 's'; + if (chan->moderated) + *(dest++) = 'm'; + if (chan->topprot) + *(dest++) = 't'; + if (chan->invite) + *(dest++) = 'i'; + if (chan->nomsg) + *(dest++) = 'n'; + if (chan->limitmode && chan->limit) + *(dest++) = 'l'; + if (chan->keymode) + *(dest++) = 'k'; + *dest = 0; + + if ((chan->limitmode) && (chan->limit)) + { + sprintf(dest," %i",chan->limit); + } + if (chan->keymode) + { + Strcat(dest," "); + Strcat(dest,(chan->key) ? chan->key : "???"); + } +} + +char *find_nuh(char *nick) +{ + Chan *chan; + ChanUser *cu; + + for(chan=current->chanlist;chan;chan=chan->next) + { + if ((cu = find_chanuser(chan,nick))) + return(get_nuh(cu)); + } + return(NULL); +} + +Ban *make_ban(Ban **banlist, char *from, char *banmask, time_t when) +{ + Ban *new; + int sz; + + for(new=*banlist;new;new=new->next) + { + if (!Strcasecmp(new->banstring,banmask)) + return(NULL); + } + + sz = sizeof(Ban) + Strlen2(from,banmask); + //sz = sizeof(Ban) + strlen(from) + strlen(banmask); + + set_mallocdoer(make_ban); + new = (Ban*)Calloc(sz); + + new->bannedby = Strcpy(new->banstring,banmask) + 1; + Strcpy(new->bannedby,from); + + new->time = when; + new->next = *banlist; + *banlist = new; + return(new); +} + +void delete_ban(Chan *chan, char *banmask) +{ + Ban *ban,**pp; + + pp = &chan->banlist; + while(*pp) + { + ban = *pp; + if (!Strcasecmp(ban->banstring,banmask)) + { + *pp = ban->next; + Free((char**)&ban); + return; + } + pp = &(*pp)->next; + } +} + +#ifdef IRCD_EXTENSIONS + +void delete_modemask(Chan *chan, char *mask, int mode) +{ + Ban *ban,**pp; + + pp = &chan->banlist; + while(*pp) + { + ban = *pp; + if ((mode == 'I' && ban->imode == TRUE) || + (mode == 'e' && ban->emode == TRUE)) + { + if (!Strcasecmp(ban->banstring,mask)) + { + *pp = ban->next; + Free((char**)&ban); + return; + } + } + pp = &(*pp)->next; + } +} + +#endif /* IRCD_EXTENSIONS */ + +void purge_banlist(Chan *chan) +{ + Ban *ban,*next; + + ban = chan->banlist; + while(ban) + { + next = ban->next; + Free((char**)&ban); + ban = next; + } + chan->banlist = NULL; +} + +void channel_massmode(Chan *chan, char *pattern, int filtmode, char mode, char typechar) +{ + ChanUser *cu; + char *pat,*uh,burst[MSGLEN],deopstring[MSGLEN]; + int i,maxmode,mal,willdo,uaccess,ispat; + + if ((pat = chop(&pattern)) == NULL) + return; + + ispat = (STRCHR(pat,'*')) ? TRUE : FALSE; + maxmode = current->setting[INT_MODES].int_var; + mal = chan->setting[INT_MAL].int_var; + *burst = 0; + +#ifdef DEBUG + debug("(...) sign %c, mode %c, maxmode %i\n",mode,typechar,maxmode); +#endif /* DEBUG */ + for(cu=chan->users;cu;) + { + uh = deopstring + sprintf(deopstring,"MODE %s %c",chan->name,mode); +#ifdef DEBUG + { + char *s; + s = deopstring; + while(*s) s++; + debug("(...) deopstring "mx_pfmt" uh "mx_pfmt" ("mx_pfmt")\n",(mx_ptr)deopstring,(mx_ptr)uh,(mx_ptr)s); + s = strchr(deopstring,0); + debug("(...) deopstring "mx_pfmt" uh "mx_pfmt" ("mx_pfmt")\n",(mx_ptr)deopstring,(mx_ptr)uh,(mx_ptr)s); + } +#endif /* DEBUG */ + i = maxmode; + do { + *uh = typechar; + uh++; + } + while(--i); + *uh = 0; +#ifdef DEBUG + debug("(...) %s\n",deopstring); +#endif /* DEBUG */ + + /* i == 0 from the while loop */ + while(cu && (i < maxmode)) + { + willdo = FALSE; + if ((mode == '+') && ((cu->flags & filtmode) == 0)) + willdo = TRUE; + if ((mode == '-') && ((cu->flags & filtmode) != 0)) + willdo = TRUE; +#ifdef DEBUG + uaccess = 0; +#endif /* DEBUG */ + if (willdo) + { + willdo = FALSE; + uh = get_nuh(cu); + uaccess = get_useraccess(uh,chan->name); + if (ispat) + { + if (!matches(pat,uh)) + { + if (typechar == 'v') + willdo = TRUE; + if ((mode == '+') && (uaccess >= mal)) + willdo = TRUE; + if ((mode == '-') && (uaccess < mal)) + willdo = TRUE; + } + } + else + if (!nickcmp(pat,cu->nick)) + { + if (mode == '-') + { + /* + * never deop yourself, stupid bot + */ + if (nickcmp(pat,current->nick)) + willdo = TRUE; + } + else + willdo = TRUE; + } + } +#ifdef DEBUG + else + { + uh = get_nuh(cu); + } + debug("(massmode(2)) willdo = %s (%s[%i]) (pat=%s)\n", + (willdo) ? "TRUE" : "FALSE",uh,uaccess,pat); +#endif /* DEBUG */ + if (willdo && ((cu->flags & CU_MASSTMP) == 0)) + { + Strcat(deopstring," "); + Strcat(deopstring,cu->nick); + cu->flags |= CU_MASSTMP; + i++; + } + + cu = cu->next; + if (!cu && (pat = chop(&pattern))) + { + ispat = (STRCHR(pat,'*')) ? TRUE : FALSE; + cu = chan->users; + } + } + + if (i) + { + if ((Strlen2(deopstring,burst)) >= MSGLEN-2) + { + write(current->sock,burst,strlen(burst)); +#ifdef DEBUG + debug("(channel_massmode)\n%s\n",burst); +#endif /* DEBUG */ + *burst = 0; + } + Strcat(burst,deopstring); + Strcat(burst,"\n"); + } + } + + if (strlen(burst)) + { + write(current->sock,burst,strlen(burst)); +#ifdef DEBUG + debug("(...)\n%s\n",burst); +#endif /* DEBUG */ + } + + for(cu=chan->users;cu;cu=cu->next) + cu->flags &= ~CU_MASSTMP; +} + +void channel_massunban(Chan *chan, char *pattern, time_t seconds) +{ + Shit *shit; + Ban *ban; + int pri; + + pri = (seconds) ? 180 : 90; + + for(ban=chan->banlist;ban;ban=ban->next) + { + if (!matches(pattern,ban->banstring) || !matches(ban->banstring,pattern)) + { + if (!seconds || ((now - ban->time) > seconds)) + { + if (chan->setting[TOG_SHIT].int_var) + { + shit = find_shit(ban->banstring,chan->name); + if (shit && shit->action > 2) + continue; + } + send_mode(chan,pri,QM_RAWMODE,'-','b',(void*)ban->banstring); + } + } + } +} + +/* + * Channel userlist stuff + */ + +/* + * this is one of the big cpu hogs in the energymech. + * its been debugged a whole lot and probably cant be + * made much better without big changes. + * + * cache hit ratio (%): 2 - 4 (smaller channels) + * 4 - 10 (larger channels) + * + * nickcmp is called for an average of 1/19th of all chanusers, + * the rest is avoided with the first-char comparison. + * + * for each nickcmp call, 10-15% cpu is saved by skipping one char + * into both nicks (first-char comparison has already been made). + */ +__attr(CORE_SEG, __regparm (2) ) +ChanUser *find_chanuser(Chan *chan, const char *nick) +{ + ChanUser *cu; + uchar ni; + + /* + * small quick'n'dirty cache + */ + if (chan->cacheuser && !nickcmp(nick,chan->cacheuser->nick)) + return(chan->cacheuser); + + /* + * avoid calling nickcmp if first char doesnt match + */ + ni = nickcmptab[(uchar)(*nick)]; + nick++; + + /* + * hog some cpu... + */ + for(cu=chan->users;cu;cu=cu->next) + { + if (ni == nickcmptab[(uchar)(*cu->nick)]) + { + if (!nickcmp(nick,cu->nick+1)) + return(chan->cacheuser = cu); + } + } + return(NULL); +} + +__attr(CORE_SEG, __regparm (2) ) +void remove_chanuser(Chan *chan, char *nick) +{ + ChanUser *cu,**pp; + uchar ni; + + /* + * avoid calling Strcasecmp if first char doesnt match + */ + ni = nickcmptab[(uchar)(*nick)]; + nick++; + + /* + * Dont call find_chanuser() because it caches the found user + * and we dont want to cache a user who quits/parts/is kicked... + */ + pp = &chan->users; + while(*pp) + { + cu = *pp; + if (ni == nickcmptab[(uchar)(*cu->nick)]) + { + if (!nickcmp(nick,cu->nick+1)) + { + if (cu == chan->cacheuser) + chan->cacheuser = NULL; + *pp = cu->next; + /* + * the mode-queue might contain a reference to this + * chanuser, remove it. + */ + unmode_chanuser(chan,cu); + /* + * byebye chanuser + */ + Free((char**)&cu->nick); + Free((char**)&cu); + return; + } + } + pp = &cu->next; + } +} + +/* + * Requires CurrentChan to be set properly + */ +__attr(CORE_SEG, __regparm (2) ) +void make_chanuser(char *nick, char *userhost) +{ + ChanUser *new; + + /* + * malloc ChanUser record with buffer space for user and host in + * a single chunk and calculate the offsets for the strings + */ + set_mallocdoer(make_chanuser); + new = (ChanUser*)Calloc(sizeof(ChanUser) + strlen(userhost)); + /* Calloc sets it all to zero */ + + new->idletime = now; + new->next = CurrentChan->users; + CurrentChan->users = new; + Strcpy(new->userhost,userhost); + + /* + * nick can change without anything else changing with it + */ + set_mallocdoer(make_chanuser); + new->nick = Strdup(nick); +} + +void purge_chanusers(Chan *chan) +{ + while(chan->users) + remove_chanuser(chan,chan->users->nick); +} + +char *get_nuh(ChanUser *user) +{ + sprintf(nuh_buf,"%s!%s",user->nick,user->userhost); + return(nuh_buf); +} + +/* + * + * commands associated with channels + * + */ + +void do_join(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *channel,*key; + + channel = chop(&rest); + if (!ischannel(channel)) + { + to_user(from,TEXT_CHANINVALID); + return; + } + if (get_authaccess(from,channel) < cmdaccess) + return; + to_user(from,"Attempting the join of %s",channel); + key = chop(&rest); + join_channel(channel,key); +} + +void do_part(COMMAND_ARGS) +{ + /* + * on_msg checks: CAXS ACCHAN + */ + Chan *chan = CurrentChan; + + chan->rejoin = FALSE; + chan->active = FALSE; + to_user(from,"Parting %s",to); + to_server("PART %s\n",to); + if (chan == current->activechan) + { + for(chan=current->chanlist;chan;chan=chan->next) + if (chan->active) + break; + current->activechan = chan; /* might be NULL */ + } +} + +void do_cycle(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + */ + to_user(from,"Cycling channel %s",to); + cycle_channel(CurrentChan); +} + +void do_forget(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + Chan *chan; + char *channel; + + channel = chop(&rest); /* cant be NULL (CARGS) */ + if ((chan = find_channel_ny(channel)) == NULL) + { + to_user(from,"Channel %s is not in memory",channel); + return; + } + if (chan->active) + { + to_user(from,"I'm currently active on %s",channel); + return; + } + to_user(from,"Channel %s is now forgotten",channel); + remove_chan(chan); +} + +void do_channels(COMMAND_ARGS) +{ + ChanUser *cu; + Chan *chan; + char text[MSGLEN]; + char *p; + int u,o,v; + + if (current->chanlist == NULL) + { + to_user(from,ERR_NOCHANNELS); + return; + } + table_buffer("\037channel\037\t \037@\037\t\037users\037\t\037ops\037\t\037voiced\037\t\037modes\037"); + for(chan=current->chanlist;chan;chan=chan->next) + { + p = Strcpy(text,chan->name); + if (chan == current->activechan) + p = Strcat(p," (current)"); + if (chan->active) + { + u = o = v = 0; + for(cu=chan->users;cu;cu=cu->next) + { + u++; + if (cu->flags & CU_CHANOP) + o++; + else + if (cu->flags & CU_VOICE) + v++; + } + sprintf(p,"\t %c\t%i\t%i\t%i\t",(chan->bot_is_op) ? '@' : ' ',u,o,v); + p = STREND(p); + chan_modestr(chan,p); + } + else + { + sprintf(p,"\t\t--\t--\t--\t%s",(chan->rejoin) ? "(Trying to rejoin...)" : "(Inactive)"); + } + table_buffer(FMT_PLAIN,text); + } + table_send(from,1); +} + +void do_wall(COMMAND_ARGS) +{ + ChanUser *cu; + +#ifdef IRCD_EXTENSIONS + if(current->ircx_flags & IRCX_WALLCHOPS) + { + to_server("WALLCHOPS %s :[Wallop/%s] %s\n",to,to,rest); + } + else +#endif /* IRCD_EXTENSIONS */ + if (current->setting[TOG_ONOTICE].int_var) + { + to_server("NOTICE @%s :[Wallop/%s] %s\n",to,to,rest); + } + else + { + cu = CurrentChan->users; + CurrentChan = NULL; + for(;cu;cu=cu->next) + { + if (cu->flags & CU_CHANOP) + { + if (cu->user == NULL || cu->user->x.x.access < BOTLEVEL) + to_user_q(cu->nick,"[Wallop/%s] %s\n",to,rest); + } + } + } + to_user(from,TEXT_SENTWALLOP,to); +} + +void do_mode(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *target; + int uaccess; + +#ifdef DEBUG + debug("(do_mode) to = %s, rest = %s\n",nullstr(to),nullstr(rest)); +#endif /* DEBUG */ + + if ((uaccess = get_authaccess(from,to)) < cmdaccess) + return; + if (ischannel(to)) + { + to_server("MODE %s %s\n",to,rest); + } + else + { + target = chop(&rest); + + if (!nickcmp(current->nick,target)) + { + to_server("MODE %s %s\n",target,rest); + } + } +} + +void do_names(COMMAND_ARGS) +{ + ChanUser *cu; + Chan *chan; + char names[MSGLEN]; + char *p; + + p = get_channel(to,&rest); + if ((chan = find_channel_ny(p)) == NULL) + { + to_user(from,ERR_CHAN,p); + return; + } + + to_user(from,"Names on %s%s:",p,(chan->active) ? "" : " (from memory)"); + for(cu=chan->users;cu;) + { + p = names; + + while(cu && ((p - names) < 60)) + { + if (p > names) + *(p++) = ' '; + + if ((cu->flags) & CU_CHANOP) + *(p++) = '@'; + else + if ((cu->flags) & CU_VOICE) + *(p++) = '+'; + + p = Strcpy(p,cu->nick); + cu = cu->next; + } + + if (p > names) + { + *p = 0; + to_user(from,FMT_PLAIN,names); + } + } +} + +void do_cchan(COMMAND_ARGS) +{ + Chan *chan; + char *channel; + + if (*rest) + { + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) != NULL) + { + current->activechan = chan; + to_user(from,"Current channel set to %s",chan->name); + } + else + to_user(from,ERR_CHAN,channel); + return; + } + to_user(from,"Current channel: %s", + (current->activechan) ? current->activechan->name : TEXT_NONE); +} + +void do_invite(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + ACCHAN + */ + char *nick; + + nick = (*rest) ? rest : CurrentNick; + + while(nick && *nick) + to_server("INVITE %s %s\n",chop(&nick),to); + + to_user(from,"User(s) invited to %s",to); +} + +void do_sayme(COMMAND_ARGS) +{ + /* + * on_msg checks CARGS + */ + + if (!ischannel(to)) + to = from; + + if (ischannel(rest)) + { + to = chop(&rest); + /* + * need to check channel access + */ + if (get_authaccess(from,to) < cmdaccess) + return; + } + else + if (CurrentDCC && current->activechan) + { + to = current->activechan->name; + } + + CoreChan.name = to; + CurrentChan = (Chan*)&CoreChan; + to_user_q(from,(CurrentCmd->name == C_SAY) ? FMT_PLAIN : "\001ACTION %s\001",rest); +} + +void do_showusers(COMMAND_ARGS) +{ + /* + * on_msg checks: CAXS + */ + Chan *chan; + ChanUser *cu; + char *pattern,*nuh; + char modechar,thechar; + int umode,uaccess,flags; + + flags = 0; + pattern = nuh = NULL; + if (*rest) + { + pattern = chop(&rest); + if (*pattern == '-') + { + nuh = pattern; + pattern = chop(&rest); + } + else + if (*rest == '-') + nuh = chop(&rest); + + if (nuh) + { + if (!Strcasecmp(nuh,"-ops")) + flags = 1; + else + if (!Strcasecmp(nuh,"-nonops")) + flags = 2; + else + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + } + } + + if ((chan = find_channel_ny(to)) == NULL) + { + to_user(from,"I have no information on %s",to); + return; + } + table_buffer(str_underline("Users on %s%s"),to,(chan->active) ? "" : " (from memory)"); + + if (chan->users) + { + thechar = 0; + for(cu=chan->users;cu;cu=cu->next) + { + umode = cu->flags; + + if ((flags == 1) && !(umode & CU_CHANOP)) + continue; + if ((flags == 2) && (umode & CU_CHANOP)) + continue; + + nuh = get_nuh(cu); + if (pattern && matches(pattern,nuh)) + continue; + + modechar = (umode & CU_CHANOP) ? '@' : ((umode & CU_VOICE) ? '+' : ' '); + + thechar = 'u'; + uaccess = get_useraccess(nuh,to); + if (!uaccess) + { + if ((uaccess = get_shitaction(nuh,to)) != 0) + thechar = 's'; + } + else + if (uaccess == 200) + thechar = 'b'; + + table_buffer("\r%i%c %c \r%s\t %s",uaccess,thechar,modechar,cu->nick,cu->userhost); + } + if (!thechar) + table_buffer("No matching users found"); + } + table_send(from,0); +} + +void do_topic(COMMAND_ARGS) +{ + /* + * on_msg checks CARGS + CAXS + */ + Chan *chan = CurrentChan; + + if (chan->bot_is_op || !chan->topprot) + { + to_server("TOPIC %s :%s\n",to,rest); + to_user(from,TEXT_TOPICCHANGED,to); + return; + } + to_user(from,ERR_NOTOPPED,to); +} + +void do_showidle(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + */ + ChanUser *cu; + Chan *chan = CurrentChan; + int n; + + n = 10; + if (*rest) + { + n = a2i(chop(&rest)); + if (errno) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + } + + table_buffer(str_underline("Users on %s that are idle more than %i seconds"),chan->name,n); + for(cu=chan->users;cu;cu=cu->next) + { + if (n >= (now - cu->idletime)) + continue; + table_buffer("%s\r %s\t%s",idle2str((now - cu->idletime),TRUE),cu->nick,cu->userhost); + } + table_send(from,1); +} + +void do_idle(COMMAND_ARGS) +{ + ChanUser *cu,*cu2; + Chan *chan; + + cu2 = NULL; + for(chan=current->chanlist;chan;chan=chan->next) + { + if ((cu = find_chanuser(chan,rest))) + { + if (!cu2 || (cu->idletime < cu2->idletime)) + cu2 = cu; + } + } + if (!cu2) + { + to_user(from,TEXT_UNKNOWNUSER,rest); + return; + } + to_user(from,"%s has been idle for %s",rest,idle2str(now - cu2->idletime,TRUE)); +} diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..7b4b728 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,552 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef CONFIG_H +#define CONFIG_H + +/* + * DEBUG: extreme amounts of debug code, needed if you want to use the -d commandline switch + */ +@DEF_DEBUG@ + +/* + * BOTNET: support for connecting bots to eachother in a botnet + */ +@DEF_BOTNET@ + +/* + * TELNET: connecting to the partyline via telnet + */ +@DEF_TELNET@ +#define TELNET_TIMEOUT 60 /* 60 seconds to enter password */ + +/* + * ALIAS: create aliases for commands + */ +@DEF_ALIAS@ +#define MAXALIASRECURSE 20 + +/* + * Support for SEEN command, undefined by default + * because it consumes a lardass amount of memory + */ +@DEF_SEEN@ + +/* + * sessions support (mech.session) + */ +@DEF_SESSION@ + +/* + * Support to alter commandlevels on the fly. undefine + * for static command levels (as defined in gencmd.c) + */ +@DEF_DYNCMD@ + +/* + * NEWBIE: support for some newbie spanking routines + */ +@DEF_NEWBIE@ + +/* + * WINGATE: support for proxy connections through wingates + */ +@DEF_WINGATE@ + +/* + * MD5CRYPT: support use of MD5 to hash passwords + */ +@DEF_MD5@ + +/* + * Standard CTCP replies (PING, FINGER, VERSION), and commands (CTCP, PING). + * DCC CHAT and the CHAT command still works even if this is undefined. + */ +@DEF_CTCP@ +#define CTCP_SLOTS 6 /* how many slots we have to send out CTCP replies */ +#define CTCP_TIMEOUT 60 /* how long before a used slot expires and can be used again */ + +/* + * + */ +@DEF_DCCFILE@ +#define DCC_PUBLICFILES "public/" +#define DCC_PUBLICINCOMING DCC_PUBLICFILES "incoming/" +#define DCC_FILETIMEOUT 90 + +/* + * UPTIME: send uptime packets to uptime.energymech.net + */ +@DEF_UPTIME@ + +/* + * REDIRECT: send command output from certain commands to a different target + */ +@DEF_REDIRECT@ + +/* + * GREET: greet known users when they join a channel + */ +@DEF_GREET@ + +/* + * PERL: scripting using the perl language (may not be supported on all hosts) + */ +@DEF_PERL@ + +/* + * TCL: scripting using the TCL language (may not be supported on all hosts) + */ +@DEF_TCL@ + +/* + * PYTHON: scripting using the Python language (may not be supported on all hosts) + */ +@DEF_PYTHON@ + +/* + * DYNAMODE: dynamic updating of the +l channel user limit + */ +@DEF_DYNAMODE@ + +/* + * WEB: serving documents via HTTP + */ +@DEF_WEB@ + +/* + * NOTE: enable commands to leave notes for users to read at a later time + */ +@DEF_NOTE@ + +/* + * NOTIFY: notify list with filtering and online logging + */ +@DEF_NOTIFY@ + +/* + * IDWRAP: support for idwrap ident spoofing + */ +@DEF_IDWRAP@ +#define IDWRAP_PATH @IDWRAP_PATH@ + +/* + * TRIVIA: support for playing trivia game + */ +@DEF_TRIVIA@ + +/* + * TOYBOX: various amusing commands + */ +@DEF_TOYBOX@ + +/* + * BOUNCE: support for `standalone' irc proxy + */ +@DEF_BOUNCE@ + +/* + * STATS: channel statistics + */ +@DEF_STATS@ + +/* + * RAWDNS: support for asynchronous hostname lookups + */ +@DEF_RAWDNS@ + +/* + * IRCD_EXTENSIONS: support some special features of new ircds + */ +@DEF_IRCD_EXT@ +#ifdef IRCD_EXTENSIONS +@DEF_CHANBAN@ +#else +#undef CHANBAN +#endif /* IRCD_EXTENSIONS */ + +/* + * FASTNICK: faster nick regain if the nick is seen when released + * Enables code that is potentially dangerous if an attacker aquires + * the nick that the bot wants + */ +#undef FASTNICK + +/* + * NEWUSER_SPAM: notify new users when they are added. + * Spamming added users with their access levels, etc. + * (it only spams when a nick is specified for ADD) + */ +#define NEWUSER_SPAM + +/* + * assume that sockets have default options + */ +#define ASSUME_SOCKOPTS + +/* + * very dangerous features that allow execution of commands on the shell + * dont define this unless you know exactly what you're doing + */ +#undef PLEASE_HACK_MY_SHELL + +/* + * define SCRIPTING if either PERL, TCL or PYTHON is enabled + */ +#if defined(PERL) || defined(TCL) || defined(PYTHON) +#define SCRIPTING +#endif + +/* + * + */ +@DEF_CRYPT_FUNCTION@ +@CRYPT_HAS_MD5@ +@CRYPT_HAS_DES@ + +/* + * Easier to include ALL header files here and + * then include only *this* file elsewhere. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * stuff under here shouldnt be changed + * unless you really know what you're doing + */ +#define str_bold(x) "\002" x "\002" +#define str_underline(x) "\037" x "\037" + +#define COMMENT_CHAR ';' +#define COMMENT_STRCHR ";" + +#define BOTDIR "" +#define MECHBASENAME "mech" + +#define RANDDIR "messages/" +#define HELPDIR "help/" +#define CFGFILE BOTDIR MECHBASENAME ".conf" +#define PIDFILE BOTDIR MECHBASENAME ".pid" +#define MSGFILE BOTDIR MECHBASENAME ".msg" +#define SESSIONFILE BOTDIR MECHBASENAME ".session" +#define TRIVIASCOREFILE BOTDIR MECHBASENAME ".trivscore" + +#define AWAYFILE RANDDIR "away.txt" +#define NICKSFILE RANDDIR "nick.txt" +#define RANDKICKSFILE RANDDIR "kick.txt" +#define RANDTOPICSFILE RANDDIR "say.txt" +#define RANDSAYFILE RANDDIR "say.txt" +#define RANDINSULTFILE RANDDIR "insult.txt" +#define RANDPICKUPFILE RANDDIR "pickup.txt" +#define RAND8BALLFILE RANDDIR "8ball.txt" +#define SIGNOFFSFILE RANDDIR "signoff.txt" +#define VERSIONFILE RANDDIR "version.txt" + +#define DEFAULT_IRC_PORT 6667 + +#define DEFAULTSHITLEVEL 2 +#define DEFAULTSHITLENGTH 30 /* in days */ + +#define EXVERSION "EnergyMech 3" +#define EXFINGER EXVERSION + +#define AWAYFORM "AWAY :%s (since %s)\n" + +#define KILLSOCKTIMEOUT 30 +#define WAITTIMEOUT 30 + +#define NICKFLOODTIME 120 /* 240 second window for floods ( 240 / 2 = 120 ) */ + +#define PINGSENDINTERVAL 210 +#define RESETINTERVAL 90 + +#define SRSIZE 200 +#define MRSIZE 200 + +#define DCC_INPUT_LIMIT 2000 +#define DCC_INPUT_DECAY 200 /* 2000 (limit) / 200 (per second) = 10 seconds */ + +#define LINKTIME 120 +#define AUTOLINK_DELAY 240 /* should be greater than link timeout ... */ + +#define REJOIN_DELAY 8 /* seconds between each channel joined */ +#define NEEDOP_DELAY 10 /* seconds between each neeop request */ + +#define SEEN_TIME 14 /* how long in DAYS to keep track of a record? */ + +#define JOINLEVEL 70 /* affects invites */ +#define ASSTLEVEL 80 +#define OWNERLEVEL 100 +#define BOTLEVEL 200 + +#define DEFAULT_KS_LEVEL 1 +#define MAX_KS_LEVEL 3 + +#define MAXPROTLEVEL 4 +#define SELFPROTLEVEL 1 /* protlevel for the bot itself */ + +#define PASSLEN 20 +#define PASSBUF PASSLEN+1 + +#define NAMELEN 79 +#define NAMEBUF NAMELEN+1 + +#define MINPASSCHARS 4 +#define MAXPASSCHARS 50 + +#define MAXHOSTLEN 64 +#define NUHLEN 128 +#define MSGLEN 512 +#define MAXLEN 800 + +#define LASTCMDSIZE 20 + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define NEWFILEMODE 0644 +#define SECUREFILEMODE 0600 /* files that might contain priviliged information + (eg, plaintext passwords in the debug log) */ + +/* + * Defines for commandlist parsing + */ + +#define DCC 0x00100 /* requires DCC */ +#define CC 0x00200 /* requires commandchar */ +#define PASS 0x00400 /* requires password / authentication */ +#define CARGS 0x00800 /* requires args */ +#define NOPUB 0x01000 /* ignore in channel (for password commands) */ +#define NOCMD 0x02000 /* not allowed to be executed thru CMD */ +#define GAXS 0x04000 /* check global access */ +#define CAXS 0x08000 /* check channel access */ +#define REDIR 0x10000 /* may be redirected */ +#define LBUF 0x20000 /* should be linebuffered to server */ +#define CBANG 0x40000 /* command may be prefixed with a bang (!) */ +#define ACCHAN 0x80000 /* needs an active channel */ + +#define CLEVEL 0x000ff + +/* + * integer only version of RANDOM() + */ +#define RANDOM(min,max) (min + (rand() / (RAND_MAX / (max - min + 1)))) + +/* unaligned memory access */ +@UNALIGNED_MEM@ + +/* 32bit machines */ +@PTSIZE_DEFINE32@ + +/* 64bit machines */ +@PTSIZE_DEFINE64@ + +/* + * How to make things non-portable: + */ +#ifdef PTSIZE_32BIT + +#define mx_ptr unsigned int +#define mx_pfmt "%.8x" + +#endif + +#ifdef PTSIZE_64BIT + +#define mx_ptr long long +#define mx_pfmt "%.16Lx" + +#endif + +/* + * String of compile-time options: + */ +#define OPT_COMMA "" +#define OPT_COREONLY 1 + +#ifdef MAIN_C + +#ifdef LIBRARY +static +#endif +const char __mx_opts[] = "" + +#ifdef ALIAS + OPT_COMMA "alias" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* ALIAS */ + +#ifdef BOUNCE + OPT_COMMA "bounce" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* BOUNCE */ + +#ifdef CHANBAN + OPT_COMMA "chanban" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* CHANBAN */ + +#ifdef DEBUG + OPT_COMMA "debug" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* DEBUG */ + +#ifdef DYNCMD + OPT_COMMA "dyn" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* DYNCMD */ + +#ifdef MD5CRYPT + OPT_COMMA "md5" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* MD5CRYPT */ + +#ifdef BOTNET + OPT_COMMA "net" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* BOTNET */ + +#ifdef NEWBIE + OPT_COMMA "newbie" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* NEWBIE */ + +#ifdef PYTHON + OPT_COMMA "python" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* PYTHON */ + +#ifdef RAWDNS + OPT_COMMA "rawdns" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* RAWDNS */ + +#ifdef SEEN + OPT_COMMA "seen" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* SEEN */ + +#ifdef SESSION + OPT_COMMA "session" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* SESSION */ + +#ifdef TCL + OPT_COMMA "tcl" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* TCL */ + +#ifdef TELNET + OPT_COMMA "telnet" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* TELNET */ + +#ifdef WINGATES + OPT_COMMA "wingate" +#undef OPT_COMMA +#define OPT_COMMA ", " +#undef OPT_COREONLY +#endif /* WINGATES */ + +#ifdef OPT_COREONLY + "(core only)" +#endif /* OPT_COREONLY */ + ; + +/* + * end of the option ID string + */ + +#else /* MAIN_C */ + +extern char __mx_opts[]; + +#endif /* MAIN_C */ + +#ifndef ulong +#define ulong unsigned long +#endif + +#ifndef uchar +#define uchar unsigned char +#endif + +#ifndef INT_MAX +#define INT_MAX ((int)(((unsigned int)-3) >> 1)) +#endif + +#define TIME_MAX 2147483647 + +/* + * why are you looking here? + */ +#undef I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS + +#endif /* CONFIG_H */ diff --git a/src/core.c b/src/core.c new file mode 100644 index 0000000..e85288b --- /dev/null +++ b/src/core.c @@ -0,0 +1,1681 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define COMBOT_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" +#include "settings.h" + +#ifdef IDWRAP + +void unlink_identfile(void) +{ + if (current->identfile) + { +#ifdef DEBUG + debug("(unlink_identfile) unlink(%s)\n",current->identfile); +#endif /* DEBUG */ + unlink(current->identfile); + Free((char**)¤t->identfile); + } +} + +#endif /* IDWRAP */ + +int conf_callback(char *line) +{ + + if (line && *line == COMMENT_CHAR) + return(FALSE); + + fix_config_line(line); + + on_msg((char*)CoreUser.name,current->nick,line); + return(FALSE); +} + +void readcfgfile(void) +{ + Mech *bot; + int oc,in; + + oc = TRUE; + in = -1; + +#ifdef SESSION + if (!Strcmp(CFGFILE,configfile)) + { + if ((in = open(SESSIONFILE,O_RDONLY)) >= 0) + { + to_file(1,"init: Restoring previously saved session...\n"); + oc = FALSE; + } + } +#endif /* SESSION */ + + if (oc) + { + if ((in = open(configfile,O_RDONLY)) < 0) + { + to_file(1,"init: Couldn't open the file %s\n",configfile); + mechexit(1,exit); + } + } + + current = add_bot(0,"(conf)"); + + *CurrentNick = 0; + CurrentShit = NULL; + CurrentChan = NULL; + CurrentDCC = (Client*)&CoreClient; + CurrentUser = (User*)&CoreUser; + + readline(in,&conf_callback); /* readline closes in */ + + CurrentDCC = NULL; + + if (!current->guid) + { + to_file(1,"init: Error: No bots in the configfile\n"); + mechexit(1,exit); + } + + if ((current) && (current->chanlist == NULL)) + to_file(1,"%s %s will not join any channels\n",ERR_INIT,current->nick); + + oc = 0; +#ifdef DEBUG + in = dodebug; + dodebug = 0; +#endif /* DEBUG */ + to_file(1,"init: Mech(s) added [ "); + for(bot=botlist;bot;bot=bot->next) + { + if (oc > 30) + { + to_file(1,", ..."); + break; + } + to_file(1,"%s%s",(oc > 0) ? ", " : "",bot->nick); + oc += strlen(bot->nick); + } + to_file(1," ]\n"); +#ifdef DEBUG + dodebug = in; +#endif /* DEBUG */ +} + +#ifdef SESSION + +int write_session(void) +{ +#ifdef BOTNET + NetCfg *cfg; +#endif /* BOTNET */ +#ifdef RAWDNS + Strp *p; +#endif /* RAWDNS */ + Server *sp; + Chan *chan; + Mech *bot; + UniVar *varval; + int j,sf; + + if ((sf = open(SESSIONFILE,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0) + return(FALSE); + +#ifdef RAWDNS + for(p=dnsrootfiles;p;p=p->next) + to_file(sf,"dnsroot %s\n",p->p); + for(j=0;j 0) + to_file(sf,"dnsserver %s\n",inet_ntoa(ia_ns[j])); +#endif /* RAWDNS */ + + for(bot=botlist;bot;bot=bot->next) + { + to_file(sf,"nick %i %s\n",bot->guid,bot->wantnick); + /* + * current->setting contains channel defaults and global vars + */ + for(j=0;VarName[j].name;j++) + { + varval = &bot->setting[j]; + if (IsProc(j)) + varval = varval->proc_var; + if (IsChar(j)) + { + if ((int)VarName[j].setto != varval->char_var) + to_file(sf,"set %s %c\n",VarName[j].name,varval->char_var); + } + else + if (IsNum(j)) + { + if ((int)VarName[j].setto != varval->int_var) + to_file(sf,"set %s %i\n",VarName[j].name,varval->int_var); + } + else + if (IsStr(j)) + { + /* + * There are no default string settings + */ + if (varval->str_var) + to_file(sf,"set %s %s\n",VarName[j].name,varval->str_var); + } + } + for(chan=bot->chanlist;chan;chan=chan->next) + { + if (!chan->active && !chan->rejoin) + continue; + to_file(sf,"join %s %s\n",chan->name,(chan->key) ? chan->key : ""); + /* + * using CHANSET_SIZE: only the first settings contain stuff + */ + for(j=0;jsetting[j]; + if (IsNum(j)) + { + if ((int)VarName[j].setto != varval->int_var) + to_file(sf,"set %s %i\n",VarName[j].name,varval->int_var); + } + else + if (IsStr(j)) + { + /* + * There are no default string settings + */ + if (varval->str_var) + to_file(sf,"set %s %s\n",VarName[j].name,varval->str_var); + } + } + } + } + + to_file(sf,"set ctimeout %i\n",ctimeout); + for(sp=serverlist;sp;sp=sp->next) + { + to_file(sf,"server %s %i %s\n",sp->name,(sp->port) ? sp->port : 6667, + (sp->pass[0]) ? sp->pass : ""); + } + +#ifdef TRIVIA + if (triv_qfile) + to_file(sf,"set qfile %s\n",triv_qfile); + to_file(sf,"set qdelay %i\n",triv_qdelay); + to_file(sf,"set qchar %c\n",triv_qchar); +#endif /* TRIVIA */ + +#ifdef UPTIME + if (uptimehost && Strcasecmp(uptimehost,defaultuptimehost)) + to_file(sf,"set uphost %s\n",uptimehost); + if (uptimeport) + to_file(sf,"set upport %i\n",uptimeport); + if (uptimenick) + to_file(sf,"set upnick %s\n",uptimenick); +#endif /* UPTIME */ + +#ifdef SEEN + if (seenfile) + to_file(sf,"set seenfile %s\n",seenfile); +#endif /* SEEN */ + +#ifdef BOTNET + if (linkpass) + to_file(sf,"set linkpass %s\n",linkpass); + if (linkport) + to_file(sf,"set linkport %i\n",linkport); + if (autolink) + to_file(sf,"set autolink 1\n"); + for(cfg=netcfglist;cfg;cfg=cfg->next) + { + to_file(sf,"link %i %s",cfg->guid,(cfg->pass) ? cfg->pass : MATCH_ALL); + if (cfg->host) + to_file(sf," %s %i",cfg->host,cfg->port); + to_file(sf,"\n"); + } +#endif /* BOTNET */ + +#ifdef BOUNCE + if (bounce_port) + to_file(sf,"set bncport %i\n",bounce_port); +#endif /* BOUNCE */ + +#ifdef WEB + if (webport) + to_file(sf,"set webport %i\n",webport); +#endif /* WEB */ + +#ifdef DYNCMD + /* + * because of "chaccess XXX disable" its best to save chaccess last + */ + for(j=0;mcmd[j].name;j++) + { + if (acmd[j] != mcmd[j].defaultaccess) + { + if (acmd[j] == 250) + to_file(sf,"chaccess %s disable\n",mcmd[j].name); + else + to_file(sf,"chaccess %s %i\n",mcmd[j].name,(int)acmd[j]); + } + } +#endif /* DYNCMD */ + + close(sf); + return(TRUE); +} + +#endif /* SESSION */ + +/* + * Bot nicking, adding and killing + */ + +void setbotnick(Mech *bot, char *nick) +{ + /* + * if its exactly the same we dont need to change it + */ + if (!Strcmp(bot->nick,nick)) + return; + + Free((char**)&bot->nick); + set_mallocdoer(setbotnick); + bot->nick = Strdup(nick); +#ifdef BOTNET + botnet_refreshbotinfo(); +#endif /* BOTNET */ +} + +Mech *add_bot(int guid, char *nick) +{ + Mech *bot; + + set_mallocdoer(add_bot); + bot = (Mech*)Calloc(sizeof(Mech)); + bot->connect = CN_NOSOCK; + bot->sock = -1; + bot->guid = guid; + set_mallocdoer(add_bot); + bot->nick = Strdup(nick); + set_mallocdoer(add_bot); + bot->wantnick = Strdup(nick); + set_binarydefault(bot->setting); + bot->next = botlist; + botlist = bot; + +#ifndef I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS + spawning_lamer++; +#endif /* I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS */ + + return(bot); +} + +void signoff(char *from, char *reason) +{ + Mech **pp; + char *fname; + int i; + + if (from) + { + to_user(from,"Killing mech: %s",current->nick); + to_user(from,"Saving the lists..."); + } + fname = current->setting[STR_USERFILE].str_var; +#ifdef DEBUG + debug("(signoff) Saving lists...\n"); +#endif /* DEBUG */ + if (!write_userlist(fname) && from) + { + to_user(from,(fname) ? ERR_NOSAVE : ERR_NOUSERFILENAME,fname); + } +#ifdef SEEN + if (seenfile && !write_seenlist() && from) + { + to_user(from,TEXT_SEENNOSAVE,seenfile); + } +#endif /* SEEN */ +#ifdef NOTIFY + if (current->notifylist) + write_notifylog(); +#endif /* NOTIFY */ + if (from) + { + to_user(from,"ShutDown Complete"); + } + + while(current->chanlist) + remove_chan(current->chanlist); + + while(current->clientlist) + delete_client(current->clientlist); + + while(current->authlist) + remove_auth(current->authlist); + + purge_shitlist(); + purge_kicklist(); + +#ifdef NOTIFY + purge_notify(); +#endif /* NOTIFY */ + + if (current->sock != -1) + { +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ + if (!reason) + reason = randstring(SIGNOFFSFILE); + to_server("QUIT :%s\n",(reason) ? reason : ""); + killsock(current->sock); + current->sock = -1; + } + + /* + * release string var memory + */ + delete_vars(current->setting,(SIZE_VARS-1)); + +#ifdef DEBUG + debug("(signoff) Removing userlist...\n"); +#endif /* DEBUG */ + while(current->userlist) + remove_user(current->userlist); + +#ifdef DEBUG + debug("(signoff) Removing lastcmd list...\n"); +#endif /* DEBUG */ + for(i=0;ilastcmds[i]); + } + + /* + * These 2 are used by do_die() to pass reason and doer. + */ + Free((char**)¤t->signoff); + Free((char**)¤t->from); + +#ifdef DEBUG + debug("(signoff) Unlinking bot record from linked list...\n"); +#endif /* DEBUG */ + + pp = &botlist; + while(*pp) + { + if (*pp == current) + { + *pp = current->next; + break; + } + } + Free((char**)¤t); + + /* + * get a new current bot, or exit + */ + if ((current = botlist) == NULL) + { +#if defined(BOUNCE) && defined(IDWRAP) + bounce_cleanup(); +#endif /* BOUNCE && IDWRAP */ + +#ifdef TRIVIA + write_triviascore(); +#endif /* TRIVIA */ + +#ifdef UPTIME + uptime_death(UPTIME_GENERICDEATH); +#endif /* UPTIME */ + + while(killsock(-2)) + /* killsock() sleeps 1 second in select() */ + ; + + mechexit(0,exit); + } + +#ifdef DEBUG + debug("(signoff) All done.\n"); +#endif /* DEBUG */ +} + +void kill_all_bots(char *reason) +{ + while(TRUE) + { + current = botlist; + signoff(NULL,reason); + } +} + +/* + * Server lists, connects, etc... + */ +Server *add_server(char *host, int port, char *pass) +{ + Server *sp,**pp; + + pp = &serverlist; + while(*pp) + { + sp = *pp; + if ((!port || sp->port == port) && (!Strcasecmp(host,sp->name) || !Strcasecmp(host,sp->realname))) + return(sp); + pp = &sp->next; + } + set_mallocdoer(add_server); + *pp = sp = (Server*)Calloc(sizeof(Server)); + sp->ident = serverident++; + Strncpy(sp->name,host,NAMELEN); + if (pass && *pass) + Strncpy(sp->pass,pass,PASSLEN); + sp->port = (port) ? port : DEFAULT_IRC_PORT; + if (currentservergroup) + sp->servergroup = currentservergroup->servergroup; + return(sp); +} + +ServerGroup *getservergroup(const char *name) +{ + ServerGroup *sg; + + for(sg=servergrouplist;sg;sg=sg->next) + { + if (!strcasecmp(sg->name,name)) + return(sg); + } + return(NULL); +} + +ServerGroup *getservergroupid(int id) +{ + ServerGroup *sg; + + for(sg=servergrouplist;sg;sg=sg->next) + { + if (sg->servergroup == id) + return(sg); + } + return(NULL); +} + +Server *find_server(int id) +{ + Server *sp; + + for(sp=serverlist;sp;sp=sp->next) + if (sp->ident == id) + return(sp); + return(NULL); +} + +int try_server(Server *sp, char *hostname) +{ +#ifdef RAWDNS + char temphost[NAMEBUF]; + char *host; + ulong ip; +#endif /* RAWDNS */ + + if (!hostname) + { + send_spy(SPYSTR_STATUS,"Trying new server: %s:%i",(*sp->realname) ? sp->realname : sp->name,sp->port); + hostname = sp->name; + } + + sp->lastattempt = now; + sp->usenum++; + +#ifdef RAWDNS + if ((host = poll_rawdns(hostname))) + { +#ifdef DEBUG + debug("(try_server) rawdns: %s ==> %s\n",sp->name,host); +#endif /* DEBUG */ + Strcpy(temphost,host); + hostname = temphost; + } + else + if ((ip = inet_addr(hostname)) == -1) + { + current->server = sp->ident; + current->connect = CN_DNSLOOKUP; + current->conntry = now; + rawdns(hostname); + return(0); + } +#endif /* RAWDNS */ + current->server = sp->ident; + if ((current->sock = SockConnect(hostname,sp->port,TRUE)) < 0) + { + sp->err = SP_ERRCONN; + return(-1); + } + current->away = FALSE; + current->connect = CN_TRYING; + current->activity = current->conntry = now; + *current->modes = 0; + return(current->sock); +} + +void connect_to_server(void) +{ + ServerGroup *sg; + Server *sp,*sptry; + Chan *chan; + char *s; + int sgi; + + /* + * This should prevent the bot from chewing up too + * much CPU when it fails to connect to ANYWHERE + */ + current->conntry = now; + + /* + * Is this the proper action if there is no serverlist? + */ + if (!serverlist) + return; + + if (current->chanlist) + { +#ifdef DEBUG + if (current->connect != CN_SPINNING) debug("[CtS] Setting rejoin- and synced-status for all channels\n"); +#endif /* DEBUG */ + current->rejoin = TRUE; + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->active) + { + chan->active = FALSE; + chan->rejoin = TRUE; + } + chan->sync = TRUE; + chan->bot_is_op = FALSE; +#ifdef STATS + if (chan->stats) + chan->stats->flags |= CSTAT_PARTIAL; +#endif /* STATS */ + } + } + + if (current->nextserver) + { + sp = find_server(current->nextserver); + current->nextserver = 0; + if (sp && (try_server(sp,NULL) >= 0)) + return; + } + + /* + * The purpose of this kludge is to find the least used server + * July 7th: added logic for servergroup + */ + sptry = NULL; + if ((s = current->setting[STR_SERVERGROUP].str_var)) + { + if ((sg = getservergroup(s))) + sgi = sg->servergroup; +#ifdef DEBUG + if (sg) + debug("[CtS] trying servergroup \"%s\" (%i)\n",s,sg->servergroup); + else + debug("[CtS] trying servergroup \"%s\" (not found)\n",s); +#endif /* DEBUG */ + } + else + { + sgi = 0; + } + for(sp=serverlist;sp;sp=sp->next) + { + if ((sgi == 0 || sp->servergroup == sgi || sp->servergroup == 0) && sp->lastattempt != now) + { + if ((!sptry) || (sp->usenum < sptry->usenum)) + { + if (sp->err == 0) + sptry = sp; + else + if ( + (sp->err == SP_THROTTLED && (sp->lastattempt + 45) < now) || /* retry throttled after 45 seconds */ + (sp->err == SP_KLINED && (sp->lastattempt + 86400) < now) /* retry Klined after a day */ + ) + sptry = sp; + } + } + } + /* + * Connect... + */ + if (sptry) + try_server(sptry,NULL); + else + { +#ifdef DEBUG + const char *errtxt; + + if (current->connect != CN_SPINNING) + { + debug("[CtS] Serverlist Exhausted:\n"); + for(sp=serverlist;sp;sp=sp->next) + { + errtxt = (const char *[]){"No error","SP_NOAUTH","SP_KLINED","SP_FULLCLASS", + "SP_TIMEOUT","SP_ERRCONN","SP_DIFFPORT","SP_NO_DNS","SP_THROTTLED"}[sp->err]; + debug("[CtS] (%i) %s[%i]: %s(%i) / servergroup %i\n",sp->ident,(sp->realname[0]) ? sp->realname : sp->name, + sp->port,errtxt,sp->err,sp->servergroup); + } + debug("[CtS] Server connection is spinning...\n"); + } + current->connect = CN_SPINNING; +#endif /* DEBUG */ + } +} + +/* + * === according to rfc1459 === + * Command: USER + * Parameters: + */ +void register_with_server(void) +{ + Server *sp; + char *ident,*ircname; + int sendpass; + +#ifdef IRCD_EXTENSIONS + current->ircx_flags = 0; +#endif /* IRCD_EXTENSIONS */ + sp = find_server(current->server); + ident = current->setting[STR_IDENT].str_var; + ircname = current->setting[STR_IRCNAME].str_var; + sendpass = (sp && *sp->pass); + to_server((sendpass) ? "PASS :%s\nNICK %s\nUSER %s " MECHUSERLOGIN " 0 :%s\n" : + "%sNICK %s\nUSER %s " MECHUSERLOGIN " 0 :%s\n", + (sendpass) ? sp->pass : "", + current->wantnick, + (ident) ? ident : BOTLOGIN, + (ircname) ? ircname : VERSION); + current->connect = CN_CONNECTED; + current->conntry = now; +} + +/* + * scripting stuff + */ + +#ifdef SCRIPTING + +int sub_compile_timer(int limit, ulong *flags1, ulong *flags2, char *args) +{ + char *s,*dash; + ulong f; + int n,hi,lo; + + *flags1 = 0; + if (flags2) *flags2 = 0; + + if (!args || !*args) + return -1; + + if (args[0] == '*' && args[1] == 0) + { + *flags1 = -1; /* 0-29 = 1 */ + if (flags2) *flags2 = -1; /* 30-59 = 1 */ + return 0; + } + + /* "n,n,n-m,n-m" */ + for(s=args;*s;s++) + if (*s == ',') + *s = ' '; + + /* "n n n-m n-m" */ + do + { + s = chop(&args); + if (s && *s) + { + if ((dash = STRCHR(s,'-'))) + { + *(dash++) = 0; + if (!*dash) + return -1; + + lo = a2i(s); + hi = a2i(dash); + + if (lo < 0 || lo > limit || hi < 0 || hi > limit) + return -1; + for(n=lo;n<=hi;n++) + { + if (n >= 30) + { + f = (1 << (n - 30)); + if (!flags2) + return -1; + *flags2 |= f; + } + else + { + f = (1 << n); + *flags1 |= f; + } + } + } + else + { + n = a2i(s); + if (n < 0 || n > limit) + return -1; + if (n >= 30) + { + f = (1 << (n - 30)); + if (!flags2) + return -1; + *flags2 |= f; + } + else + { + f = (1 << n); + *flags1 |= f; + } + } + } + } + while(s); + return 0; +} + +#if 0 + + turn this: +"* * * *" +"0-59 0-59 0-23 0-6" +0,1,2,3,...,58,59 = all seconds +0,1,2,3,...,58,59 = all minutes +0,1,2,3,...,22,23 = all hours +0,1,2,3,4,5,6 = all weekdays + + into this: + +typedef struct +{ + time_t last; + time_t next; + ulong second1:30; + ulong second2:30; + ulong minute1:30; + ulong minute2:30; + ulong hour:24; + ulong weekday:7; + +} HookTimer; + +#endif /* 0 */ +/* + * return -1 on failure + */ +int compile_timer(HookTimer *timer, char *rest) +{ + char backup[strlen(rest)+1]; + char *sec,*min,*hour,*day; + + Strcpy(backup,rest); + rest = backup; +#ifdef DEBUG + debug("(compile_timer) rest = %s\n",nullstr(rest)); +#endif /* DEBUG */ + + sec = chop(&rest); + min = chop(&rest); + hour = chop(&rest); + day = chop(&rest); + + if (sub_compile_timer(59,&timer->second1,&timer->second2,sec) < 0) + return -1; + if (sub_compile_timer(59,&timer->minute1,&timer->minute2,min) < 0) + return -1; + if (sub_compile_timer(23,&timer->hour,NULL,hour) < 0) + return -1; + if (sub_compile_timer(6,&timer->weekday,NULL,day) < 0) + return -1; + return 0; +} + +#endif /* SCRIPTING */ + +/* + * Periodic updates + */ + +void update(SequenceTime *this) +{ + Server *sp; + Chan *chan; + char *temp,*chantemp; + int tt,th; + int x,n; + + tt = now / 600; /* current 10-minute period */ + th = tt / 6; /* current hour */ + +#ifdef DEBUG + x = 0; + if (tt != this->tenminute) + { + debug("(update) running: ten minute updates [%i]",tt); + x++; + } + if (th != this->hour) + { + debug("%shour updates [%i]",(x) ? ", " : "(update) running: ",th); + x++; + } + if (x) + debug("\n"); +#endif /* DEBUG */ + + short_tv &= ~TV_REJOIN; + for(current=botlist;current;current=current->next) + { + if (current->reset || current->connect != CN_ONLINE) + continue; + + if (current->rejoin) + { + if ((now - current->lastrejoin) > REJOIN_DELAY) + { + current->rejoin = FALSE; + current->lastrejoin = now; + } + short_tv |= TV_REJOIN; + } + +#ifdef NOTIFY + send_ison(); +#endif /* NOTIFY */ + + for(chan=current->chanlist;chan;chan=chan->next) + { + if (!chan->active) + { + if (!current->rejoin && chan->rejoin) + { + /* + * join_channel() will set current->rejoin to TRUE + */ + join_channel(chan->name,chan->key); + } + } + + if (tt != this->tenminute) + { + chan->last10 = chan->this10; + chan->this10 = 0; + } + if (th != this->hour) + { + chan->last60 = chan->this60; + chan->this60 = 0; +#ifdef STATS + if ((temp = chan->setting[STR_STATS].str_var)) + stats_loghour(chan,temp,this->hour); +#endif /* STATS */ + } +#ifdef DYNAMODE + if (chan->bot_is_op && chan->sync && chan->setting[STR_DYNLIMIT].str_var) + check_dynamode(chan); +#endif /* DYNAMODE */ + } + if ((now - current->lastreset) > RESETINTERVAL) + { + current->lastreset = now; + if (Strcmp(current->nick,current->wantnick)) + to_server("NICK %s\n",current->wantnick); + check_idlekick(); + if ((x = current->setting[INT_AAWAY].int_var) && current->away == FALSE) + { + if ((now - current->activity) > (x * 60)) + { + temp = randstring(AWAYFILE); + to_server(AWAYFORM,(temp && *temp) ? temp : "auto-away",time2away(now)); + current->away = TRUE; + } + } + } + /* + * 10 minute update. + */ + if (tt != this->tenminute) + { + /* + * use `x' to count channels for the status line + */ + x = 0; + for(chan=current->chanlist;chan;chan=chan->next) + { +#ifdef BOTNET + if (chan->bot_is_op == FALSE) + { + botnet_relay(NULL,"CO%i %s\n",current->guid,chan->name); + } +#endif /* BOTNET */ + if ((n = chan->setting[INT_AUB].int_var)) + { + channel_massunban(chan,MATCH_ALL,60*n); + } + x++; + } + temp = TEXT_NOTINSERVLIST; + if ((sp = find_server(current->server))) + { + sprintf(gsockdata,"%s:%i",(*sp->realname) ? sp->realname : sp->name,sp->port); + temp = gsockdata; + } + chantemp = (current->activechan) ? current->activechan->name : TEXT_NONE; + send_spy(SPYSTR_STATUS,"C:%s AC:%i CS:%s",chantemp,x,temp); + } + /* + * Hourly update. + */ + if (th != this->hour) + { + if (current->userlist && current->ul_save) + { + temp = current->setting[STR_USERFILE].str_var; + if (!write_userlist(temp)) + send_spy(SPYSTR_STATUS,(temp) ? ERR_NOSAVE : ERR_NOUSERFILENAME,temp); + else + send_spy(SPYSTR_STATUS,TEXT_LISTSAVED,temp); + } +#ifdef SEEN + if (seenfile && !write_seenlist()) + { + send_spy(SPYSTR_STATUS,TEXT_SEENNOSAVE,seenfile); + } +#endif /* SEEN */ +#ifdef NOTIFY + if (current->notifylist) + write_notifylog(); +#endif /* NOTIFY */ + } + } + +#ifdef SESSION + if (th != this->hour) + { + if (!write_session()) + send_global(SPYSTR_STATUS,"Session could not be saved to file %s",SESSIONFILE); + } +#endif /* SESSION */ + + this->tenminute = tt; + this->hour = th; +} + +/* + * Read data from server socket + */ +void parse_server_input(void) +{ +#ifdef WINGATE + Server *sp; +#endif /* WINGATE */ + char linebuf[MSGLEN]; + char *res; + + if (FD_ISSET(current->sock,&write_fds)) + { + setbotnick(current,current->wantnick); +#ifdef DEBUG + debug("[PSI] {%i} connection established (%s) [ASYNC]\n", + current->sock,current->wantnick); +#endif /* DEBUG */ +#ifdef WINGATE + if ((current->vhost_type & VH_WINGATE_BOTH) == VH_WINGATE) + { + sp = find_server(current->server); + if (!sp) + { +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ + close(current->sock); + current->sock = -1; + return; + } +#ifdef DEBUG + debug("[PSI] Connecting via WinGate proxy...\n"); +#endif /* DEBUG */ + to_server("%s %i\n",sp->name,sp->port); + /* + * If we fail here, we'd better drop the WinGate + * and retry the SAME server again + */ + if (current->sock == -1) + { + current->nextserver = sp->ident; + current->vhost_type |= VH_WINGATE_FAIL; + } + current->connect = CN_WINGATEWAIT; + current->conntry = now; + return; + } +#endif /* WINGATE */ + /* + * send NICK, USER and maybe PASS + */ + register_with_server(); +#ifdef IDWRAP + if (current->sock == -1) + unlink_identfile(); +#endif /* IDWRAP */ + return; + } + if (FD_ISSET(current->sock,&read_fds)) + { +get_line: + res = sockread(current->sock,current->sockdata,linebuf); + if (res) + { +#ifdef WINGATE + if (current->connect == CN_WINGATEWAIT) + { + if (!matches("Connecting to host *Connected",linebuf)) + { +#ifdef DEBUG + debug("[PSI] WinGate proxy active\n"); +#endif /* DEBUG */ + register_with_server(); + return; + } + else + { +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ + close(current->sock); + current->sock = -1; + return; + } + } +#endif /* WINGATE */ + parseline(linebuf); + goto get_line; + } + switch(errno) + { + case EINTR: + case EAGAIN: + break; + default: +#ifdef DEBUG + debug("[PSI] {%i} errno = %i; closing server socket\n",current->sock,errno); +#endif /* DEBUG */ + *current->sockdata = 0; +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ + close(current->sock); + current->sock = -1; + current->connect = CN_NOSOCK; + break; + } + } +} + +/* + * + * commands associated with core.c + * + */ + +void do_version(COMMAND_ARGS) +{ + to_user_q(from,"%s %s",BOTCLASS,VERSION); +} + +void do_core(COMMAND_ARGS) +{ + char tmp[MSGLEN]; /* big buffers at the top */ + Server *sp; + Chan *chan; + User *user; + char *pt; + int i,u,su,bu; + + u = su = bu = 0; + for(user=current->userlist;user;user=user->next) + { + u++; + if (user->x.x.access == OWNERLEVEL) + su++; + if (user->x.x.access == BOTLEVEL) + bu++; + } + + i = Strcmp(current->nick,current->wantnick); + table_buffer((i) ? TEXT_CURRNICKWANT : TEXT_CURRNICKHAS,current->nick,current->wantnick); + table_buffer(TEXT_CURRGUID,current->guid); + table_buffer(TEXT_USERLISTSTATS,u,su,EXTRA_CHAR(su),bu,EXTRA_CHAR(bu)); + + pt = tmp; + *pt = i = 0; + for(chan=current->chanlist;chan;chan=chan->next) + { + if (*tmp && ((pt - tmp) + strlen(chan->name) > 57)) + { + table_buffer((i) ? TEXT_MOREACTIVECHANS : TEXT_ACTIVECHANS,tmp); + pt = tmp; + i++; + } + if (chan->bot_is_op) + *(pt++) = '@'; + if (chan == current->activechan) + { + *(pt++) = '\037'; + pt = Strcpy(pt,chan->name); + *(pt++) = '\037'; + } + else + { + pt = Strcpy(pt,chan->name); + } + if (chan->next) + *(pt++) = ' '; + *pt = 0; + } + table_buffer((i) ? TEXT_MOREACTIVECHANS : TEXT_ACTIVECHANS,(*tmp) ? tmp : TEXT_NONE); + + if (current->setting[STR_VIRTUAL].str_var) + { + if ((current->vhost_type & VH_IPALIAS_FAIL) == 0) + pt = ""; + else + pt = TEXT_VHINACTIVE; + table_buffer(TEXT_VIRTHOST,current->setting[STR_VIRTUAL].str_var,pt); + } +#ifdef WINGATE + if (current->setting[STR_WINGATE].str_var && current->setting[INT_WINGPORT].int_var) + { + if ((current->vhost_type & VH_WINGATE_FAIL) == 0) + pt = ""; + else + pt = TEXT_VHINACTIVE; + table_buffer(TEXT_VIRTHOSTWINGATE,current->setting[STR_WINGATE].str_var, + current->setting[INT_WINGPORT].int_var,pt); + } +#endif /* WINGATE */ + sp = find_server(current->server); + if (sp) + table_buffer(TEXT_CURRSERVER, + (sp->realname[0]) ? sp->realname : sp->name,sp->port); + else + table_buffer(TEXT_CURRSERVERNOT); + table_buffer(TEXT_SERVERONTIME,idle2str(now - current->ontime,FALSE)); + table_buffer(TEXT_BOTMODES,(*current->modes) ? current->modes : TEXT_NONE); + table_buffer(TEXT_CURRENTTIME,time2str(now)); + table_buffer(TEXT_BOTSTARTED,time2str(uptime)); + table_buffer(TEXT_BOTUPTIME,idle2str(now - uptime,FALSE)); + table_buffer(TEXT_BOTVERSION,VERSION,SRCDATE); + table_buffer(TEXT_BOTFEATURES,__mx_opts); + table_send(from,2); +} + +void do_die(COMMAND_ARGS) +{ + /* + * on_msg checks GAXS + */ + char *reason; + +#ifdef SESSION + write_session(); +#endif /* SESSION */ + + if (!rest || !*rest) + { + if ((reason = randstring(SIGNOFFSFILE)) == NULL) + reason = "I'll get you for this!!!"; + } + else + { + reason = rest; + if (!uptime) + { + to_file(1,"init: %s\n",rest); + _exit(0); + } + } + + set_mallocdoer(do_die); + current->signoff = Strdup(reason); + set_mallocdoer(do_die); + current->from = Strdup(from); + + current->connect = CN_BOTDIE; +} + +void do_shutdown(COMMAND_ARGS) +{ + send_global(SPYSTR_STATUS,TEXT_SHUTDOWNBY,nickcpy(NULL,from)); + +#ifdef SESSION + write_session(); +#endif /* SESSION */ + + kill_all_bots(NULL); + /* NOT REACHED */ +} + +void do_servergroup(COMMAND_ARGS) +{ + ServerGroup *sg,*new,**sgp; + char *name; + + name = chop(&rest); + + /* + * no args, list servergroups + */ + if (!name) + { + table_buffer(str_underline("id") "\t" str_underline("name")); + for(sg=servergrouplist;sg;sg=sg->next) + { + table_buffer("%i\t%s%s",sg->servergroup,sg->name,(sg == currentservergroup) ? " (current)" : ""); + } + table_send(from,2); + return; + } + + /* + * find pre-existing severgroup by the same name (case-insensitive) + */ + sg = getservergroup(name); + if (!sg) + { +#ifdef DEBUG + debug("(do_servergroup) creating new servergroup: %s\n",name); +#endif /* DEBUG */ + set_mallocdoer(do_servergroup); + new = (ServerGroup*)Calloc(sizeof(ServerGroup) + strlen(name)); + servergroupid++; + new->servergroup = servergroupid; + strcpy(new->name,name); + sgp = &servergrouplist; + while(*sgp) + sgp = &(*sgp)->next; + sg = *sgp = new; +#ifdef DEBUG + { + ServerGroup *g; + + for (g=servergrouplist;g;g=g->next) + { + debug("(do_servergroup) %s (%i)\n",g->name,g->servergroup); + } + } +#endif /* DEBUG */ + } + currentservergroup = sg; +#ifdef DEBUG + debug("(do_servergroup) current servergroup set to \"%s\" (%i)\n",sg->name,sg->servergroup); +#endif /* DEBUG */ +} + +void do_server(COMMAND_ARGS) +{ + ServerGroup *sg; + Server *sp,*dp,**spp; + char *server,*aport,*pass; + char addc,*last,*quitmsg = "Trying new server, brb..."; + int n,iport,sgi; + + if (CurrentCmd->name == C_NEXTSERVER) + { + quitmsg = "Switching servers..."; + to_user(from,FMT_PLAIN,quitmsg); + goto do_server_jump; + } + server = chop(&rest); + + /* + * no args, list all known servers + */ + if (!server) + { + if (dcc_only_command(from)) + return; + if (servergrouplist->next) + table_buffer(str_underline("server") "\t" str_underline("last connect") "\t" str_underline("group")); + else + table_buffer(str_underline("server") "\t" str_underline("last connect")); + sgi = -1; + for(sp=serverlist;sp;sp=sp->next) + { + if (sp->lastconnect) + last = idle2str(now - sp->lastconnect,FALSE); + else + { + switch(sp->err) + { + case SP_NOAUTH: + last = TEXT_SP_NOAUTH; + break; + case SP_KLINED: + last = TEXT_SP_KLINED; + break; + case SP_FULLCLASS: + last = TEXT_SP_FULLCLASS; + break; + case SP_TIMEOUT: + last = TEXT_SP_TIMEOUT; + break; + case SP_ERRCONN: + last = TEXT_SP_ERRCONN; + break; + case SP_DIFFPORT: + last = TEXT_SP_DIFFPORT; + break; + case SP_NO_DNS: + last = TEXT_SP_NO_DNS; + break; + default: + last = TEXT_NEVER; + } + } + if (servergrouplist->next) + { + if (sgi != sp->servergroup) + { + sg = getservergroupid(sp->servergroup); + if (sg) + sgi = sg->servergroup; + } + table_buffer("%s:%i\t%s%s%s\t%s",(*sp->realname) ? sp->realname : sp->name,sp->port, + last,(sp->lastconnect) ? TEXT_AGO : "",(sp->ident == current->server) ? TEXT_CURRENT : "", + (sg) ? sg->name : "(unknown)"); + } + else + table_buffer("%s:%i\t%s%s%s",(*sp->realname) ? sp->realname : sp->name,sp->port, + last,(sp->lastconnect) ? TEXT_AGO : "",(sp->ident == current->server) ? TEXT_CURRENT : ""); + } + table_send(from,2); + return; + } + + addc = *server; + if (addc == '-' || addc == '+') + { + server++; + if (!*server) + { + usage(from); + return; + } + } + if (strlen(server) >= MAXHOSTLEN) + { + to_user(from,TEXT_NAMETOOLONG); + return; + } + + aport = chop(&rest); + pass = chop(&rest); + iport = a2i(aport); + + if (aport && *aport == COMMENT_CHAR) + { + aport = pass = NULL; + } + else + if (pass && *pass == COMMENT_CHAR) + { + pass = NULL; + } + + if (aport && (errno || iport < 1 || iport > 65534)) + { + usage(from); + return; + } + if (!aport) + { + iport = 0; + } + + if (addc == '-') + { + if (!serverlist) + { + to_user(from,TEXT_EMPTYSERVLIST); + return; + } + n = 0; + dp = NULL; + for(sp=serverlist;sp;sp=sp->next) + { + if ((!Strcasecmp(server,sp->name)) || (!Strcasecmp(server,sp->realname))) + { + if (!iport || (iport && sp->port == iport)) + { + dp = sp; + n++; + } + } + } + switch(n) + { + case 0: + to_user(from,(iport) ? TEXT_NOSERVMATCHP : TEXT_NOSERVMATCH,server,iport); + break; + case 1: + to_user(from,TEXT_SERVERDELETED,server,dp->port); + for(spp=&serverlist;*spp;spp=&(*spp)->next) + { + if (*spp == dp) + { + *spp = dp->next; + Free((void*)&dp); + break; + } + } + break; + default: + to_user(from,TEXT_MANYSERVMATCH,server); + } + return; + } + sp = add_server(server,iport,pass); + if (!sp) + { + to_user(from,"Problem adding server: %s",server); + return; + } + if (addc || from == CoreUser.name) + return; + + current->nextserver = sp->ident; + to_user(from,"Trying new server: %s on port %i",server,iport); +do_server_jump: + switch(current->connect) + { + case CN_CONNECTED: + case CN_ONLINE: + to_server("QUIT :%s\n",quitmsg); + killsock(current->sock); + break; + default: + if (current->sock != -1) + close(current->sock); + } + current->sock = -1; +} + +void do_cserv(COMMAND_ARGS) +{ + Server *sp; + + if ((sp = find_server(current->server))) + to_user(from,TEXT_CSERV,(*sp->realname) ? sp->realname : sp->name,sp->port); + else + to_user(from,TEXT_CSERVNOT); +} + +void do_away(COMMAND_ARGS) +{ + if (!rest || !*rest) + { + to_server("AWAY\n"); + to_user(from,TEXT_NOLONGERAWAY); + current->away = FALSE; + current->activity = now; + return; + } + to_server(AWAYFORM,rest,time2away(now)); + to_user(from,TEXT_NOWSETAWAY); + current->away = TRUE; +} + +void do_do(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + GAXS + */ + to_server(FMT_PLAINLINE,rest); +} + +void do_nick(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + CARGS + */ + Mech *backup; + char *nick; + int guid; + + nick = chop(&rest); + if (!nick || !*nick) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + guid = a2i(nick); + backup = current; + if (!errno) + { + nick = chop(&rest); + for(current=botlist;current;current=current->next) + { + if (current->guid == guid) + break; + if (current->guid == 0) + break; + } + } + if (!is_nick(nick)) + { + current = backup; + to_user(from,ERR_NICK,nick); + return; + } + if (!current) + { +#ifdef DEBUG + debug("(do_nick) Adding new bot: guid = %i; nick = %s\n",guid,nick); +#endif /* DEBUG */ + current = add_bot(guid,nick); + if (!sigmaster) + sigmaster = guid; + if (from == CoreUser.name) + return; + } + else + { + if (current->guid == 0) + { + Free((char**)¤t->nick); + set_mallocdoer(do_nick); + current->nick = Strdup(nick); + current->guid = guid; + } + Free((char**)¤t->wantnick); + set_mallocdoer(do_nick); + current->wantnick = Strdup(nick); + to_server("NICK %s\n",current->wantnick); + } + current = backup; +} + +void do_time(COMMAND_ARGS) +{ + to_user_q(from,"Current time: %s",time2away(now)); +} + +void do_upontime(COMMAND_ARGS) +{ + to_user_q(from,CurrentCmd->cmdarg, + idle2str(now - ((CurrentCmd->name == C_UPTIME) ? uptime : current->ontime),FALSE)); +} + +void do_msg(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + Client *client; + + to = chop(&rest); + if (ischannel(to)) + { + /* + * need to check channel access + */ + if (get_authaccess(from,to) < cmdaccess) + return; + } + + if (*to == '=') + { + to++; + if ((client = find_client(to)) == NULL) + { + to_user(from,"I have no DCC connection to %s",to); + return; + } + } + CoreChan.name = to; + CurrentChan = (Chan*)&CoreChan; + to_user_q(to,FMT_PLAIN,rest); + to_user(from,"Message sent to %s",to); +} diff --git a/src/ctcp.c b/src/ctcp.c new file mode 100644 index 0000000..e7a2918 --- /dev/null +++ b/src/ctcp.c @@ -0,0 +1,1041 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define CTCP_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +int dcc_only_command(char *from) +{ +#ifdef REDIRECT + if (!redirect.to) +#endif /* REDIRECT */ + if (!CurrentDCC) + { + to_user(from,TEXT_DCC_ONLY,CurrentCmd->name); + return(TRUE); + } + return(FALSE); +} + +Client *find_client(const char *nick) +{ + Client *client; + + if (CurrentDCC && CurrentDCC->user->name == nick) + return(CurrentDCC); + + for(client=current->clientlist;client;client=client->next) + { + if (((client->flags & DCC_SEND) == 0) && !Strcasecmp(nick,client->user->name)) + return(client); + } + return(NULL); +} + +void delete_client(Client *client) +{ + Client **pp; + Spy *spy,**pspy; + +#ifdef DEBUG + if (client->sock >= 0) + debug("(delete_client) closing {%i}\n",client->sock); +#endif /* DEBUG */ + + pp = ¤t->clientlist; + while(*pp) + { + if (*pp == client) + { + *pp = client->next; + break; + } + pp = &(*pp)->next; + } + + if (client->user) + { + pspy = ¤t->spylist; + while(*pspy) + { + spy = *pspy; + /* + * for DCC spy dest we use the exact same pointer as in the userlist + */ + if (spy->dest == client->user->name) + { + *pspy = spy->next; + Free((char**)&spy); + continue; + } + pspy = &(*pspy)->next; + } + send_global(SPYSTR_STATUS,"[%s] %s[%i] has disconnected", + current->nick,client->user->name,client->user->x.x.access); + } +#ifdef DCC_FILE + if (client->fileno >= 0) + close(client->fileno); +#endif /* DCC_FILE */ + if (client->sock >= 0) + close(client->sock); + Free((char **)&client); +} + +void partyline_broadcast(Client *from, char *format, char *rest) +{ + Client *client; + Mech *bot; + + for(bot=botlist;bot;bot=bot->next) + { + for(client=bot->clientlist;client;client=client->next) + { + if (0 == (client->flags & (DCC_ACTIVE|DCC_TELNET))) + continue; + if (client == from && client->user->x.x.echo == FALSE) + continue; + to_file(client->sock,format,CurrentNick,rest); + } + } +} + +void dcc_banner(Client *client) +{ + char tmp[MSGLEN]; + + client->flags = DCC_ACTIVE; + client->lasttime = now; + + sprintf(tmp,"[%s] %s[%i] has connected", + current->nick,client->user->name,(int)client->user->x.x.access); + + if ((to_file(client->sock,"[%s] %s\n",time2medium(now),tmp)) < 0) + { + client->flags = DCC_DELETE; + return; + } + send_global(SPYSTR_STATUS,tmp); + if (client->user->x.x.access == OWNERLEVEL) + { + CurrentDCC = client; + Strcpy(tmp,SPYSTR_STATUS); + do_spy(client->user->name,current->nick,tmp,0); + CurrentDCC = NULL; + } +} + +#ifdef DCC_FILE + +int dcc_sendfile(char *target, char *filename) +{ + struct sockaddr_in sai; + Client *client; + int s,f,sz; + char tempfile[Strlen2(filename,DCC_PUBLICFILES)+2]; + + Strcpy(tempfile,DCC_PUBLICFILES); + Strcat(tempfile,filename); + +#ifdef DEBUG + debug("(dcc_sendfile) opening %s for transfer\n",tempfile); +#endif /* DEBUG */ + if ((f = open(tempfile,O_RDONLY)) < 0) + return -1; + if ((s = SockListener(0)) < 0) + return -1; + + sz = sizeof(sai); + if (getsockname(s,(struct sockaddr *)&sai,&sz) < 0) + { + close(s); + return -1; + } + + set_mallocdoer(dcc_sendfile); + client = (Client*)Calloc(sizeof(Client) + Strlen2(filename,target)); + + client->fileno = f; + client->sock = s; + client->user = NULL; + client->flags = DCC_WAIT|DCC_ASYNC|DCC_SEND; + client->lasttime = now; + client->whom = Strcpy(client->filename,filename) + 1; + Strcpy(client->whom,target); + + client->next = current->clientlist; + current->clientlist = client; + + sz = lseek(f,0,SEEK_END); + lseek(f,0,SEEK_SET); + client->fileend = sz; + + to_server("PRIVMSG %s :\001DCC SEND %s %u %u %i\001\n",target, + filename,htonl(current->ip.s_addr),ntohs(sai.sin_port),sz); + return(sz); +} + +void dcc_pushfile(Client *client, off_t where) +{ + char tempdata[16384]; + int sz; + + sz = (where + 16384) - lseek(client->fileno,0,SEEK_CUR); + sz = read(client->fileno,tempdata,sz); + if (sz < 1) + return; + write(client->sock,tempdata,sz); +} + +int dcc_freeslots(int uaccess) +{ + Client *client; + int n; + + n = (uaccess > 0) ? current->setting[INT_DCCUSER].int_var : 0; + n += current->setting[INT_DCCANON].int_var; + for(client=current->clientlist;client;client=client->next) + { + if (client->flags & (DCC_SEND|DCC_RECV)) + n--; + } + return(n); +} + +#endif /* DCC_FILE */ + +void parse_dcc(Client *client) +{ + char text[MSGLEN]; + char *ptr,*bp; + int s,oc; + + if (client->flags & DCC_WAIT) + { +#ifdef DCC_FILE + if (client->flags & DCC_RECV) + { + client->flags = DCC_SEND|DCC_RECV; + return; + } +#endif /* DCC_FILE */ +#ifdef DEBUG +#ifdef DCC_FILE + if (client->flags & DCC_SEND) + debug("(parse_dcc) new dcc filetransfer connecting\n"); + else +#endif /* DCC_FILE */ + debug("(parse_dcc) new user connecting\n"); +#endif /* DEBUG */ + s = SockAccept(client->sock); + close(client->sock); + client->sock = s; + if (s == -1) + { + client->flags = DCC_DELETE; + return; + } + + if ((client->flags & DCC_SEND) == 0) + { + /* + * tell them how much we love them + */ + dcc_banner(client); + } +#ifdef DCC_FILE + else + { + client->flags = DCC_SEND; + client->start = now; + dcc_pushfile(client,0); + } +#endif /* DCC_FILE */ + return; + } + + /* + * read data from socket + */ +#ifdef DCC_FILE + if (client->flags & DCC_RECV) + { + char bigtemp[4096]; + ulong where; + + do + { + s = read(client->sock,bigtemp,4096); + if (s > 0) + write(client->fileno,bigtemp,s); + } + while(s > 0); + + if ((s < 0) && (errno != EINTR) && (errno != EAGAIN)) + client->flags = DCC_DELETE; + + where = oc = lseek(client->fileno,0,SEEK_CUR); + where = htonl(where); + write(client->sock,&where,4); + client->lasttime = now; + + if (oc == client->fileend) + { +#ifdef DEBUG + debug("(parse_dcc) DCC file recv `%s' completed in %lu seconds (%i bytes)\n", + client->filename,(client->lasttime - client->start),client->fileend); +#endif /* DEBUG */ + client->flags = DCC_DELETE; + } + return; + } + if (client->flags & DCC_SEND) + { + ulong where; + + client->lasttime = now; + s = client->inputcount; + oc = read(client->sock,(client->sockdata+s),(4-s)); + if ((oc < 1) && (errno != EINTR) && (errno != EAGAIN)) + { + client->flags = DCC_DELETE; + return; + } + oc += s; + if (oc == 4) + { + where = ((uchar)client->sockdata[0] << 24) | + ((uchar)client->sockdata[1] << 16) | + ((uchar)client->sockdata[2] << 8) | + (uchar)client->sockdata[3]; + client->inputcount = 0; + if (client->fileend == where) + { +#ifdef TCL + tcl_dcc_complete(client,0); +#endif /* TCL */ +#ifdef DEBUG + debug("(parse_dcc) dcc_sendfile: end of file\n"); +#endif /* DEBUG */ + client->flags = DCC_DELETE; + } + else + dcc_pushfile(client,where); + } + else + { +#ifdef DEBUG + debug("(parse_dcc) DCC_SEND partial ack\n"); +#endif /* DEBUG */ + client->inputcount = oc; + } + return; + } +#endif /* DCC_FILE */ + ptr = sockread(client->sock,client->sockdata,text); + oc = errno; +#ifdef TELNET + if (ptr && (client->flags & DCC_TELNETPASS)) + { + check_telnet_pass(client,ptr); + } + else +#endif /* TELNET */ + if (ptr) + { + /* + * DCC input flood protection + */ + s = now - client->lasttime; + if (s > 10) + { + client->inputcount = strlen(ptr); + } + else + { + client->inputcount += strlen(ptr); + if ((client->inputcount - (s * DCC_INPUT_DECAY)) > DCC_INPUT_LIMIT) + { + client->flags = DCC_DELETE; + return; + } + } + /* + * set up source-destination information for on_msg/on_action + */ + CurrentShit = NULL; + CurrentChan = NULL; + client->lasttime = now; + CurrentDCC = client; + CurrentUser = client->user; + Strcpy(CurrentNick,CurrentUser->name); + + if (*ptr == 1) + { + bp = ptr; + chop(&bp); + ptr = get_token(&bp,"\001"); + on_action(CurrentUser->name,current->nick,ptr); + } + else + { + on_msg(CurrentUser->name,current->nick,ptr); + } + CurrentDCC = NULL; + /* + * get out of here ASAP, consider dangerous commands such as DIE and DEL (user) + */ + return; + } + switch(oc) + { + case EAGAIN: + case EINTR: + return; + default: + client->flags = DCC_DELETE; + return; + } +} + +void process_dcc(void) +{ + Client *client; + + for(client=current->clientlist;client;client=client->next) + { + if (FD_ISSET(client->sock,&read_fds)) + { + parse_dcc(client); + } + else + if ((client->flags & DCC_ASYNC) && (FD_ISSET(client->sock,&write_fds))) + { +#ifdef DEBUG + debug("(process_dcc) chat connected [ASYNC]\n"); +#endif /* DEBUG */ + dcc_banner(client); + } + else + if ((client->flags & DCC_WAIT) && ((now - client->lasttime) >= WAITTIMEOUT)) + { +#ifdef DEBUG + debug("(process_dcc) connection timed out (%s)\n", + (client->flags & (DCC_SEND|DCC_RECV)) ? "file transfer" : client->user->name); +#endif /* DEBUG */ + client->flags = DCC_DELETE; + } +#ifdef DCC_FILE + else + if ((client->flags & DCC_SEND) && ((now - client->lasttime) >= DCC_FILETIMEOUT)) + { +#ifdef DEBUG + debug("(process_dcc) {%i} DCC %s stalled (%s), closing connection\n", + client->sock,(client->flags & DCC_RECV) ? "RECV" : "SEND", + nullstr(client->filename)); +#endif /* DEBUG */ + client->flags = DCC_DELETE; + } +#endif /* DCC_FILE */ +#ifdef TELNET + else + if ((client->flags & DCC_TELNETPASS) && ((now - client->lasttime) >= TELNET_TIMEOUT)) + { + client->flags = DCC_DELETE; + } +#endif /* TELNET */ + } +} + +void dcc_chat(char *from) +{ + struct sockaddr_in sai; + Client *client; + User *user; + int sock,sz; + + if ((user = get_authuser(from,NULL)) == NULL) + return; + if (find_client(user->name)) + return; + + if ((sock = SockListener(0)) < 0) + return; + + sz = sizeof(sai); + if (getsockname(sock,(struct sockaddr *)&sai,&sz) < 0) + { + close(sock); + return; + } + + set_mallocdoer(dcc_chat); + client = (Client*)Calloc(sizeof(Client)); +#ifdef DCC_FILE + client->fileno = -1; +#endif /* DCC_FILE */ + client->user = user; + client->sock = sock; + client->flags = DCC_WAIT; + client->lasttime = now; + + client->next = current->clientlist; + current->clientlist = client; + + to_server("PRIVMSG %s :\001DCC CHAT CHAT %u %u\001\n", + CurrentNick,htonl(current->ip.s_addr),ntohs(sai.sin_port)); +} + +/* + * + * CTCP things... + * + */ + +void ctcp_dcc(char *from, char *to, char *rest) +{ + struct in_addr ia; + Client *client; + User *user; + char *addr,*port,ip_addr[20]; + ulong longip; + int x; +#ifdef DCC_FILE + char *filename; + int s,f,portnum,filesz; +#endif /* DCC_FILE */ + + if (!rest || !*rest) + return; + + addr = port = chop(&rest); + + while(*addr) + { + if (*addr >= 'a' && *addr <= 'z') + *addr = *addr - 0x20; + addr++; + } + + x = get_maxaccess(from); + send_spy(SPYSTR_STATUS,"[DCC] :%s[%i]: Requested DCC %s [%s]",CurrentNick,x,port,nullstr(rest)); + +#ifdef DCC_FILE + if (!Strcasecmp(port,"SEND")) + { +#ifdef DEBUG + debug("(ctcp_dcc) rest: `%s'\n",nullstr(rest)); +#endif /* DEBUG */ + filename = chop(&rest); + if (!is_safepath(filename)) + { +#ifdef DEBUG + debug("(ctcp_dcc) filename `%s' is not safe\n",filename); +#endif /* DEBUG */ + return; + } + /* + * check filename against masks in STR_DCCFILES + */ + s = 1; + if ((addr = current->setting[STR_DCCFILES].str_var)) + { + char tempmask[strlen(addr)+1]; + + Strcpy(tempmask,addr); + do + { + port = chop(&addr); + if (matches(port,filename) == 0) + { + s = 0; + break; + } + } + while(port && *port); + } + /* + * + */ + if (s) + return; + /* + * check for free DCC slots (x is maxuserlevel from above) + */ + if (dcc_freeslots(x) < 1) + return; + + addr = chop(&rest); + port = chop(&rest); + portnum = a2i(port); + x = errno; + filesz = a2i(rest); + if (errno || x || portnum < 1024 || portnum > 63353 || filesz <= 0) + return; + + longip = 0; + while(*addr) + { + longip = (longip * 10) + (*addr - '0'); + addr++; + } + ia.s_addr = ntohl(longip); + Strcpy(ip_addr,inet_ntoa(ia)); + + if (1) + { + char tempname[Strlen2(filename,DCC_PUBLICINCOMING)+1]; + + Strcpy(Strcpy(tempname,DCC_PUBLICINCOMING),filename); + + if ((f = open(tempname,O_RDWR|O_CREAT|O_EXCL,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) + return; + if ((s = SockConnect(ip_addr,portnum,FALSE)) < 0) + { + close(f); + return; + } + set_mallocdoer(ctcp_dcc); + client = (Client*)Calloc(sizeof(Client) + Strlen2(filename,from)); + client->fileno = f; + client->fileend = filesz; + client->sock = s; + client->flags = DCC_WAIT|DCC_SEND|DCC_RECV; + client->lasttime = client->start = now; + client->whom = Strcpy(client->filename,filename) + 1; + Strcpy(client->whom,from); + + client->next = current->clientlist; + current->clientlist = client; + } + } + else +#endif /* DCC_FILE */ + if (x && (x < BOTLEVEL) && !Strcasecmp(port,"CHAT")) + { + if ((user = get_authuser(from,NULL)) == NULL) + { + to_user(from,"Use \"VERIFY\" to get verified first"); + return; + } + + if (find_client(user->name)) + return; + + chop(&rest); /* discard "chat" */ + addr = chop(&rest); + port = chop(&rest); + if (!port || !*port) + return; + + x = a2i(port); + if (errno || (x < 1024) || (x > 65535)) + return; + + /* + * quick hack; if someone sends a fake/non-numeric long_ip, this all just fails + * and we couldnt care less. blaha. + */ + longip = 0; + while(*addr) + { + longip = (longip * 10) + (*addr - '0'); + addr++; + } + ia.s_addr = ntohl(longip); + Strcpy(ip_addr,inet_ntoa(ia)); + +#ifdef DEBUG + debug("(ctcp_dcc) %s [%s,%s]\n",from,ip_addr,port); +#endif /* DEBUG */ + + if ((x = SockConnect(ip_addr,x,FALSE)) < 0) + return; + + set_mallocdoer(ctcp_dcc); + client = (Client*)Calloc(sizeof(Client)); +#ifdef DCC_FILE + client->fileno = -1; +#endif /* DCC_FILE */ + client->sock = x; + client->user = user; + client->flags = DCC_WAIT|DCC_ASYNC; + client->lasttime = now; + client->next = current->clientlist; + current->clientlist = client; + } +} + +#ifdef CTCP + +void ctcp_finger(char *from, char *to, char *rest) +{ + char *reply; + int maxul; + + /* + * checking for a spylist first saves a few clocks + */ + if (current->spy & SPYF_STATUS) + { + maxul = get_maxaccess(from); + send_spy(SPYSTR_STATUS,"[CTCP] :%s[%i]: Requested Finger Info",CurrentNick,maxul); + } + + if (!current->setting[TOG_CTCP].int_var) + return; + + reply = (current->setting[TOG_RF].int_var) ? randstring(VERSIONFILE) : NULL; + to_server("NOTICE %s :\001FINGER %s\001\n",CurrentNick,(reply) ? reply : EXFINGER); +} + +void ctcp_ping(char *from, char *to, char *rest) +{ + int maxul; + + if (!rest || !*rest || (strlen(rest) > 100)) + return; + + /* + * this kludge saves a few clock cycles + */ + maxul = ASSTLEVEL; + if (current->spylist || !current->setting[TOG_CTCP].int_var) + { + maxul = get_maxaccess(from); + if (current->spy & SPYF_STATUS) + send_spy(SPYSTR_STATUS,"[CTCP] :%s[%i]: Requested Ping Info",CurrentNick,maxul); + } + + if (maxul >= ASSTLEVEL) + { + to_server("NOTICE %s :\001PING %s\001\n",CurrentNick,(rest) ? rest : ""); + } +} + +void ctcp_version(char *from, char *to, char *rest) +{ + char *reply; + int maxul; + + /* + * checking for a spylist first saves a few clocks + */ + if (current->spy & SPYF_STATUS) + { + maxul = get_maxaccess(from); + send_spy(SPYSTR_STATUS,"[CTCP] :%s[%i]: Requested Version Info",CurrentNick,maxul); + } + + if (!current->setting[TOG_CTCP].int_var) + return; + + reply = (current->setting[TOG_RV].int_var) ? randstring(VERSIONFILE) : NULL; + to_server("NOTICE %s :\001VERSION %s\001\n",CurrentNick,(reply) ? reply : EXVERSION); +} + +#define NEED_SLOT_NO ,0 +#define NEED_SLOT_YES ,1 + +#else /* CTCP */ + +#define NEED_SLOT_NO /* nothing */ +#define NEED_SLOT_YES /* nothing */ + +#endif /* CTCP */ + +LS const struct +{ + char *name; + void (*func)(char *, char *, char *); +#ifdef CTCP + int need_slot; +#endif /* CTCP */ + +} ctcp_commands[] = +{ +/* + * most common types first + */ +{ "ACTION", on_action NEED_SLOT_NO }, +#ifdef CTCP +{ "PING", ctcp_ping NEED_SLOT_YES }, +{ "VERSION", ctcp_version NEED_SLOT_YES }, +{ "FINGER", ctcp_finger NEED_SLOT_YES }, +#endif /* CTCP */ +{ "DCC", ctcp_dcc NEED_SLOT_NO }, +{ NULL, }}; + +void on_ctcp(char *from, char *to, char *rest) +{ + char *command,*p; + int i,mul; + + /* + * some of these stupid CTCP's have leading spaces, bad! + */ + while(*rest == ' ') + rest++; + + i = FALSE; + p = command = rest; + for(;*p;p++) + { + if (*p == ' ' && !i) + { + i = TRUE; + *p = 0; + rest = p + 1; + while(*rest == ' ') + rest++; + } + if (*p == 1) + { + *p = 0; + break; + } + } + for(i=0;ctcp_commands[i].name;i++) + { + if (!Strcasecmp(ctcp_commands[i].name,command)) + { +#ifdef CTCP + if (ctcp_commands[i].need_slot) + { + for(mul=0;mul=CTCP_SLOTS) + { +#ifdef DEBUG + debug("(on_ctcp) ctcp_slot for %s not found, being flooded?\n", + nullstr(command)); +#endif /* DEBUG */ + return; + } + } +#endif /* CTCP */ + ctcp_commands[i].func(from,to,rest); + return; + } + } + if (current->spy & SPYF_STATUS) + { + mul = get_maxaccess(from); + send_spy(SPYSTR_STATUS,"[CTCP] :%s[%i]: Unknown [%s]",CurrentNick,mul,command); + } +} + +/* + * + * commands associated with CTCP and DCC + * + */ + +void do_chat(COMMAND_ARGS) +{ + User *user; + + user = get_authuser(from,NULL); + if (!user) + return; + if (find_client(user->name)) + { + to_user(from,"You are already DCC chatting me"); + return; + } + dcc_chat(from); +} + +#ifdef CTCP + +void do_ping_ctcp(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *target; + + if ((target = chop(&rest))) + { + if (CurrentCmd->name == C_PING || !Strcasecmp(rest,"PING")) + { + to_server("PRIVMSG %s :\001PING %lu\001\n",target,now); + return; + } + if (*rest) + { + to_server("PRIVMSG %s :\001%s\001\n",target,rest); + return; + } + } + usage(from); +} + +#endif /* CTCP */ + +#ifdef BOTNET + +void whom_printbot(char *from, BotInfo *binfo, char *stt) +{ + char *us; + int uaccess; + + us = ""; + if (binfo->nuh) + { + uaccess = get_maxaccess(binfo->nuh); + if (uaccess == BOTLEVEL) + us = "b200"; + else + if (uaccess) + sprintf((us = stt),"u%i",uaccess); + } + uaccess = get_authaccess(from,MATCH_ALL); + table_buffer((uaccess >= ASSTLEVEL) ? TEXT_WHOMBOTGUID : TEXT_WHOMBOTLINE,(binfo->nuh) ? nickcpy(NULL,binfo->nuh) : "???",us, + (binfo->server) ? binfo->server : "???",(binfo->version) ? binfo->version : "???",binfo->guid); +} + +#endif /* BOTNET */ + +void do_whom(COMMAND_ARGS) +{ +#ifdef BOTNET + BotNet *bn; + BotInfo *binfo; +#endif /* BOTNET */ + Server *sp; + char stt[NUHLEN]; + Client *client; + Mech *bot; + int m,s; + + if (dcc_only_command(from)) + return; + + for(bot=botlist;bot;bot=bot->next) + { + if (bot->connect == CN_ONLINE) + { + sp = find_server(bot->server); + if (sp) + { + sprintf(stt,"%s:%i",(*sp->realname) ? sp->realname : sp->name,sp->port); + } + else + { + Strcpy(stt,TEXT_NOTINSERVLIST); + } + } + else + { + Strcpy(stt,TEXT_NOTCONNECTED); + } + table_buffer(TEXT_WHOMSELFLINE,bot->nick,(bot == current) ? "(me)" : "b200",stt); + for(client=bot->clientlist;client;client=client->next) + { + m = (now - client->lasttime) / 60; + s = (now - client->lasttime) % 60; + table_buffer(TEXT_WHOMUSERLINE, +#ifdef TELNET + client->user->name,client->user->x.x.access,(client->flags & DCC_TELNET) ? "telnet" : "DCC",m,s); +#else + client->user->name,client->user->x.x.access,"DCC",m,s); +#endif /* TELNET */ + } + } +#ifdef BOTNET + for(bn=botnetlist;bn;bn=bn->next) + { + if (bn->status != BN_LINKED) + continue; + for(binfo=bn->botinfo;binfo;binfo=binfo->next) + whom_printbot(from,binfo,stt); + } +#endif /* BOTNET */ + table_send(from,3); +} + +void do_bye(COMMAND_ARGS) +{ + if (CurrentDCC) + { + to_user(from,TEXT_DCC_GOODBYE); + CurrentDCC->flags = DCC_DELETE; + } +} + +#ifdef DCC_FILE + +void do_send(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *filename,*target; + int sz; + + + target = chop(&rest); + filename = chop(&rest); + if (!filename) + { + if (CurrentDCC) + { + to_user_q(from,"Unable to send files over DCC connections"); + return; + } + filename = target; + target = CurrentNick; + } + if (dcc_freeslots(get_maxaccess(from)) < 1) + { + to_user_q(from,"Unable to send: No free DCC slots"); + return; + } + sz = dcc_sendfile(target,filename); + if (sz < 0) + { + to_user_q(from,"Unable to locate file: %s",filename); + return; + } + if (target == CurrentNick) + { + to_user_q(from,"Sending %s (%i bytes)",filename,sz); + } + else + { + to_user_q(from,"Sending %s (%i bytes) to %s",filename,sz,target); + } +} + +#endif /* DCC_FILE */ diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..0067513 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,1337 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define DEBUG_C +#include "config.h" + +#ifdef DEBUG + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "settings.h" + +#define boolstr(x) (x) ? "TRUE" : "FALSE" + +LS const char tabs[20] = "\t\t\t\t\t\t\t\t\t\t"; + +LS const struct +{ + char *name; + int size; + +} StructList[] = +{ +{ "Auth", sizeof(Auth) }, +{ "Ban\t", sizeof(Ban) }, +{ "Chan", sizeof(Chan) }, +{ "ChanStats", sizeof(ChanStats) }, +{ "ChanUser", sizeof(ChanUser) }, +{ "Client", sizeof(Client) }, +{ "ircLink", sizeof(ircLink) }, +{ "IReq", sizeof(IReq) }, +{ "KillSock", sizeof(KillSock) }, +{ "Mech", sizeof(Mech) }, +#ifdef NOTIFY +{ "Notify", sizeof(Notify) }, +#endif /* NOTIFY */ +#ifdef SEEN +{ "Seen", sizeof(Seen) }, +#endif /* SEEN */ +{ "Server", sizeof(Server) }, +{ "ServerGroup", sizeof(ServerGroup) }, +{ "Setting", sizeof(Setting) }, +{ "Shit", sizeof(Shit) }, +{ "Strp", sizeof(Strp) }, +#ifdef TRIVIA +{ "TrivScore", sizeof(TrivScore) }, +#endif /* TRIVIA */ +{ "User", sizeof(User) }, +{ "OnMsg", sizeof(OnMsg) }, +{ "Spy\t", sizeof(Spy) }, +{ NULL, }}; + +LS struct +{ + void *func; + char *name; + int num; + int size; + int mall_size; + +} ProcList[] = +{ +{ SockConnect, "SockConnect" }, +{ add_bot, "add_bot" }, +{ add_server, "add_server" }, +{ add_shit, "add_shit" }, +{ add_user, "add_user" }, +{ addtouser, "addtouser" }, +{ cfg_opt, "cfg_opt" }, +{ cfg_pass, "cfg_pass" }, +{ cfg_user, "cfg_user" }, +{ change_authnick, "change_authnick" }, +{ ctcp_dcc, "ctcp_dcc" }, +{ copy_vars, "copy_vars" }, +{ dcc_chat, "dcc_chat" }, +{ do_die, "do_die" }, +{ do_nick, "do_nick" }, +{ do_kicksay, "do_kicksay" }, +{ do_servergroup, "do_servergroup" }, +{ do_set, "do_set" }, +{ do_spy, "do_spy" }, +{ join_channel, "join_channel" }, +{ killsock, "killsock" }, +{ make_auth, "make_auth" }, +{ make_ban, "make_ban" }, +{ make_chanuser, "make_chanuser" }, +{ make_ireq, "make_ireq" }, +{ mirror_user, "mirror_user" }, +{ on_join, "on_join" }, +{ on_kick, "on_kick" }, +{ on_mode, "on_mode" }, +{ on_msg, "on_msg" }, +{ on_nick, "on_nick" }, +{ parse_311, "parse_311" }, +{ randstring_getline, "randstring_getline" }, +{ readcfgfile, "readcfgfile" }, +{ reverse_topic, "reverse_topic" }, +{ to_user, "to_user" }, +{ to_user_q, "to_user_q" }, +{ send_kick, "send_kick" }, +{ send_mode, "send_mode" }, +{ set_str_varc, "set_str_varc" }, +{ setbotnick, "setbotnick" }, +{ sig_hup, "sig_hup" }, +{ table_buffer, "table_buffer" }, +#ifdef ALIAS +{ do_alias, "do_alias" }, +#endif /* ALIAS */ +#ifdef BOTNET +{ botnet_newsock, "botnet_newsock" }, +{ connect_to_bot, "connect_to_bot" }, +{ do_link, "do_link" }, +{ make_botinfo, "make_botinfo" }, +#endif /* BOTNET */ +#ifdef BOUNCE +{ bounce_parse, "bounce_parse" }, +{ process_bounce, "process_bounce" }, +#endif /* BOUNCE */ +#ifdef CHANBAN +{ process_chanbans, "process_chanbans" }, +#endif /* CHANBAN */ +#ifdef GREET +{ cfg_greet, "cfg_greet" }, +{ do_greet, "do_greet" }, +#endif /* GREET */ +#ifdef NOTE +{ catch_note, "catch_note" }, +{ do_note, "do_note" }, +#endif /* NOTE */ +#ifdef NOTIFY +{ catch_whois, "catch_whois" }, +{ notify_callback, "notify_callback" }, +{ notifylog_callback, "notifylog_callback" }, +#endif /* NOTIFY */ +#ifdef PYTHON +{ python_hook, "python_hook" }, +{ python_unhook, "python_unhook" }, +#endif /* PYTHON */ +#ifdef RAWDNS +{ rawdns, "rawdns" }, +{ parse_query, "parse_query" }, +{ read_dnsroot, "read_dnsroot" }, +#endif /* RAWDNS */ +#ifdef SEEN +{ make_seen, "make_seen" }, +#endif /* SEEN */ +#ifdef STATS +{ stats_plusminususer, "stats_plusminususer" }, +#endif /* STATS */ +#ifdef TCL +{ tcl_hook, "tcl_hook" }, +#endif /* TCL */ +#ifdef TELNET +{ check_telnet, "check_telnet" }, +#endif /* TELNET */ +#ifdef TOYBOX +{ read_bigcharset_callback, "read_bigcharset_callback" }, +#endif /* TOYBOX */ +#ifdef TRIVIA +{ trivia_check, "trivia_check" }, +{ trivia_question, "trivia_question" }, +{ trivia_score_callback, "trivia_score_callback" }, +#endif /* TRIVIA */ +#ifdef UPTIME +{ init_uptime, "init_uptime" }, +{ send_uptime, "send_uptime" }, +#endif /* UPTIME */ +{ 0, "(unknown)" }, +{ NULL, }}; + +#ifdef SCRIPTING +LS const DEFstruct SCRIPTdefs[] = +{ +{ HOOK_PARSE, "HOOK_PARSE" }, +{ HOOK_TIMER, "HOOK_TIMER" }, +{ HOOK_COMMAND, "HOOK_COMMAND" }, +{ HOOK_BOTNET, "HOOK_BOTNET" }, +{ HOOK_DCC_COMPLETE, "HOOK_DCC_COMPLETE" }, +#ifdef TCL +{ (int)tcl_timer_jump, "tcl_timer_jump" }, +{ (int)tcl_parse_jump, "tcl_parse_jump" }, +#endif /* TCL */ +#ifdef PYTHON +{ (int)python_timer_jump, "python_timer_jump" }, +{ (int)python_parse_jump, "python_parse_jump" }, +#endif /* PYTHON */ +{ 0, }}; +#endif /* SCRIPTING */ + +LS const DEFstruct CNdefs[] = +{ +{ CN_NOSOCK, "CN_NOSOCK" }, +{ CN_DNSLOOKUP, "CN_DNSLOOKUP" }, +{ CN_TRYING, "CN_TRYING" }, +{ CN_CONNECTED, "CN_CONNECTED" }, +{ CN_ONLINE, "CN_ONLINE" }, +{ CN_DISCONNECT, "CN_DISCONNECT" }, +{ CN_BOTDIE, "CN_BOTDIE" }, +{ CN_NEXTSERV, "CN_NEXTSERV" }, +{ 0, }}; + +LS const DEFstruct SPdefs[] = +{ +{ SP_NULL, "SP_NULL" }, +{ SP_NOAUTH, "SP_NOAUTH" }, +{ SP_KLINED, "SP_KLINED" }, +{ SP_FULLCLASS, "SP_FULLCLASS" }, +{ SP_TIMEOUT, "SP_TIMEOUT" }, +{ SP_ERRCONN, "SP_ERRCONN" }, +{ SP_DIFFPORT, "SP_DIFFPORT" }, +{ 0, }}; + +#ifdef NOTIFY +LS const DEFstruct NFdefs[] = +{ +{ NF_OFFLINE, "NF_OFFLINE" }, +{ NF_WHOIS, "NF_WHOIS" }, +{ NF_MASKONLINE, "NF_MASKONLINE" }, +{ NF_NOMATCH, "NF_NOMATCH" }, +{ 0, }}; +#endif /* NOTIFY */ + +#ifdef SEEN +LS const DEFstruct SEdefs[] = +{ +{ SEEN_PARTED, "SEEN_PARTED" }, +{ SEEN_QUIT, "SEEN_QUIT" }, +{ SEEN_NEWNICK, "SEEN_NEWNICK" }, +{ SEEN_KICKED, "SEEN_KICKED" }, +{ 0, }}; +#endif /* SEEN */ + +#ifdef BOTNET +LS const DEFstruct BNdefs[] = +{ +{ BN_UNKNOWN, "BN_UNKNOWN" }, +{ BN_DEAD, "BN_DEAD" }, +{ BN_LINKSOCK, "BN_LINKSOCK" }, +{ BN_CONNECT, "BN_CONNECT" }, +{ BN_BANNERSENT, "BN_BANNERSENT" }, +{ BN_WAITAUTH, "BN_WAITAUTH" }, +{ BN_WAITLINK, "BN_WAITLINK" }, +{ BN_LINKED, "BN_LINKED" }, +{ 0, }}; +#endif /* BOTNET */ + +LS const DEFstruct dcc_flags[] = +{ +{ DCC_SEND, "DCC_SEND" }, +{ DCC_RECV, "DCC_RECV" }, +{ DCC_WAIT, "DCC_WAIT" }, +{ DCC_ASYNC, "DCC_ASYNC" }, +{ DCC_ACTIVE, "DCC_ACTIVE" }, +{ DCC_TELNET, "DCC_TELNET" }, +{ DCC_TELNETPASS, "DCC_TELNETPASS" }, +{ DCC_DELETE, "DCC_DELETE" }, +{ 0, }}; + +LS const DEFstruct ircx_flags[] = +{ +{ IRCX_WALLCHOPS, "IRCX_WALLCHOPS" }, +{ IRCX_WALLVOICES, "IRCX_WALLVOICES" }, +{ IRCX_IMODE, "IRCX_IMODE" }, +{ IRCX_EMODE, "IRCX_EMODE" }, +{ 0, }}; + +LS const DEFstruct chanuser_flags[] = +{ +{ CU_VOICE, "CU_VOICE" }, +{ CU_CHANOP, "CU_CHANOP" }, +{ CU_VOICETMP, "CU_VOICETMP" }, +{ CU_NEEDOP, "CU_NEEDOP" }, +{ CU_MODES, "CU_MODES" }, +{ CU_DEOPPED, "CU_DEOPPED" }, +{ CU_KICKED, "CU_KICKED" }, +{ CU_BANNED, "CU_BANNED" }, +{ CU_MASSTMP, "CU_MASSTMP" }, +{ 0, }}; + +void strflags(char *dst, const DEFstruct *flagsstruct, int flags) +{ + int i; + + *dst = 0; + for(i=0;(flagsstruct[i].id);i++) + { + if (flagsstruct[i].id & flags) + { + if (*dst) + Strcat(dst,"|"); + Strcat(dst,flagsstruct[i].idstr); + } + } +} + +const char *strdef(const DEFstruct *dtab, int num) +{ + int i; + + for(i=0;(dtab[i].idstr);i++) + { + if (dtab[i].id == num) + return(dtab[i].idstr); + } + return("UNKNOWN"); +} + +void memreset(void) +{ + aMEA *mp; + int i; + + for(mp=mrrec;mp;mp=mp->next) + { + for(i=0;imme[i].touch = FALSE; + } +} + +LS void *mem_lowptr; +LS void *mem_hiptr; + +void memtouch(void *addr) +{ + aMEA *mp; + int i; + + if (addr == NULL) + return; + addr = addr - 4; + for(mp=mrrec;mp;mp=mp->next) + { + for(i=0;imme[i].area == addr) + { + mp->mme[i].touch = TRUE; + if (mem_hiptr < addr) + mem_hiptr = addr; + if (mem_lowptr > addr) + mem_lowptr = addr; + return; + } + } + } +} + +const char *proc_lookup(void *addr, int size) +{ + int i; + + for(i=0;ProcList[i].name;i++) + { + if (ProcList[i].func == addr) + { + ProcList[i].size += size; + ProcList[i].mall_size += size + 8 + ((size & 7) ? (8 - (size & 7)) : 0); + ProcList[i].num++; + return(ProcList[i].name); + } + } + return(NULL); +} + +char *atime(time_t when) +{ + char *pt,*zp; + + pt = ctime(&when); + zp = STRCHR(pt,'\n'); + *zp = 0; + return(pt); +} + +void debug_server(Server *sp, char *pad) +{ + ServerGroup *sg; + char *pl; + + if (!sp) + { + debug("%s; ---\n",pad); + return; + } + pl = (strlen(pad) == 2) ? "\t" : ""; + debug("%s; Server*\t\t"mx_pfmt"\n",pad,(mx_ptr)sp); + debug("%s; next\t\t"mx_pfmt"\n",pad,(mx_ptr)sp->next); + debug("%s; ident\t\t%i\n",pad,sp->ident); + debug("%s; name\t\t\"%s\"\n",pad,nullbuf(sp->name)); + debug("%s; pass\t\t\"%s\"\n",pad,nullbuf(sp->pass)); + debug("%s; realname\t\t\"%s\"\n",pad,nullbuf(sp->realname)); + debug("%s; usenum\t\t%i\n",pad,sp->usenum); + sg = getservergroupid(sp->servergroup); + if (sg) + { + debug("%s; servergroup\t%s%i \"%s\"\n",pad,pl,sp->servergroup,sg->name); + } + else + { + debug("%s; servergroup\t%s%i (unknown)\n",pad,pl,sp->servergroup); + } + debug("%s; port\t\t%i\n",pad,sp->port); + debug("%s; err\t\t%s%s (%i)\n",pad,pl,strdef(SPdefs,sp->err),sp->err); + debug("%s; lastconnect\t%s%s (%lu)\n",pad,pl,atime(sp->lastconnect),sp->lastconnect); + debug("%s; lastattempt\t%s%s (%lu)\n",pad,pl,atime(sp->lastattempt),sp->lastattempt); + debug("%s; maxontime\t\t%s (%lu)\n",pad,idle2str(sp->maxontime,FALSE),sp->maxontime); + debug("%s; ---\n",pad); +} + +#define DSET_PROC 0 +#define DSET_GLOBAL 1 +#define DSET_CHAN 2 + +void debug_settings(UniVar *setting, int type) +{ + UniVar *varval; + const char *tpad; + char *pad; + int i,n; + + i = CHANSET_SIZE; + switch(type) + { + case DSET_PROC: + pad = " "; + break; + case DSET_GLOBAL: + pad = " "; + break; + default: + i = 0; + pad = " "; + } + + debug("%s> setting\n",pad+2); + for(;VarName[i].name;i++) + { + if ((type == DSET_CHAN) && (i >= CHANSET_SIZE)) + break; + if (IsProc(i)) + { + if (type == DSET_GLOBAL) + continue; + } + else + { + if (type == DSET_PROC) + continue; + } + + tpad = STREND(tabs); + n = 24 - (Strlen2(pad,VarName[i].name) + 2); + while(n >= 8) + { + n = n - 8; + tpad--; + } + if (n > 0) + tpad--; + + varval = (IsProc(i)) ? setting[i].proc_var : &setting[i]; + + if (IsChar(i)) + { + debug("%s; %s%s`%c'\n",pad,VarName[i].name,tpad,varval->int_var); + } + else + if (IsTog(i)) + { + debug("%s; %s%s%s\n",pad,VarName[i].name,tpad,boolstr(varval->int_var)); + } + else + if (IsInt(i)) + { + debug("%s; %s%s%i\n",pad,VarName[i].name,tpad,varval->int_var); + } + else + if (IsStr(i)) + { + memtouch(setting[i].str_var); + debug("%s; %s%s"mx_pfmt" \"%s\"\n",pad,VarName[i].name,tpad, + (mx_ptr)varval->str_var,nullstr(varval->str_var)); + } + } + debug("%s; ---\n",pad); +} + +void debug_memory(void) +{ +#ifdef ALIAS + Alias *alias; +#endif /* ALIAS */ + Chan *chan; + Mech *bot; + aMEA *mea; + char t[100],*pt,*funcname; + int *hc; + int i,n; + +#ifdef ALIAS + for(alias=aliaslist;alias;alias=alias->next) + { + memtouch(alias); + memtouch(alias->alias); + memtouch(alias->format); + } +#endif /* ALIAS */ + for(bot=botlist;bot;bot=bot->next) + { + for(i=CHANSET_SIZE;VarName[i].name;i++) + { + if (IsStr(i)) + memtouch(bot->setting[i].str_var); + } + for(chan=bot->chanlist;chan;chan=chan->next) + { + for(i=0;isetting[i].str_var); + } + } + for(i=0;ilastcmds[i]); + } + } + debug("> Memory allocations\n"); + for(mea=mrrec;(mea);mea=mea->next) + { + for(i=0;imme[i].area) + { + hc = mea->mme[i].area; + funcname = (char*)proc_lookup(mea->mme[i].doer,mea->mme[i].size); + if (funcname) + { + Strcpy(t,funcname); + } + else + { + sprintf(t,mx_pfmt,(mx_ptr)mea->mme[i].doer); + } + pt = (char*)&tabs[10 - ((31 - strlen(t)) / 8)]; + debug(" ; "mx_pfmt"\t\t%s%s\t%i\t%s\t%s (%lu) %s\n", + (mx_ptr)mea->mme[i].area, + t,pt,mea->mme[i].size,(mea->mme[i].touch) ? "" : "(Leak)", + atime(mea->mme[i].when),mea->mme[i].when,(*hc) ? "HEAP CORRUPTION" : ""); + } + } + } + debug(" ; ---\n"); + debug("> Memory by Function\n"); + for(i=0;ProcList[i].name;i++) + { + if (ProcList[i].num == 0) + continue; + + sprintf(t," ; "mx_pfmt"",(mx_ptr)ProcList[i].func); + + n = 24 - strlen(t); + while(n > 0) + { + n = n - 8; + Strcat(t,"\t"); + } + Strcat(t,ProcList[i].name); + n = 32 - strlen(ProcList[i].name); + while(n > 0) + { + n = n - 8; + Strcat(t,"\t"); + } + + debug("%s%i\t\t%i\t\t%i\n",t,ProcList[i].num,ProcList[i].size,ProcList[i].mall_size); + } + debug(" ; ---\n"); + debug("; mem_lowptr\t\t"mx_pfmt"\n",(mx_ptr)mem_lowptr); + debug("; mem_hiptr\t\t"mx_pfmt"\n",(mx_ptr)mem_hiptr); + debug("; Memory Span\t\t%i bytes\n",(int)(mem_hiptr - mem_lowptr)); +} + +#ifdef BOTNET + +void debug_botinfo(BotInfo *binfo) +{ + for(;binfo;binfo=binfo->next) + { + debug(" ; BotInfo*\t\t"mx_pfmt"\n",(mx_ptr)binfo); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)binfo->next); + debug(" ; guid\t\t%i\n",binfo->guid); + debug(" ; hops\t\t%i\n",binfo->hops); + debug(" ; version\t\t\"%s\"\n",nullstr(binfo->version)); + debug(" ; server\t\t\"%s\"\n",nullstr(binfo->server)); + debug(" ; nuh\t\t\"%s\"\n",nullbuf(binfo->nuh)); + debug(" ; ---\n"); + } +} + +void debug_botnet(void) +{ + struct sockaddr_in sai; + BotNet *bn; + NetCfg *cfg; + int sz; + + debug("; linkpass\t\t\"%s\"\n",nullstr(linkpass)); + memtouch(linkpass); + debug("; linkport\t\t%i\n",linkport); + debug("; autolink\t\t%s\n",boolstr(autolink)); + debug("; global_tick\t\t%i\n",global_tick); + debug("> netcfglist\t\t"mx_pfmt"\n",(mx_ptr)netcfglist); + if (netcfglist) + { + for(cfg=netcfglist;cfg;cfg=cfg->next) + { + memtouch(cfg); + debug(" ; NetCfg*\t\t"mx_pfmt"\n",(mx_ptr)cfg); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)cfg->next); + debug(" ; guid\t\t%i\n",cfg->guid); + debug(" ; port\t\t%i\n",cfg->port); + debug(" ; linked\t\t%s\n",boolstr(cfg->linked)); + debug(" ; host\t\t\"%s\"\n",nullstr(cfg->host)); + debug(" ; pass\t\t\"%s\"\n",nullstr(cfg->pass)); + if (cfg->next) + debug(" ; ---\n"); + } + debug("; ---\n"); + } + debug("> botnetlist\t\t"mx_pfmt"\n",(mx_ptr)botnetlist); + if (botnetlist) + { + for(bn=botnetlist;bn;bn=bn->next) + { + memtouch(bn); + debug(" ; BotNet*\t\t"mx_pfmt"\n",(mx_ptr)bn); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)bn->next); + sz = sizeof(sai); + if (getpeername(bn->sock,(struct sockaddr*)&sai,&sz) < 0) + { + debug(" ; sock\t\t%i [ getpeername == -1 ]\n",bn->sock); + } + else + { + debug(" ; sock\t\t%i [ %s:%i ]\n",bn->sock, + inet_ntoa(sai.sin_addr),ntohs(sai.sin_port)); + } + debug(" ; status\t\t%s (%i)\n",strdef(BNdefs,bn->status),bn->status); + if (bn->status != BN_LINKSOCK) + { + debug(" ; has_data\t\t%i\n",bn->has_data); + debug(" ; guid\t\t%i\n",bn->guid); + debug(" ; lsid\t\t%i\n",bn->lsid); + debug(" ; rsid\t\t%i\n",bn->rsid); + debug(" ; opt.pta\t\t%s\n",boolstr(bn->opt.pta)); + debug(" ; controller\t\t"mx_pfmt" { guid = %i }\n",(mx_ptr)bn->controller,bn->controller->guid); + debug(" ; when\t\t%s (%lu)\n",atime(bn->when),bn->when); + debug(" > botinfo\t\t"mx_pfmt"\n",(mx_ptr)bn->botinfo); + debug_botinfo(bn->botinfo); + } + if (bn->next) + debug(" ; ---\n"); + } + } +} + +#endif /* BOTNET */ + +void debug_core(void) +{ + char tmpbuf[MAXLEN]; + Auth *auth; + Ban *ban; + Chan *chan; + ChanUser *CU; + Client *client; + IReq *ir; + Mech *bot; +#ifdef NOTIFY + nfLog *nfl; + Notify *nf; +#endif /* NOTIFY */ +#ifdef SEEN + Seen *seen; +#endif /* SEEN */ + Server *sp; + ServerGroup *sg; + Spy *spy; + Strp *st; + Shit *shit; +#ifdef TRIVIA + TrivScore *triv; +#endif /* TRIVIA */ + User *user; + int i; + + debug("; VERSION\t\t\"%s\"\n",VERSION); + debug("; SRCDATE\t\t\"%s\"\n",SRCDATE); + debug("; BOTLOGIN\t\t\"%s\"\n",BOTLOGIN); + debug("; BOTCLASS\t\t\"%s\"\n",BOTCLASS); + debug("> StructList\n"); + for(i=0;StructList[i].name;i++) + { + debug(" ; %s\t\t%i\n",StructList[i].name,StructList[i].size); + } + debug(" ; ---\n"); + if (current) + debug("; current\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)current,nullstr(current->nick)); + else + debug("; current\t\t"mx_pfmt"\n",(mx_ptr)current); + debug("; executable\t\t\"%s\"\n",executable); + debug("; configfile\t\t\"%s\"\n",configfile); + debug("; uptime\t\t%s (%lu)\n",atime(uptime),uptime); + debug("; short_tv\t\t%s (%is wait)\n",boolstr(short_tv),(short_tv) ? 1 : 30); + debug("> currentservergroup\t"mx_pfmt"\n",(mx_ptr)currentservergroup); + if (currentservergroup) + { + sg = currentservergroup; + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)sg->next); + debug(" ; servergroup\t\t%i\n",sg->servergroup); + debug(" ; name\t\t\"%s\"\n",nullbuf(sg->name)); + debug(" ; ---\n"); + } + debug("> servergrouplist\t"mx_pfmt"\n",(mx_ptr)servergrouplist); + for(sg=servergrouplist;sg;sg=sg->next) + { + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)sg->next); + debug(" ; servergroup\t\t%i\n",sg->servergroup); + debug(" ; name\t\t\"%s\"\n",nullbuf(sg->name)); + debug(" ; ---\n"); + } + debug("> serverlist\t\t"mx_pfmt"\n",(mx_ptr)serverlist); + for(sp=serverlist;sp;sp=sp->next) + { + memtouch(sp); + debug_server(sp," "); + } + if (botlist) debug_settings(botlist->setting,DSET_PROC); + debug("> botlist\t\t"mx_pfmt"\n",(mx_ptr)botlist); + for(bot=botlist;bot;bot=bot->next) + { + memtouch(bot); + memtouch(bot->nick); + memtouch(bot->wantnick); + debug(" ; Mech*\t\t"mx_pfmt"\n",(mx_ptr)bot); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)bot->next); + debug(" ; connect\t\t%s (%i)\n",strdef(CNdefs,bot->connect),bot->connect); + debug(" ; sock\t\t%i\n",bot->sock); + debug(" ; ip\t\t\t%s\n",inet_ntoa(bot->ip)); + debug(" > server\t\t%i\n",bot->server); + sp = find_server(bot->server); + if (sp) + { + debug_server(sp," "); + } + debug(" > nextserver\t\t%i\n",bot->nextserver); + sp = find_server(bot->nextserver); + if (sp) + { + debug_server(sp," "); + } + debug(" ; nick\t\t\"%s\"\n",nullstr(bot->nick)); + debug(" ; wantnick\t\t\"%s\"\n",nullstr(bot->wantnick)); + + debug_settings(bot->setting,DSET_GLOBAL); + + /* + * userlist + */ + debug(" > userlist\t\t"mx_pfmt"\n",(mx_ptr)bot->userlist); + for(user=bot->userlist;user;user=user->next) + { + memtouch(user); + debug(" ; User*\t\t"mx_pfmt"\n",(mx_ptr)user); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)user->next); + debug(" > mask\t\t"mx_pfmt"\n",(mx_ptr)user->mask); + for(st=user->mask;st;st=st->next) + { + memtouch(st); + debug(" ; Strp*\t\t"mx_pfmt" { "mx_pfmt", \"%s\" }\n", + (mx_ptr)st,(mx_ptr)st->next,st->p); + } + debug(" > chan\t\t"mx_pfmt"\n",(mx_ptr)user->chan); + for(st=user->chan;st;st=st->next) + { + memtouch(st); + debug(" ; Strp*\t\t"mx_pfmt" { "mx_pfmt", \"%s\" }\n", + (mx_ptr)st,(mx_ptr)st->next,st->p); + } + debug(" ; name\t\t\"%s\"\n",user->name); + debug(" ; pass\t\t\"%s\"\n",nullstr(user->pass)); + debug(" ; access\t\t%i\n",user->x.x.access); + debug(" ; prot\t\t%i\n",user->x.x.prot); +#ifdef GREET + debug(" ; greetfile\t\t%s\n",boolstr(user->greet)); + debug(" ; randline\t\t%s\n",boolstr(user->x.x.randline)); +#endif /* GREET */ +#ifdef BOUNCE + debug(" ; bounce\t\t%s\n",boolstr(user->x.x.bounce)); +#endif /* BOUNCE */ + debug(" ; echo\t\t%s\n",boolstr(user->x.x.echo)); + debug(" ; aop\t\t%s\n",boolstr(user->x.x.aop)); + debug(" ; avoice\t\t%s\n",boolstr(user->x.x.avoice)); +#ifdef GREET + memtouch(user->greet); + debug(" ; greet\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)user->greet,nullstr(user->greet)); +#endif /* GREET */ +#ifdef NOTE + debug(" > note\t\t"mx_pfmt"\n",(mx_ptr)user->note); + for(st=user->note;st;st=st->next) + { + memtouch(st); + debug(" ; Strp*\t\t"mx_pfmt" { "mx_pfmt", \"%s\" }\n", + (mx_ptr)st,(mx_ptr)st->next,st->p); + } +#endif /* NOTE */ +#ifdef BOTNET + debug(" ; modcount\t\t%i\n",user->modcount); + debug(" ; guid\t\t%i\n",user->guid); + debug(" ; tick\t\t%i\n",user->tick); + debug(" ; addsession\t\t%i\n",user->addsession); +#endif /* BOTNET */ + + debug(" ; ---\n"); + } + + debug(" > parselist\t\t"mx_pfmt"\n",(mx_ptr)bot->parselist); + for(ir=bot->parselist;ir;ir=ir->next) + { + memtouch(ir); + debug(" ; IReq*\t\t"mx_pfmt"\n",(mx_ptr)ir); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)ir->next); + debug(" ; ---\n"); + } + + /* + * spylist + */ + debug(" > spylist\t\t"mx_pfmt"\n",(mx_ptr)bot->spylist); + for(spy=bot->spylist;spy;spy=spy->next) + { + memtouch(spy); + debug(" ; Spy*\t\t"mx_pfmt"\n",(mx_ptr)spy); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)spy->next); + debug(" ; t_src\t\t%i\n",spy->t_src); + debug(" ; t_dest\t\t%i\n",spy->t_dest); + debug(" ; dcc\t\t"mx_pfmt"\n",(mx_ptr)spy->dcc); + debug(" ; src\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)spy->src,nullstr(spy->src)); + debug(" ; dest\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)spy->dest,nullstr(spy->dest)); + debug(" ; ---\n"); + } + debug(" > spy flags\n"); + debug(" ; any\t\t%s\n", boolstr(bot->spy & SPYF_ANY)); + debug(" ; channel\t\t%s\n",boolstr(bot->spy & SPYF_CHANNEL)); + debug(" ; status\t\t%s\n", boolstr(bot->spy & SPYF_STATUS)); + debug(" ; message\t\t%s\n",boolstr(bot->spy & SPYF_MESSAGE)); + debug(" ; rawirc\t\t%s\n", boolstr(bot->spy & SPYF_RAWIRC)); + debug(" ; botnet\t\t%s\n", boolstr(bot->spy & SPYF_BOTNET)); + debug(" ; ---\n"); + +#ifdef NOTIFY + debug(" > notifylist\t\t"mx_pfmt"\n",(mx_ptr)bot->notifylist); + for(nf=bot->notifylist;nf;nf=nf->next) + { + memtouch(nf); + debug(" ; Notify*\t\t"mx_pfmt"\n",(mx_ptr)nf); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)nf->next); + debug(" ; status\t\t%s (%i)\n",strdef(NFdefs,nf->status),nf->status); + debug(" ; checked\t\t%s (%lu)\n",atime(nf->checked),nf->checked); + + debug(" > log\t\t"mx_pfmt"\n",(mx_ptr)nf->log); + for(nfl=nf->log;nfl;nfl=nfl->next) + { + memtouch(nfl); + debug(" ; nfLog*\t\t"mx_pfmt"\n",(mx_ptr)nfl); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)nfl->next); + debug(" ; signon\t\t%s (%lu)\n",atime(nfl->signon),nfl->signon); + debug(" ; signoff\t\t%s (%lu)\n",atime(nfl->signoff),nfl->signoff); + debug(" ; realname\t\"%s\"\n",nullstr(nfl->realname)); + debug(" ; userhost\t\"%s\"\n",nullstr(nfl->userhost)); + debug(" ; ---\n"); + } + + debug(" ; info\t\t\"%s\"\n",nullstr(nf->info)); + debug(" ; endofmask\t\t"mx_pfmt"\n",(mx_ptr)nf->endofmask); + debug(" ; mask\t\t\"%s\"\n",nullstr(nf->mask)); + debug(" ; nick\t\t\"%s\"\n",nf->nick); + debug(" ; ---\n"); + } +#endif /* NOTIFY */ + + debug(" > authlist\t\t"mx_pfmt"\n",(mx_ptr)bot->authlist); + for(auth=bot->authlist;auth;auth=auth->next) + { + memtouch(auth); + debug(" ; Auth*\t\t"mx_pfmt"\n",(mx_ptr)auth); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)auth->next); + debug(" ; active\t\t%s (%lu)\n",atime(auth->active),auth->active); + debug(" ; user\t\t"mx_pfmt" { \"%s\", ... }\n",(mx_ptr)auth->user,auth->user->name); + debug(" ; nuh\t\t\"%s\"\n",nullstr(auth->nuh)); + debug(" ; ---\n"); + } + + debug(" > shitlist\t\t"mx_pfmt"\n",(mx_ptr)bot->shitlist); + for(shit=bot->shitlist;shit;shit=shit->next) + { + memtouch(shit); + debug(" ; Shit*\t\t"mx_pfmt"\n",(mx_ptr)shit); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)shit->next); + debug(" ; action\t\t%i\n",shit->action); + debug(" ; time\t\t%s (%lu)\n",atime(shit->time),shit->time); + debug(" ; expire\t\t%s (%lu)\n",atime(shit->expire),shit->expire); + debug(" ; chan\t\t\"%s\"\n",nullstr(shit->chan)); + debug(" ; from\t\t\"%s\"\n",nullstr(shit->from)); + debug(" ; reason\t\t\"%s\"\n",nullstr(shit->reason)); + debug(" ; mask\t\t\"%s\"\n",nullstr(shit->mask)); + } + + debug(" > chanlist\t\t"mx_pfmt"\n",(mx_ptr)bot->chanlist); + for(chan=bot->chanlist;chan;chan=chan->next) + { + memtouch(chan); + memtouch(chan->name); + memtouch(chan->key); + memtouch(chan->topic); + memtouch(chan->kickedby); + debug(" ; Chan*\t\t"mx_pfmt"\n",(mx_ptr)chan); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)chan->next); + debug(" ; name\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)chan->name,nullstr(chan->name)); + debug(" ; key\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)chan->key,nullstr(chan->key)); + debug(" ; topic\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)chan->topic,nullstr(chan->topic)); + debug(" ; kickedby\t\t"mx_pfmt" \"%s\"\n",(mx_ptr)chan->kickedby,nullstr(chan->kickedby)); + debug(" > Chan->banlist\n"); + for(ban=chan->banlist;ban;ban=ban->next) + { + memtouch(ban); +#ifdef IRCD_EXTENSIONS + { + char *s; + + if (ban->imode) + s = "+I invite exception"; + else + if (ban->emode) + s = "+e ban exception"; + else + s = "+b banmask"; + debug(" ; Ban*\t\t"mx_pfmt" { "mx_pfmt", \"%s\", %s, \"%s\", %s (%lu) }\n", + (mx_ptr)ban,(mx_ptr)ban->next,nullstr(ban->banstring),s, + nullstr(ban->bannedby),atime(ban->time),ban->time); + } +#else /* IRCD_EXTENSIONS */ + debug(" ; Ban*\t\t"mx_pfmt" { "mx_pfmt", \"%s\", \"%s\", %s (%lu) }\n", + (mx_ptr)ban,(mx_ptr)ban->next,nullstr(ban->banstring), + nullstr(ban->bannedby),atime(ban->time),ban->time); +#endif /* IRCD_EXTENSIONS */ + + } + debug(" ; ---\n"); + debug(" > Chan->users\n"); + for(CU=chan->users;CU;CU=CU->next) + { + memtouch(CU); + memtouch(CU->nick); + strflags(tmpbuf,chanuser_flags,CU->flags); + debug(" ; ChanUser*\t"mx_pfmt" { "mx_pfmt", \"%s\", \"%s\", [%s], ... }\n", + (mx_ptr)CU,(mx_ptr)CU->next,nullstr(CU->nick),nullstr(CU->userhost),tmpbuf); + } + debug(" ; ---\n"); + if (chan->cacheuser) + { + debug(" ; cacheuser\t\t"mx_pfmt" { "mx_pfmt", \"%s\", \"%s\", ... }\n", + (mx_ptr)chan->cacheuser,(mx_ptr)chan->cacheuser->next, + nullstr(chan->cacheuser->nick),nullstr(chan->cacheuser->userhost)); + } + else + { + debug(" ; cacheuser\t\t"mx_pfmt"\n",(mx_ptr)chan->cacheuser); + } + + debug_settings(chan->setting,DSET_CHAN); + + debug(" ; limit\t\t%i\n",chan->limit); + + debug(" ; private\t\t%s\n",boolstr(chan->private)); + debug(" ; secret\t\t%s\n",boolstr(chan->secret)); + debug(" ; moderated\t\t%s\n",boolstr(chan->moderated)); + debug(" ; topprot\t\t%s\n",boolstr(chan->topprot)); + debug(" ; limitmode\t\t%s\n",boolstr(chan->limitmode)); + debug(" ; invite\t\t%s\n",boolstr(chan->invite)); + debug(" ; nomsg\t\t%s\n",boolstr(chan->nomsg)); + debug(" ; keymode\t\t%s\n",boolstr(chan->keymode)); + debug(" ; hiddenkey\t\t%s\n",boolstr(chan->hiddenkey)); + debug(" ; sync\t\t%s\n",boolstr(chan->sync)); + debug(" ; wholist\t\t%s\n",boolstr(chan->wholist)); + debug(" ; bot_is_op\t\t%s\n",boolstr(chan->bot_is_op)); + debug(" ; active\t\t%s\n",boolstr(chan->active)); + debug(" ; rejoin\t\t%s\n",boolstr(chan->rejoin)); + + debug(" ; this10\t\t%i\n",chan->this10); + debug(" ; last10\t\t%i\n",chan->last10); + debug(" ; this60\t\t%i\n",chan->this60); + debug(" ; last60\t\t%i\n",chan->last60); + +#ifdef DYNAMODE + debug(" ; lastlimit\t\t%s (%lu)\n",atime(chan->lastlimit),chan->lastlimit); +#endif /* DYNAMODE */ +#ifdef STATS + if (chan->stats) + { + debug(" > ChanStats\t\t"mx_pfmt"\n",(mx_ptr)chan->stats); + debug(" ; userseconds\t%i\n",chan->stats->userseconds); + debug(" ; LHuserseconds\t%i\n",chan->stats->LHuserseconds); + debug(" ; users\t\t%i\n",chan->stats->users); + debug(" ; lastuser\t%s (%lu)\n",atime(chan->stats->lastuser),chan->stats->lastuser); + if (chan->stats->flags & CSTAT_PARTIAL) + debug(" ; flags\t\tCSTAT_PARTIAL (%i)\n",chan->stats->flags); + else + debug(" ; flags\t\tNO_FLAGS (%i)\n",chan->stats->flags); + debug(" ; userpeak\t%i\n",chan->stats->userpeak); + debug(" ; userlow\t\t%i\n",chan->stats->userlow); + debug(" ; kicks\t\t%i\n",chan->stats->kicks); + debug(" ; joins\t\t%i\n",chan->stats->joins); + debug(" ; parts\t\t%i\n",chan->stats->parts); + debug(" ; quits\t\t%i\n",chan->stats->quits); + debug(" ; privmsg\t\t%i\n",chan->stats->privmsg); + debug(" ; notice\t\t%i\n",chan->stats->notice); + } +#endif /* STATS */ + debug(" ; ---\n"); + } + debug(" ; activechan\t\t"mx_pfmt"\n",(mx_ptr)bot->activechan); + debug(" > clientlist\t\t"mx_pfmt"\n",(mx_ptr)bot->clientlist); + for(client=bot->clientlist;client;client=client->next) + { + memtouch(client); + debug(" ; Client*\t\t"mx_pfmt"\n",(mx_ptr)client); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)client->next); + if (client->user) + debug(" ; user\t\t"mx_pfmt" { \"%s\", ... }\n",(mx_ptr)client->user,client->user->name); + else + debug(" ; user\t\tNULL\n"); + debug(" ; sock\t\t%i\n",client->sock); +#ifdef DCC_FILE + debug(" ; fileno\t\t%i\n",client->fileno); + debug(" ; fileend\t\t%i\n",client->fileend); +#endif /* DCC_FILE */ + strflags(tmpbuf,dcc_flags,client->flags); + debug(" ; flags\t\t%i [%s]\n",client->flags,tmpbuf); + debug(" ; inputcount\t%i\n",client->inputcount); + debug(" ; lasttime\t\t%s (%lu)\n",atime(client->lasttime),client->lasttime); + debug(" ; ---\n"); + } + debug(" ; lastreset\t\t%s (%lu)\n",atime(bot->lastreset),bot->lastreset); + debug(" ; lastantiidle\t%s (%lu)\n",atime(bot->lastantiidle),bot->lastantiidle); + debug(" ; lastrejoin\t\t%s (%lu)\n",atime(bot->lastrejoin),bot->lastrejoin); + debug(" ; conntry\t\t%s (%lu)\n",atime(bot->conntry),bot->conntry); + debug(" ; activity\t\t%s (%lu)\n",atime(bot->activity),bot->activity); + debug(" ; ontime\t\t%s (%lu)\n",atime(bot->ontime),bot->ontime); +#ifdef IRCD_EXTENSIONS + strflags(tmpbuf,ircx_flags,bot->ircx_flags); + debug(" ; ircx_flags\t\t%i [%s]\n",bot->ircx_flags,tmpbuf); +#endif /* IRCD_EXTENSIONS */ + debug(" ; ---\n"); + } +#ifdef SEEN + debug("> seenlist\t\t"mx_pfmt"\n",(mx_ptr)seenlist); + if (seenlist) + { + for(seen=seenlist;seen;seen=seen->next) + { + memtouch(seen); + debug(" ; Seen*\t\t"mx_pfmt" { "mx_pfmt", \"%s\", \"%s\", \"%s\", \"%s\", %s (%lu), %s (%i) }\n", + (mx_ptr)seen, + (mx_ptr)seen->next, + nullstr(seen->nick), + nullstr(seen->userhost), + nullstr(seen->pa), + nullstr(seen->pb), + atime(seen->when),seen->when, + strdef(SEdefs,seen->t), + seen->t + ); + } + debug(" ; ---\n"); + } +#endif /* SEEN */ +#ifdef TRIVIA + debug("> scorelist\t\t"mx_pfmt"\n",(mx_ptr)scorelist); + for(triv=scorelist;triv;triv=triv->next) + { + memtouch(triv); + debug(" ; TrivScore*\t\t"mx_pfmt" { "mx_pfmt", %i, %i, %i, %i, %i, %i, \"%s\" }\n", + (mx_ptr)triv, + (mx_ptr)triv->next, + triv->score_wk, + triv->score_last_wk, + triv->week_nr, + triv->score_mo, + triv->score_last_mo, + triv->month_nr, + triv->nick); + } + if (scorelist) + debug(" ; ---\n"); +#endif /* TRIVIA */ +} + +#ifdef RAWDNS + +void debug_rawdns(void) +{ + dnsAuthority *da; + dnsList *dns; + + for(da=dnsroot;da;da=da->next) + { + memtouch(da); + } + + debug("> dnslist\t\t"mx_pfmt"\n",(mx_ptr)dnslist); + for(dns=dnslist;dns;dns=dns->next) + { + memtouch(dns); + debug(" ; dnsList*\t\t"mx_pfmt"\n",(mx_ptr)dns); + debug(" ; next\t\t"mx_pfmt"\n",(mx_ptr)dns->next); + debug(" ; when\t\t%s (%lu)\n",atime(dns->when),dns->when); + if (dns->ip.s_addr) + { + debug(" ; ip\t\t\t%s\n",inet_ntoa(dns->ip)); + } + else + debug(" ; ip\t\t\t0\n"); + debug(" ; id\t\t\t%i\n",dns->id); + debug(" ; cname\t\t\"%s\"\n",nullstr(dns->cname)); + debug(" ; host\t\t\"%s\"\n",dns->host); + if (dns->next) + debug(" ; ---\n"); + } +} + +#endif /* RAWDNS */ + +#if defined(TCL) || defined(PYTHON) + +#if 0 +typedef struct +{ + time_t last; + time_t next; + ulong second1; //:30; + ulong second2; //:30; + ulong minute1; //:30; + ulong minute2; //:30; + ulong hour; //:24; + ulong weekday; //:7; + +} HookTimer; +#endif /* 0 */ + +char binstr[33]; + +char *ulong2bin(int limit, ulong x) +{ + char *dst = binstr; + int n; + + if (limit > 30) + limit -= 30; + x <<= (32 - limit); + + for(n=0;n hooklist\n"); + for(h=hooklist;h;h=h->next) + { + memtouch(h); + debug(" ; func\t\t"mx_pfmt" %s\n",(mx_ptr)h->func,strdef(SCRIPTdefs,(int)h->func)); + debug(" ; guid\t\t%i\n",h->guid); + debug(" ; flags\t\t%s (%i)\n",strdef(SCRIPTdefs,h->flags),h->flags); + if (h->flags == HOOK_TIMER) + { + debug(" ; timer\t\t"mx_pfmt"\n",(mx_ptr)h->type.timer); + debug(" ; timer.second1\t%s ( 0..29)\n",ulong2bin(30,h->type.timer->second1)); + debug(" ; timer.second2\t%s (30..59)\n",ulong2bin(30,h->type.timer->second2)); + debug(" ; timer.minute1\t%s ( 0..29)\n",ulong2bin(30,h->type.timer->minute1)); + debug(" ; timer.minute2\t%s (30..59)\n",ulong2bin(30,h->type.timer->minute2)); + debug(" ; timer.hour\t\t%s (0..23)\n",ulong2bin(24,h->type.timer->hour)); + debug(" ; timer.weekday\t%s (0..6)\n",ulong2bin(7,h->type.timer->weekday)); + } + debug(" ; self\t\t\"%s\"\n",nullstr(h->self)); + if (h->next) + debug(" ; ---\n"); + } +} + +#endif /* SCRIPTING */ + +void run_debug(void) +{ + memreset(); + + mem_lowptr = ((void*)-1); + mem_hiptr = NULL; + +#ifdef SEEN + memtouch(seenfile); +#endif /* SEEN */ + +#ifdef TRIVIA + memtouch(triv_qfile); +#endif /* TRIVIA */ + +#ifdef UPTIME + memtouch(uptimehost); + memtouch(uptimenick); +#endif /* UPTIME */ + + debug_core(); +#ifdef BOTNET + debug_botnet(); +#endif /* BOTNET */ +#ifdef RAWDNS + debug_rawdns(); +#endif /* RAWDNS */ +#ifdef SCRIPTING + debug_scripthook(); +#endif /* SCRIPTING */ + + debug_memory(); +} + +int wrap_debug(void) +{ + char fname[20]; + int fd,backup_fd,backup_dodebug; + + debug("(wrap_debug) init...\n"); + + backup_dodebug = dodebug; + backup_fd = debug_fd; + + sprintf(fname,"debug.%lu",now); + if ((fd = open(fname,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0) + return(0); + debug_fd = fd; + dodebug = TRUE; + + run_debug(); + + close(fd); + debug_fd = backup_fd; + dodebug = backup_dodebug; + + debug("(wrap_debug) all done.\n"); + return(1); +} + +void do_debug(COMMAND_ARGS) +{ + if (wrap_debug()) + to_user(from,"Debug information has been written to file"); + else + to_user(from,"Unable to write debug information to file"); +} + +void debug(char *format, ...) +{ + va_list msg; + + if (!dodebug) + return; + + if (debug_fd == -1) + { + if (debugfile) + { + if ((debug_fd = open(debugfile,O_CREAT|O_TRUNC|O_WRONLY,SECUREFILEMODE)) < 0) + { + dodebug = FALSE; + return; + } + } + else + { + debug_fd = 0; + } + CoreClient.sock = debug_fd; + } + + va_start(msg,format); + vsprintf(debugbuf,format,msg); + va_end(msg); + + if ((write(debug_fd,debugbuf,strlen(debugbuf))) < 0) + dodebug = FALSE; +} + +#endif /* DEBUG */ diff --git a/src/defines.h b/src/defines.h new file mode 100644 index 0000000..61bcca0 --- /dev/null +++ b/src/defines.h @@ -0,0 +1,468 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef DEFINES_H +#define DEFINES_H 1 + +/* + * dont export too many symbols... + */ +#ifdef LIBRARY +#define LS static +#else /* not LIBRARY */ +#define LS /* nothing */ +#endif /* LIBRARY */ + +/* + * Channel Crap + */ + +#define CHAN_ANY 0 +#define CHAN_ACTIVE 1 + +#define ANY_CHANNEL NULL +#define GLOBAL_CHANNEL MATCH_ALL + +#define COLLECT_TYPE 1 +#define REJOIN_TYPE 2 +#define CYCLE_TYPE 4 + +#define ADD_MODE 1 +#define SUB_MODE 2 +#define MODE_FORCE 3 + +#define MODE_PRIVATE 0x0004 +#define MODE_SECRET 0x0008 +#define MODE_MODERATED 0x0010 +#define MODE_TOPICLIMIT 0x0020 +#define MODE_INVITEONLY 0x0040 +#define MODE_NOPRIVMSGS 0x0080 +#define MODE_KEY 0x0100 +#define MODE_BAN 0x0200 +#define MODE_LIMIT 0x0400 +#define MODE_FLAGS 0x07ff + +#define CU_VOICE 0x0001 +#define CU_CHANOP 0x0002 +#define CU_VOICETMP 0x0004 +#define CU_NEEDOP 0x0008 +#define CU_MODES 0x00ff + +#define CU_DEOPPED 0x0100 +#define CU_KICKED 0x0200 +#define CU_BANNED 0x0400 +#define CU_MASSTMP 0x0800 +#define CU_KSWARN 0x1000 +#define CU_CHANBAN 0x2000 + +#define SHIT_NOOP 0 // do nothing +#define SHIT_CHANOP 1 // not allowed to be chanop +#define SHIT_KB 2 // simple kick and ban +#define SHIT_PERMABAN 3 // permanent ban, re-ban if unbanned +#define SHIT_CHANBAN 4 + +#define MAXSHITLEVEL 4 +#define MAXSHITLEVELSTRING "4" +#define MAXSHITLEVELCHAR '4' + +/* + * channel modequeues + */ +#define QM_RAWMODE 0 +#define QM_CHANUSER 1 + +#define QM_PRI_LOW 100 + +/* + * Bitfield for short_tv being set to 1 or 30 seconds + */ + +#define TV_TELNET_NICK 0x0002 +#define TV_UCACHE 0x0004 +#define TV_SERVCONNECT 0x0008 +#define TV_LINEBUF 0x0010 +#define TV_BOTNET 0x0020 +#define TV_REJOIN 0x0040 +#define TV_TRIVIA 0x0080 + +/* Misc Crap: */ + +#define EXTRA_CHAR(q) q==1?"":"s" +#define SPLIT 1 + +/* Parse Stuff */ + +#define PA_END 1 /* b00000001 */ /* mask for `end' bit */ +#define PA_WHOIS 2 /* b00000010 */ +#define PA_LUSERS 4 /* b00000100 */ +#define PA_STATS 8 /* b00001000 */ +#define PA_USERHOST 16 /* b00010000 */ +#define PA_DNS 32 /* b00100000 */ /* for resolving hosts */ +#define PA_TYPE 62 /* b00111110 */ /* mask for type bits */ + +/* DCC Crap: */ + +#define DCC_SEND 0x0001 +#define DCC_RECV 0x0002 +#define DCC_WAIT 0x0010 +#define DCC_ASYNC 0x0020 +#define DCC_ACTIVE 0x0040 +#define DCC_TELNET 0x0080 +#define DCC_TELNETPASS 0x0100 +#define DCC_DELETE 0x0200 + +/* + * format_uh() + */ + +#define FUH_USERHOST 1 +#define FUH_HOST 2 + +/* Type of Variable: */ + +#define INT_VAR 0x01 +#define STR_VAR 0x02 +#define TOG_VAR 0x04 +#define GLOBAL_VAR 0x08 +#define PROC_VAR 0x10 +#define CHR_VAR (0x20 | INT_VAR) +#define ANY_VAR 0xff + +#define INT_GLOBAL INT_VAR|GLOBAL_VAR +#define TOG_GLOBAL TOG_VAR|GLOBAL_VAR +#define TOG_PROC TOG_VAR|PROC_VAR|GLOBAL_VAR +#define INT_PROC INT_VAR|PROC_VAR|GLOBAL_VAR +#define STR_PROC STR_VAR|PROC_VAR|GLOBAL_VAR +#define CHR_PROC CHR_VAR|PROC_VAR|GLOBAL_VAR +#define STR_GLOBAL STR_VAR|GLOBAL_VAR +#define CHR_GLOBAL CHR_VAR|GLOBAL_VAR + +#define IsInt(x) (VarName[x].type & INT_VAR) +#define IsStr(x) (VarName[x].type & STR_VAR) +#define IsTog(x) (VarName[x].type & TOG_VAR) +#define IsNum(x) (VarName[x].type & (INT_VAR|TOG_VAR)) +#define IsChar(x) ((VarName[x].type & CHR_VAR) == CHR_VAR) +#define IsProc(x) (VarName[x].type & PROC_VAR) + +/* + * see settings.h for the actual setting struct + */ +enum { + /* + * channel settings + */ + TOG_ABK, + TOG_AOP, + INT_AUB, + INT_AVOICE, +#ifdef CHANBAN + TOG_CHANBAN, +#endif /* CHANBAN */ + INT_CKL, + TOG_CTL, +#ifdef DYNAMODE + STR_DYNLIMIT, +#endif /* DYNAMODE */ + TOG_ENFM, + STR_ENFMODES, + INT_FL, + INT_FPL, + INT_IKT, + TOG_KS, + INT_MAL, + INT_MBL, + INT_MDL, + INT_MKL, + INT_MPL, + INT_NCL, + INT_PROT, + TOG_PUB, + TOG_RK, + TOG_SD, + TOG_SHIT, + TOG_SO, +#ifdef STATS + STR_STATS, +#endif /* STATS */ + TOG_TOP, + /* + * global settings + * Note: first global setting (now: INT_AAWAY) is used below ... + */ + INT_AAWAY, + STR_ALTNICK, +#ifdef BOTNET + TOG_AUTOLINK, +#endif /* BOTNET */ +#ifdef BOUNCE + INT_BNCPORT, +#endif /* BOUNCE */ + TOG_CC, + CHR_CMDCHAR, +#ifdef CTCP + TOG_CTCP, +#endif /* CTCP */ + INT_CTIMEOUT, +#ifdef DCC_FILE + INT_DCCANON, + STR_DCCFILES, + INT_DCCUSER, +#endif /* DCC_FILE */ + TOG_ENFPASS, + STR_IDENT, + STR_IRCNAME, +#ifdef NOTIFY + INT_ISONDELAY, +#endif /* NOTIFY */ +#ifdef BOTNET + STR_LINKPASS, + INT_LINKPORT, +#endif /* BOTNET */ + INT_MODES, +#ifdef BOTNET + TOG_NETUSERS, +#endif /* BOTNET */ + TOG_NOIDLE, +#ifdef NOTIFY + STR_NOTIFYFILE, +#endif /* NOTIFY */ + TOG_ONOTICE, +#ifdef TRIVIA + CHR_QCHAR, + INT_QDELAY, + STR_QFILE, +#endif /* TRIVIA */ +#ifdef CTCP + TOG_RF, + TOG_RV, +#endif /* CTCP */ +#ifdef SEEN + STR_SEENFILE, +#endif /* SEEN */ + STR_SERVERGROUP, + TOG_SPY, + STR_UMODES, +#ifdef UPTIME + STR_UPHOST, + STR_UPNICK, + INT_UPPORT, +#endif /* UPTIME */ + STR_USERFILE, + STR_VIRTUAL, +#ifdef WEB + INT_WEBPORT, +#endif /* WEB */ +#ifdef WINGATE + STR_WINGATE, + INT_WINGPORT, +#endif /* WINGATE */ + __NULL_VAR__, + SIZE_VARS +}; + +/* + * why would channel structs contain global vars? + * they shouldnt! and now they dont! :) + */ +#define CHANSET_SIZE INT_AAWAY + +/* + * For botlinks + */ + +#ifdef BOTNET + +/* BotNet->status */ + +#define BN_UNKNOWN 0 +#define BN_DEAD 1 +#define BN_LINKSOCK 2 +#define BN_CONNECT 3 +#define BN_BANNERSENT 4 +#define BN_WAITAUTH 5 +#define BN_WAITLINK 6 +#define BN_LINKED 7 + +#define BNAUTH_PLAINTEXT 0 +#define BNAUTH_DES 1 +#define BNAUTH_MD5 2 + +#endif /* BOTNET */ + +/* for connect status */ + +#define CN_NOSOCK 0 +#define CN_DNSLOOKUP 1 +#define CN_TRYING 2 +#define CN_CONNECTED 3 +#define CN_ONLINE 4 +#define CN_DISCONNECT 5 +#define CN_BOTDIE 6 +#define CN_NEXTSERV 7 +#define CN_WINGATEWAIT 8 +#define CN_SPINNING 9 /* after exhausting serverlist */ + +/* DCC Kill flags (BYE command) */ + +#define DCC_NULL 0 +#define DCC_COMMAND 1 +#define DCC_KILL 2 + +/* VHOST types */ + +#define VH_ZERO 0 +#define VH_IPALIAS (1 << 1) +#define VH_IPALIAS_FAIL (1 << 2) +#define VH_IPALIAS_BOTH (VH_IPALIAS|VH_IPALIAS_FAIL) +#define VH_WINGATE (1 << 3) +#define VH_WINGATE_FAIL (1 << 4) +#define VH_WINGATE_BOTH (VH_WINGATE|VH_WINGATE_FAIL) + +/* server error types */ + +#define SP_NULL 0 +#define SP_NOAUTH 1 +#define SP_KLINED 2 +#define SP_FULLCLASS 3 +#define SP_TIMEOUT 4 +#define SP_ERRCONN 5 +#define SP_DIFFPORT 6 +#define SP_NO_DNS 7 +#define SP_THROTTLED 8 + +/* find_channel() */ + +#define CH_ACTIVE 0x1 +#define CH_OLD 0x2 +#define CH_ANY 0x3 + +/* check_mass() */ + +#define CHK_CAPS 0 +#define CHK_PUB 1 +#define CHK_PUBLIC CHK_PUB +#define CHK_DEOP 2 +#define CHK_BAN 3 +#define CHK_KICK 4 + +/* + * seen selector types + */ +#define SEEN_PARTED 0 +#define SEEN_QUIT 1 +#define SEEN_NEWNICK 2 +#define SEEN_KICKED 3 + +/* + * spying types, source and target types are mixed + */ +#define SPY_FILE 1 +#define SPY_DCC 2 +#define SPY_CHANNEL 3 +#define SPY_STATUS 4 +#define SPY_MESSAGE 5 +#define SPY_RAWIRC 6 +#define SPY_BOTNET 7 + +#define SPYF_ANY 1 +#define SPYF_CHANNEL (1 << SPY_CHANNEL) +#define SPYF_STATUS (1 << SPY_STATUS) +#define SPYF_MESSAGE (1 << SPY_MESSAGE) +#define SPYF_RAWIRC (1 << SPY_RAWIRC) +#define SPYF_BOTNET (1 << SPY_BOTNET) + +/* + * notify defines + */ +#define NF_OFFLINE 0 +#define NF_WHOIS 1 +#define NF_MASKONLINE 2 /* anything above NF_MASKONLINE is "online" */ +#define NF_NOMATCH 3 + +/* + * uptime defines + */ + +#define UPTIME_ENERGYMECH 1 /* http://www.energymech.net */ +#define UPTIME_EGGDROP 2 /* http://www.eggheads.org */ +#define UPTIME_MINIMECH 3 /* http://www.energymech.net */ +#define UPTIME_WINMECH 4 /* http://www.energymech.net */ +#define UPTIME_RACBOT 5 /* http://www.racbot.org */ +#define UPTIME_MIRC 6 /* http://www.mirc.com */ +#define UPTIME_HAL9000 7 /* http://www.2010.org */ +#define UPTIME_ANABOT 8 /* http://www.sirklabs.hu/ana-liza/ */ +#define UPTIME_ANGELBOT 9 /* unknown */ +#define UPTIME_FIRECLAW 10 /* http://www.fireclaw.org */ +#define UPTIME_GARNAX 11 /* http://garnax.mircx.com */ +#define UPTIME_WINEGGDROP 12 /* http://www.eggheads.org */ +#define UPTIME_SUPYBOT 14 /* http://supybot.sourceforge.net */ + +#define UPTIME_GENERICDEATH 5000 /* generic death */ +#define UPTIME_SIGSEGV 5001 +#define UPTIME_SIGBUS 5002 +#define UPTIME_SIGTERM 5003 +#define UPTIME_SIGINT 5004 + +#define UPTIMEHOST "uptime.energymech.net" + +#ifdef __CYGWIN__ +#define UPTIME_BOTTYPE UPTIME_WINMECH +#else +#define UPTIME_BOTTYPE UPTIME_ENERGYMECH +#endif /* __CYGWIN__ */ + +/* + * scripting hooks + */ +#define HOOK_PARSE 0 +#define HOOK_TIMER 1 +#define HOOK_COMMAND 2 +#define HOOK_BOTNET 3 +#define HOOK_DCC_COMPLETE 4 +#define HOOK_DNS 5 + +/* + * + */ +#define IRCX_WALLCHOPS 1 +#define IRCX_WALLVOICES 2 +#define IRCX_IMODE 4 +#define IRCX_EMODE 8 + +/* + * stats.c + */ +#define CSTAT_PARTIAL 1 + +/* + * dns.c + */ +#define MAX_NAMESERVERS 4 + +/* + * redirect.c + */ +#define R_NOTICE 0 +#define R_PRIVMSG 1 +#define R_FILE 2 +#define R_BOTNET 3 + +#endif /* DEFINES_H */ + diff --git a/src/dns.c b/src/dns.c new file mode 100644 index 0000000..2a78156 --- /dev/null +++ b/src/dns.c @@ -0,0 +1,939 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define DNS_C +#include "config.h" + +#ifdef RAWDNS + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#define unpack_ushort(x) (((x)[0] << 8) | ((x)[1])) +#define unpack_ulong(x) (((x)[0] << 24) | ((x)[1] << 16) | ((x)[2] << 8) | ((x)[3])) + +typedef struct dnsType +{ + ushort type; + ushort class; + +} dnsType; + +typedef struct dnsRType +{ + ushort type; /* &0 */ + ushort class; /* &2 */ + ulong ttl; /* &4 */ + ushort rdlength; /* &8 */ + +} dnsRType; + +#define DNS_QUERY 1 +#define DNS_TYPE_A 1 +#define DNS_CLASS_IN 1 +#define DNS_TYPE_NS 2 +#define DNS_TYPE_CNAME 5 + +#define QUERY_FLAGS 128 + +#define MAX_QUESTIONS 16 + +LS int dnssock = -1; +LS int dnsserver = 0; + +#ifdef DEBUG +char *type_textlist[] = +{ +NULL, +"A", +"NS", +"MD", +"MF", +"CNAME", +"SOA", +"MB", +"MG", +"MR", +"NULL", +"WKS", +"PTR", +"HINFO", +"MINFO", +"MX", +"TXT", +}; +#endif /* DEBUG */ + +void init_rawdns(void) +{ + struct sockaddr_in sai; + + if ((dnssock = socket(AF_INET,SOCK_DGRAM,0)) < 0) + return; + + memset(&sai,0,sizeof(sai)); + sai.sin_addr.s_addr = INADDR_ANY; + sai.sin_family = AF_INET; + + if (bind(dnssock,(struct sockaddr*)&sai,sizeof(sai)) < 0) + { + close(dnssock); + dnssock = -1; + return; + } + SockFlags(dnssock); +#ifdef DEBUG + debug("(init_rawdns) {%i} dnssock is active\n",dnssock); +#endif /* DEBUG */ +} + +struct in_addr dnsroot_lookup(const char *hostname) +{ + dnsAuthority *da; + struct in_addr ip; + + for(da=dnsroot;da;da=da->next) + { + if (!Strcasecmp(hostname,da->hostname)) + { +#ifdef DEBUG + debug("(dnsroot_lookup) %s = %s\n",hostname,inet_ntoa(da->ip)); +#endif /* DEBUG */ + return(da->ip); + } + } + ip.s_addr = -1; + return(ip); +} + +const char *get_dns_token(const char *src, const char *packet, char *dst, int sz) +{ + const char *endsrc = NULL; + ushort offptr; + int tsz; + int dot = 0; + + for(;;dot=1) + { + tsz = (uchar)*(src++); + if ((tsz & 0xC0) == 0xC0) + { + offptr = (tsz & 0x3f) << 8; + offptr |= *src; + if ((packet + offptr) > (packet + sz)) + return(src+1); + if (!endsrc) + endsrc = src + 1; + src = packet + offptr; + tsz = *(src++); + } + if (tsz == 0) + break; + if (dot) + *(dst++) = '.'; + while(tsz) + { + tsz--; + *(dst++) = *(src++); + } + } + *dst = 0; + return((endsrc) ? endsrc : src); +} + +int make_query(char *packet, const char *hostname) +{ + char *size,*dst; + + /* + * make a packet + */ + packet[0] = rand() >> 24; + packet[1] = rand() >> 24; + packet[2] = 1; // RD, recursion desired flag + packet[3] = 0; + ((ulong*)packet)[1] = htonl(0x10000); + ((ulong*)packet)[2] = 0; + size = packet + 12; + dst = size + 1; + while(*hostname) + { + if ((*dst = *hostname) == '.') + { + *size = (dst - size - 1); + size = dst; + } + hostname++; + dst++; + } + *size = (dst - size - 1); + + dst[0] = 0; + dst[1] = 0; + dst[2] = 1; + dst[3] = 0; + dst[4] = 1; + return(dst - packet + 5); +} + +#ifndef UNALIGNED_MEM + +struct in_addr temp_ip_data; + +struct in_addr *get_stored_ip(const char *ipdata) +{ + memcpy((char *)&temp_ip_data,ipdata,4); + return(&temp_ip_data); +} + +#else + +#define get_stored_ip(x) ((struct in_addr *)x) + +#endif /* UNALIGNED_MEM */ + +/* + * + */ +#ifdef SCRIPTING + +void dns_hook(char *host, char * resolved) +{ + Hook *hook; + Mech *backbot; + + backbot = current; + for(hook=hooklist;hook;hook=hook->next) + { + if (hook->flags == HOOK_DNS && !Strcasecmp(host,hook->type.host)) + { + for(current=botlist;current;current=current->next) + { + if (hook->guid == 0 || hook->guid == current->guid) + { + hook->func(hook->type.host,resolved,hook); + } + } + } + } + current = backbot; +} + +#endif /* SCRIPTING */ + +void parse_query(int psz, dnsQuery *query) +{ + struct sockaddr_in sai; + char packet[512]; + Mech *backupbot; + struct in_addr *ip; + dnsList *dns; + const char *src,*rtyp; + char token[64],token2[64]; + int sz,n; + + src = (const char*)query; + +#ifdef DEBUG + for(n=0;n<16;n++) + token[n] = (ntohs(query->flags) & (1<flags); + debug("(parse_query) %i: flags = %i { %s %i %s%s%s%s%s }\n", + sz,n,token, + (n&15), // result code + (n&32768) ? "QR 1 (Answer) ":"QR 0 (Question) ", + (n&1024) ? "AA ":"", + (n&512) ? "TC ":"", + (n&256)? "RD ":"", + (n&128)? "RA ":""); +#endif /* DEBUG */ + + src += 12; + + for(dns=dnslist;dns;dns=dns->next) + { + if (dns->id == ntohs(query->qid)) + break; + } + if (!dns) + return; + + + n = ntohs(query->questions); + while(n--) + { + /* skip QNAME */ + src = get_dns_token(src,(const char *)query,token,psz); + /* skip (ushort)QTYPE and (ushort)QCLASS */ + src += 4; + } + + n = ntohs(query->answers); + while(n) + { + src = get_dns_token(src,(const char*)query,token,psz); + rtyp = src; + src += 10; + +#ifdef DEBUG + debug("(parse_query) %i: answer = %s\n",dns->id,token); +#endif /* DEBUG */ + + if ((unpack_ushort(&rtyp[0]) == DNS_TYPE_CNAME) && + (unpack_ushort(&rtyp[2]) == DNS_CLASS_IN)) + { + get_dns_token(src,(const char *)query,token2,psz); +#ifdef DEBUG + debug("(parse_query) %i: cname: %s = %s\n",dns->id,token,token2); +#endif /* DEBUG */ + if (dns->cname) + Free((char**)&dns->cname); + dns->when = now + 30; + set_mallocdoer(parse_query); + dns->cname = Strdup(token2); + } + + if ((unpack_ushort(&rtyp[0]) == DNS_TYPE_A) && + (unpack_ushort(&rtyp[2]) == DNS_CLASS_IN) && + (unpack_ushort(&rtyp[8]) == 4)) + { + ip = get_stored_ip(src); + if (dns->auth && !Strcasecmp(dns->auth->hostname,token)) + { + dns->auth->ip.s_addr = ip->s_addr; + dns->when = now + 60; +#ifdef DEBUG + debug("(parse_query) a auth: %s = %s\n",token,inet_ntoa(*ip)); +#endif /* DEBUG */ + } + else + if (!Strcasecmp(dns->host,token) || (dns->cname && !Strcasecmp(dns->cname,token))) + { + dns->ip.s_addr = ip->s_addr; + dns->when = now + 3600; +#ifdef DEBUG + debug("(parse_query) a: %s = %s\n",token,inet_ntoa(*ip)); +#endif /* DEBUG */ + /* a final dns anwer was received */ + backupbot = current; + for(current=botlist;current;current=current->next) + { + send_pa(PA_DNS|PA_END,token,"Resolved: %s (%s)",token,inet_ntoa(*ip)); + } +#ifdef SCRIPTING + dns_hook(token,inet_ntoa(*ip)); +#endif /* SCRIPTING */ + current = backupbot; + return; + } + } + src += unpack_ushort(&rtyp[8]); + n--; + } + + n = ntohs(query->authorities); + sz = (n > 1) ? RANDOM(1,n) : 1; +#ifdef DEBUG + if (n) + debug("(parse_query) auth: select %i count %i\n",sz,n); +#endif /* DEBUG */ + while(n) + { + src = get_dns_token(src,(const char*)query,token,psz); + rtyp = src; + src += 10; + if ((unpack_ushort(&rtyp[0]) == DNS_TYPE_NS) && + (unpack_ushort(&rtyp[2]) == DNS_CLASS_IN)) + { + dnsAuthority *da; + + get_dns_token(src,(const char *)query,token2,psz); + if (sz == n) + { + if (dns->auth == NULL) + { + set_mallocdoer(parse_query); + da = dns->auth = (dnsAuthority*)Calloc(sizeof(dnsAuthority) + strlen(token2)); + /* Calloc sets to zero da->ip.s_addr = 0; */ + Strcpy(da->hostname,token2); + } + else + if (dns->findauth == 1) + { + dns->findauth = 2; + if (dns->auth2) + Free((char**)&dns->auth2); + set_mallocdoer(parse_query); + da = dns->auth2 = (dnsAuthority*)Calloc(sizeof(dnsAuthority) + strlen(token2)); + /* Calloc sets to zero da->ip.s_addr = 0; */ + Strcpy(da->hostname,token2); +#ifdef DEBUG + debug("(parse_query) 2nd auth set: %s\n",token2); +#endif /* DEBUG */ + } + } +#ifdef DEBUG + debug("(parse_query) authorities: %s = %s%s\n",token,token2,(sz==n) ? MATCH_ALL : ""); +#endif /* DEBUG */ + } +#ifdef DEBUG + else + { + debug("(parse_query) DNS TYPE %s(%i), CLASS %i, size %i\n", + type_textlist[unpack_ushort(&rtyp[0])],unpack_ushort(&rtyp[0]), + unpack_ushort(&rtyp[2]),unpack_ushort(&rtyp[8])); + } +#endif /* DEBUG */ + src += unpack_ushort(&rtyp[8]); + n--; + } + + if (dns->findauth >= 1) + dns->findauth = 1; + + n = ntohs(query->resources); + while(n) + { + src = get_dns_token(src,(const char*)query,token,psz); + rtyp = src; + src += 10; + + if ( (unpack_ushort(&rtyp[0]) == DNS_TYPE_A) && + (unpack_ushort(&rtyp[2]) == DNS_CLASS_IN) && + (unpack_ushort(&rtyp[8]) == 4)) + { + ip = get_stored_ip(src); + if (dns->auth && !Strcasecmp(dns->auth->hostname,token)) + dns->auth->ip.s_addr = ip->s_addr; + if (dns->auth2 && !Strcasecmp(dns->auth2->hostname,token)) + dns->auth2->ip.s_addr = ip->s_addr; +#ifdef DEBUG + debug("(parse_query) resources: %s = %s\n",token,inet_ntoa(*ip)); +#endif /* DEBUG */ + } + src += unpack_ushort(&rtyp[8]); + n--; + } + + if (dns->auth && dns->auth->ip.s_addr == 0) + { + sai.sin_addr = dnsroot_lookup(dns->auth->hostname); + if (sai.sin_addr.s_addr != -1) + dns->auth->ip.s_addr = sai.sin_addr.s_addr; + } + + if (dns->auth2 && dns->auth2->ip.s_addr == 0) + { + sai.sin_addr = dnsroot_lookup(dns->auth2->hostname); + if (sai.sin_addr.s_addr != -1) + dns->auth2->ip.s_addr = sai.sin_addr.s_addr; + } + + if (dns->auth && dns->auth->ip.s_addr && dns->auth2) + { + Free((char**)&dns->auth2); + dns->findauth = 0; + } + +#ifdef DEBUG + debug("> dns->when %lu\n",dns->when); + debug("> dns->ip %s\n",inet_ntoa(dns->ip)); + debug("> dns->auth %s : %s (%i)\n",(dns->auth) ? dns->auth->hostname : NULLSTR, + (dns->auth) ? inet_ntoa(dns->auth->ip) : "-",(dns->auth) ? dns->auth->count : 0); + debug("> dns->auth2 %s : %s (%i)\n",(dns->auth2) ? dns->auth2->hostname : NULLSTR, + (dns->auth2) ? inet_ntoa(dns->auth2->ip) : "-",(dns->auth2) ? dns->auth2->count : 0); + debug("> dns->findauth %i\n",dns->findauth); + debug("> dns->id %i\n",dns->id); + debug("> dns->cname %s\n",nullstr(dns->cname)); + debug("> dns->host %s\n",dns->host); +#endif /* DEBUG */ + + src = NULL; + if (dns->auth2) + { + if (dns->auth2->ip.s_addr && dns->auth) + { + src = dns->auth->hostname; + sai.sin_addr.s_addr = dns->auth2->ip.s_addr; + } + else + { + src = dns->auth2->hostname; + sai.sin_addr.s_addr = (ia_ns[dnsserver].s_addr == 0) ? ia_default.s_addr : ia_ns[dnsserver].s_addr; + if (++dns->auth->count >= MAX_QUESTIONS) + { +#ifdef DEBUG + debug("(parse_query) dns->auth->count >= 32, starting over\n"); +#endif /* DEBUG */ + /* too many questions about who the authorative dns server is, start from scratch */ + Free((char**)&dns->auth); + Free((char**)&dns->auth2); + dns->findauth = 0; + src = (dns->cname) ? dns->cname : dns->host; + } + } + } + else + if (dns->auth) + { + if (dns->auth->ip.s_addr) + { + /* + * we know the IP of the authorative NS to ask + */ + src = (dns->cname) ? dns->cname : dns->host; + sai.sin_addr.s_addr = dns->auth->ip.s_addr; + } + else + { + /* + * have to dig up the IP of the NS to ask + */ + dns->findauth = 1; + src = dns->auth->hostname; + sai.sin_addr.s_addr = (ia_ns[dnsserver].s_addr == 0) ? ia_default.s_addr : ia_ns[dnsserver].s_addr; + if (++dns->auth->count >= MAX_QUESTIONS) + { +#ifdef DEBUG + debug("(parse_query) dns->auth->count >= 32, starting over\n"); +#endif /* DEBUG */ + /* too many questions about who the authorative dns server is, start from scratch */ + Free((char**)&dns->auth); + Free((char**)&dns->auth2); + dns->findauth = 0; + src = (dns->cname) ? dns->cname : dns->host; + } + } + } + if (src) + { + dns->id = rand(); +#ifdef DEBUG + debug("(parse_query) %i: asking %s who is `%s'\n",dns->id,inet_ntoa(sai.sin_addr),src); +#endif /* DEBUG */ + sz = make_query(packet,src); + dns->when = now + 60; + sai.sin_family = AF_INET; + sai.sin_port = htons(53); + ((dnsQuery*)packet)->qid = htons(dns->id); + if (sendto(dnssock,packet,sz,0,(struct sockaddr*)&sai,sizeof(sai)) < 0) + { + close(dnssock); + dnssock = -1; + } + } + if (dns->auth && dns->auth->ip.s_addr) + Free((char**)&dns->auth); + if (dns->auth2 && dns->auth2->ip.s_addr) + Free((char**)&dns->auth2); + if (src == NULL && dns->ip.s_addr == 0 && dns->cname && dns->host && dns->auth == NULL && dns->auth2 == NULL) + { + dns->id = rand(); + sai.sin_addr.s_addr = (ia_ns[dnsserver].s_addr == 0) ? ia_default.s_addr : ia_ns[dnsserver].s_addr; +#ifdef DEBUG + debug("(parse_query) %i: asking %s who is `%s' (CNAME question)\n",dns->id,inet_ntoa(sai.sin_addr),dns->cname); +#endif /* DEBUG */ + sz = make_query(packet,dns->cname); + dns->when = now + 60; + sai.sin_family = AF_INET; + sai.sin_port = htons(53); + ((dnsQuery*)packet)->qid = htons(dns->id); + if (sendto(dnssock,packet,sz,0,(struct sockaddr*)&sai,sizeof(sai)) < 0) + { + close(dnssock); + dnssock = -1; + } + } +} + +void rawdns(const char *hostname) +{ + struct sockaddr_in sai; + dnsQuery *query; + dnsList *item; + char packet[512]; + int sz; + + if (dnssock == -1) + init_rawdns(); + + if (dnssock == -1) + return; + + sz = make_query(packet,hostname); + query = (dnsQuery*)packet; + + set_mallocdoer(rawdns); + item = (dnsList*)Calloc(sizeof(dnsList) + strlen(hostname)); + Strcpy(item->host,hostname); + item->id = ntohs(query->qid); + item->when = now + 30; + item->next = dnslist; + dnslist = item; + + /* + * send the packet + */ + sai.sin_family = AF_INET; + sai.sin_port = htons(53); + sai.sin_addr.s_addr = (ia_ns[dnsserver].s_addr == 0) ? ia_default.s_addr : ia_ns[dnsserver].s_addr; + +#ifdef DEBUG + debug("(rawdns) questions %s: %s\n",inet_ntoa(sai.sin_addr),item->host); +#endif /* DEBUG */ + + dnsserver++; + if (ia_ns[dnsserver].s_addr == 0) + dnsserver = 0; + + if (sendto(dnssock,packet,sz,0,(struct sockaddr*)&sai,sizeof(sai)) < 0) + { + close(dnssock); + dnssock = -1; + } +} + +void select_rawdns(void) +{ + dnsList *dns,**pdns; + + if (dnssock != -1) + { + chkhigh(dnssock); + FD_SET(dnssock,&read_fds); + } +restart: + pdns = &dnslist; + while(*pdns) + { + if ((*pdns)->when < now) + { + dns = *pdns; + if (dns->cname) + Free((char**)&dns->cname); + if (dns->auth) + Free((char**)&dns->auth); + if (dns->auth2) + Free((char**)&dns->auth2); +#ifdef DEBUG + debug("(select_rawdns) removing %s qid %i\n",dns->host,dns->id); +#endif /* DEBUG */ + for(current=botlist;current;current=current->next) + { + send_pa(PA_DNS|PA_END,dns->host,"Unable to resolve %s",dns->host); + } +#ifdef SCRIPTING + dns_hook(dns->host,"~"); +#endif /* SCRIPTING */ + *pdns = dns->next; + Free((char**)&dns); + goto restart; + } + pdns = &(*pdns)->next; + } +} + +void process_rawdns(void) +{ + struct sockaddr_in sai; + char packet[512]; + int sz,n; + + if (dnssock == -1) + return; + if (FD_ISSET(dnssock,&read_fds)) + { + sz = sizeof(sai); + n = recvfrom(dnssock,packet,512,0,(struct sockaddr*)&sai,&sz); + if (n < sizeof(dnsQuery)) + return; +#ifdef DEBUG + debug("(process_rawdns) packet from: %s (%i bytes)\n",inet_ntoa(sai.sin_addr),n); +#endif /* DEBUG */ + parse_query(n,(dnsQuery*)packet); + } +} + +char *poll_rawdns(char *hostname) +{ + dnsList *dns; + + for(dns=dnslist;dns;dns=dns->next) + { + if (dns->ip.s_addr && !Strcasecmp(dns->host,hostname)) + { +#ifdef DEBUG + debug("(poll_rawdns) a: %s ==> %s\n",hostname,inet_ntoa(dns->ip)); +#endif /* DEBUG */ + return(inet_ntoa(dns->ip)); + } + } + return(NULL); +} + +LS int backup_debug; + +int read_dnsroot(char *line) +{ + struct in_addr ia; + dnsAuthority *da; + char *name,*a,*ip,*src; + + name = chop(&line); + a = chop(&line); /* TTL is optional */ + if (a && Strcmp(a,"A")) + a = chop(&line); + ip = chop(&line); + + if (a && !Strcmp(a,"A") && ip && inet_aton(ip,&ia) != 0) + { + /* remove trailing dot */ + for(src=name;*src;) + { + if (*src == '.' && *(src+1) == 0) + *src = 0; + else + src++; + } + set_mallocdoer(read_dnsroot); + da = (dnsAuthority*)Calloc(sizeof(dnsAuthority) + strlen(name)); + Strcpy(da->hostname,name); + da->ip.s_addr = ia.s_addr; + da->next = dnsroot; + dnsroot = da; +#ifdef DEBUG + dodebug = backup_debug; + debug("(read_dnsroot) stored root IP: %s = %s\n",name,ip); + dodebug = 0; +#endif /* DEBUG */ + } + return(FALSE); +} + +/* + * find the IP quickly + */ +ulong rawdns_get_ip(const char *host) +{ + ulong ip; + + if ((ip = inet_addr(host)) == INADDR_NONE) + { + } +#ifdef DEBUG + debug("(rawdns_get_ip) %s -> %s\n",host,inet_ntoa(*((struct in_addr*)&ip))); +#endif /* DEBUG */ + return(ip); +} + +/* + * + * commands related to DNS + * + */ + +void do_dnsroot(COMMAND_ARGS) +{ + int in; + + if ((in = open(rest,O_RDONLY)) >= 0) + { +#ifdef SESSION + Strp *p; + + p = (Strp*)Calloc(strlen(rest)+1); + p->next = dnsrootfiles; + Strcpy(p->p,rest); + dnsrootfiles = p; +#endif /* SESSION */ +#ifdef DEBUG + backup_debug = dodebug; + dodebug = 0; +#endif /* DEBUG*/ + readline(in,&read_dnsroot); /* readline closes in */ +#ifdef DEBUG + dodebug = backup_debug; +#endif /* DEBUG */ + } +} + +void do_dnsserver(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + */ + struct in_addr ia; + char *p,c,tempservers[MAX_NAMESERVERS*16+3]; + /* (xxx.yyy.zzz.www + 1 space * MAX_NAMESERVERS) + 1 terminator char + 2 chars bold font */ + int i; + + if (!*rest) + { + *(p = tempservers) = 0; + for(i=0;i 0) + { + if (i == dnsserver) + { + sprintf(p,"\037%s\037 ",inet_ntoa(ia_ns[i])); + p = STREND(p); + } + else + { + p = Strcpy(p,inet_ntoa(ia_ns[i])); + *(p++) = ' '; + *p = 0; + } + } + } + if (*tempservers == 0) + Strcpy(tempservers,"\037127.0.0.1\037"); + to_user(from,"Current DNS Servers: %s",tempservers); + return; + } + + c = *(rest++); + if ((ia.s_addr = inet_addr(rest)) == INADDR_NONE) + c = 0; + + switch(c) + { + case '+': + for(i=0;i 0) + to_user(from,"DNS Server removed: %s",rest); + else + to_user(from,"All known DNS Servers removed."); + return; + default: + usage(from); + } +} + +/* + * resolve the dns of a user/host + * usage: DNS + */ +void do_dns(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS and GAXS + */ + char *host,*res,*src,*dst,*dot; + ulong ip; + + /* to date, all hostnames contain atleast one dot */ + if ((STRCHR(rest,'.'))) + { + host = rest; + } + else + { + /* no dots, try to find it as a nick */ + /* searches all channels and nicks, clobbers get_nuh */ + if ((host = find_nuh(rest)) == NULL) + { + to_user(from,"Unable to resolve %s: unknown nick/host",rest); + return; + } + while(*host && *host != '@') + host++; + if (*host == '@') + host++; +#ifdef DEBUG + debug("(do_dns) %s is on host %s\n",rest,host); +#endif /* DEBUG */ + } + if ((ip = inet_addr(host)) != INADDR_NONE) + { + /* flip an IP backwards to resolve hostname */ + // a11.b22.c33.d44 + // d44.c33.b22.a11.in-addr.arpa + dst = gsockdata; +flipstep: + src = host; + dot = NULL; + + while(*src) + { + if (*src == '.') + dot = src; + src++; + } + if (dot) + { + *dot++ = 0; // safe to modify buffer? + while(*dot) + *dst++ = *dot++; + *dst++ = '.'; + goto flipstep; + } + Strcpy(Strcpy(dst,host),".in-addr.arpa"); +#ifdef DEBUG + debug("(do_dns) host flipped to %s\n",gsockdata); +#endif /* DEBUG */ + host = gsockdata; + } + /* check if its in cache now */ + if ((res = poll_rawdns(host))) + { + //Resolved: irc.dal.net (194.68.45.50) + send_pa(PA_DNS|PA_END,NULL,"Resolved: %s (%s)",host,res); + return; + } + + make_ireq(PA_DNS,from,host); + rawdns(host); +} + +#endif /* RAWDNS */ diff --git a/src/dynamode.c b/src/dynamode.c new file mode 100644 index 0000000..9727d87 --- /dev/null +++ b/src/dynamode.c @@ -0,0 +1,88 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define DYNAMODE_C +#include "config.h" + +#ifdef DYNAMODE + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" + +void check_dynamode(Chan *chan) +{ + ChanUser *cu; + char tempconf[strlen(chan->setting[STR_DYNLIMIT].str_var)+2]; + char ascnum[11]; + char *src,*num,*end; + int n = 0,wind,v[3]; + + /* + * parse `delay:window:minwin' + */ + end = Strcpy(tempconf,chan->setting[STR_DYNLIMIT].str_var); + num = src = tempconf; + for(;(src<=end) && (n<3);src++) + { + if (*src == 0 || *src == ':') + { + *src = 0; + v[n] = a2i(num); + if (errno) + { + v[0] = 90; /* delay */ + v[1] = 10; /* window */ + v[2] = 4; /* minwin */ + break; + } + num = src+1; + n++; + } + } + v[0] = (v[0] < 20) ? 20 : (v[0] > 600) ? 600 : v[0]; + if ((now - chan->lastlimit) < v[0]) + return; + v[1] = (v[1] < 5) ? 5 : (v[1] > 50) ? 50 : v[1]; + v[2] = (v[2] < 1) ? 1 : (v[2] > 50) ? 50 : v[2]; + + chan->lastlimit = now; + + n = 0; + for(cu=chan->users;cu;cu=cu->next) + n++; + + wind = n / v[1]; + if (wind < v[2]) + wind = v[2]; + + wind += n; + + n = wind - chan->limit; + + if (!chan->limitmode || (n < -2) || (n > 1)) + { + sprintf(ascnum,"%i",wind); + send_mode(chan,160,QM_RAWMODE,'+','l',ascnum); + } +} + +#endif /* DYNAMODE */ diff --git a/src/function.c b/src/function.c new file mode 100644 index 0000000..3dab453 --- /dev/null +++ b/src/function.c @@ -0,0 +1,1096 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define FUNCTION_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" + +LS char timebuf[24]; /* max format lentgh == 20+1, round up to nearest longword -> 24 */ +LS char idlestr[36]; /* max format lentgh == 24+1, round up to nearest longword -> 28 */ + +LS const char monlist[12][4] = +{ + "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" +}; + +LS const char daylist[7][4] = +{ + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" +}; + +/* + * memory allocation routines + */ + +#ifdef DEBUG + +__attr(CORE_SEG,__regparm(1)) +void *Calloc(int size) +{ + aME *mmep; + aMEA *mp; + int i; + +#ifdef __GNUC__ + if (mallocdoer == NULL) + { + mallocdoer = __builtin_return_address(0); + debug("(Calloc) mallocdoer = "mx_pfmt"\n",(mx_ptr)mallocdoer); + mallocdoer = NULL; + } +#endif /* GNUC */ + + mmep = NULL; + mp = mrrec; + while(!mmep) + { + for(i=0;imme[i].area == NULL) + { + mmep = &mp->mme[i]; + break; + } + } + if (!mmep) + { + if (mp->next == NULL) + { + mp->next = calloc(sizeof(aMEA),1); + mmep = &mp->next->mme[0]; + } + else + mp = mp->next; + } + } + + /* + * heap check +4 bytes + */ + if ((mmep->area = (void*)calloc(size+4,1)) == NULL) + { + run_debug(); + exit(1); + } + mmep->size = size; + mmep->when = now; + mmep->doer = mallocdoer; + mallocdoer = NULL; + return((void*)mmep->area+4); +} + +__attr(CORE_SEG,__regparm(1)) +void Free(char **mem) +{ + aME *mmep; + aMEA *mp; + int *src; + int i; + + if (*mem == NULL) + return; + + src = (int*)((*mem)-4); + mmep = NULL; + mp = mrrec; + while(!mmep) + { + for(i=0;imme[i].area == src) + { + mmep = &mp->mme[i]; + break; + } + } + if (!mmep) + { + if (mp->next == NULL) + { + debug("(Free) PANIC: Free(0x"mx_pfmt"); Unregistered memory block\n",(mx_ptr)src); + run_debug(); + exit(1); + } + mp = mp->next; + } + } + + if (*src) + debug("Free: Heap corruption at 0x"mx_pfmt"\n",(mx_ptr)(*mem)-4); + mmep->area = NULL; + mmep->size = 0; + mmep->when = (time_t)0; + free((*mem)-4); + *mem = NULL; +} + +#else /* DEBUG */ + +__attr(CORE_SEG,__regparm(1)) +void *Calloc(int size) +{ + void *tmp; + + if ((tmp = (void*)calloc(size,1)) == NULL) + exit(1); + return((void*)tmp); +} + +/* + * Free() can be called with NULL's + */ +__attr(CORE_SEG,__regparm(1)) +void Free(char **mem) +{ + if (*mem) + { + free(*mem); + *mem = NULL; + } +} + +#endif /* DEBUG */ + +int Strlen(const char *first, ...) +{ + const char *s,*o; + int n; + va_list vlist; + + va_start(vlist,first); + + n = 0; + o = s = first; + do + { + while(*s) + s++; + n += (s - o); + s = o = va_arg(vlist,const char *); + } + while(s); + + va_end(vlist); + return(n); +} + +__attr(CORE_SEG,__regparm(2)) +int Strlen2(const char *one, const char *two) +{ + const char *s1,*s2; + + for(s1=one;*s1;s1++); + for(s2=two;*s2;s2++); + return((s1 - one) + (s2 - two)); +} + +__attr(CORE_SEG,__regparm(2)) +char *nickcpy(char *dest, const char *nuh) +{ + char *ret; + + if (!dest) + dest = nick_buf; + ret = dest; + + while(*nuh && (*nuh != '!')) + *(dest++) = *(nuh++); + *dest = 0; + + return(ret); +} + +char *getuh(char *nuh) +{ + char *s; + + s = nuh; + while(*s) + { + if (*s == '!') + { + nuh = s + 1; + /* + * We have to grab everything from the first '!' since some + * braindamaged ircds allow '!' in the "user" part of the nuh + */ + break; + } + s++; + } + return(nuh); +} + +/* + * caller is responsible for: + * + * src != NULL + * *src != NULL + * + */ +char *get_token(char **src, const char *token_sep) +{ + const char *s; + char *token = NULL; + + /* + * skip past any token_sep chars in the beginning + */ + + if (0) /* is this legal C? */ +a: ++(*src); + s = token_sep; + while(**src && *s) + { + if (*(s++) == **src) + goto a; + } + + if (token || **src == 0) + return(token); + + token = *src; + + /* + * find the next token_sep char + */ + do { + s = token_sep; + do { + if (*s == **src) + { + **src = 0; + goto a; + } + ++s; + } + while(*s); + (*src)++; + } + while(**src); + + return(token); +} + +/* + * time to string routines + */ + +char *logtime(time_t when) +{ + struct tm *btime; + + btime = localtime(&when); + sprintf(timebuf,"%s %i %i %02i:%02i:%02i", /* max format length: 20+1 */ + monlist[btime->tm_mon],btime->tm_mday,btime->tm_year+1900, + btime->tm_hour,btime->tm_min,btime->tm_sec); + return(timebuf); +} + +char *time2str(time_t when) +{ + struct tm *btime; + + if (!when) + return(NULL); + + btime = localtime(&when); + sprintf(timebuf,"%02i:%02i:%02i %s %02i %i", /* max format length: 20+1 */ + btime->tm_hour,btime->tm_min,btime->tm_sec,monlist[btime->tm_mon], + btime->tm_mday,btime->tm_year+1900); + return(timebuf); +} + +char *time2away(time_t when) +{ + struct tm *btime; + char ampm; + + if (!when) + return(NULL); + + btime = localtime(&when); + if (btime->tm_hour < 12) + { + if (btime->tm_hour == 0) + btime->tm_hour = 12; + ampm = 'a'; + } + else + { + if (btime->tm_hour != 12) + btime->tm_hour -= 12; + ampm = 'p'; + } + + sprintf(timebuf,"%i:%02i%cm %s %s %i", /* max format length: 18+1 */ + btime->tm_hour,btime->tm_min,ampm,daylist[btime->tm_wday], + monlist[btime->tm_mon],btime->tm_mday); + return(timebuf); +} + +char *time2medium(time_t when) +{ + struct tm *btime; + + btime = localtime(&when); + sprintf(timebuf,"%02i:%02i", /* max format length: 5+1 */ + btime->tm_hour,btime->tm_min); + return(timebuf); +} + +char *time2small(time_t when) +{ + struct tm *btime; + + btime = localtime(&when); + sprintf(timebuf,"%s %02i", /* max format length: 6+1 */ + monlist[btime->tm_mon],btime->tm_mday); + return(timebuf); +} + +char *idle2str(time_t when, int small) +{ + char *dst; + int n,z[4]; + + z[0] = when / 86400; + z[1] = (when -= z[0] * 86400) / 3600; + z[2] = (when -= z[1] * 3600) / 60; + z[3] = when % 60; + + /* 32 : "9999 days, 24 hours, 59 minutes" */ + /* xx : "24 hours, 59 minutes" */ + /* xx : "59 minutes, 59 seconds" */ + /* xx : "59 seconds" */ + if (small) + { + char *f[] = {"day","hour","minute","second"}; + + *idlestr = 0; + for(n=0;n<4;n++) + { + if (*idlestr || z[n]) + { + dst = STREND(idlestr); + sprintf(dst,"%s%i %s%s",(*idlestr) ? ", " : "",z[n],f[n],(z[n]==1) ? "" : "s"); + } + } + } + else + /* 18+1 (up to 9999 days) */ + sprintf(idlestr,"%i day%s %02i:%02i:%02i",z[0],EXTRA_CHAR(z[0]),z[1],z[2],z[3]); + return(idlestr); +} + +__attr(CMD1_SEG,__regparm(2)) +char *get_channel(char *to, char **rest) +{ + char *channel; + + if (*rest && ischannel(*rest)) + { + channel = chop(rest); + } + else + { + if (!ischannel(to) && current->activechan) + channel = current->activechan->name; + else + channel = to; + } + return(channel); +} + +char *get_channel2(char *to, char **rest) +{ + char *channel; + + if (*rest && (**rest == '*' || ischannel(*rest))) + { + channel = chop(rest); + } + else + { + if (!ischannel(to) && current->activechan) + channel = current->activechan->name; + else + channel = to; + } + return(channel); +} + +char *cluster(char *hostname) +{ + char mask[NUHLEN]; + char *p,*host; + char num,dot; + + host = p = hostname; + num = dot = 0; + while(*p) + { + if (*p == '@') + { + host = p + 1; + num = dot = 0; + } + else + if (*p == '.') + dot++; + else + if (*p < '0' || *p > '9') + num++; + p++; + } + + if (!num && (dot == 3)) + { + /* + * its a numeric IP address + * 1.2.3.4 --> 1.2.*.* + */ + p = mask; + while(*host) + { + if (*host == '.') + { + if (num) + break; + num++; + } + *(p++) = *(host++); + } + Strcpy(p,".*.*"); + } + else + { + /* + * its not a numeric mask + */ + p = mask; + *(p++) = '*'; + num = (dot >= 4) ? 2 : 1; + while(*host) + { + if (*host == '.') + dot--; + if (dot <= num) + break; + host++; + } + Strcpy(p,host); + } + Strcpy(hostname,mask); + return(hostname); +} + +/* + * type output + * ~~~~ ~~~~~~ + * 0,1 *!*user@*.host.com + * 2 *!*@*.host.com + */ +char *format_uh(char *userhost, int type) +{ + char tmpmask[NUHLEN]; + char *u,*h; + + if (STRCHR(userhost,'*')) + return(userhost); + + Strcpy(tmpmask,userhost); + + h = tmpmask; + get_token(&h,"!"); /* discard nickname */ + u = get_token(&h,"@"); + + if (*h == 0) + return(userhost); + + if (u && (type < 2)) + { + if ((type = strlen(u)) > 9) + u += (type - 9); + else + if (*u == '~') + u++; + } + sprintf(userhost,"*!*%s@%s",(u) ? u : "",cluster(h)); + return(userhost); +} + +/* + * NOTE! beware of conflicts in the use of nuh_buf, its also used by find_nuh() + */ +char *nick2uh(char *from, char *userhost) +{ + if (STRCHR(userhost,'!') && STRCHR(userhost,'@')) + { + Strcpy(nuh_buf,userhost); + } + else + if (!STRCHR(userhost,'!') && !STRCHR(userhost,'@')) + { + /* find_nuh() stores nickuserhost in nuh_buf */ + if (find_nuh(userhost) == NULL) + { + if (from) + to_user(from,"No information found for %s",userhost); + return(NULL); + } + } + else + { + Strcpy(nuh_buf,"*!"); + if (!STRCHR(userhost,'@')) + Strcat(nuh_buf,"*@"); + Strcat(nuh_buf,userhost); + } + return(nuh_buf); +} + +void deop_ban(Chan *chan, ChanUser *victim, char *mask) +{ + if (!mask) + mask = format_uh(get_nuh(victim),FUH_USERHOST); + send_mode(chan,85,QM_CHANUSER,'-','o',victim); + send_mode(chan,90,QM_RAWMODE,'+','b',mask); +} + +void deop_siteban(Chan *chan, ChanUser *victim) +{ + char *mask; + + mask = format_uh(get_nuh(victim),FUH_HOST); + deop_ban(chan,victim,mask); +} + +void screwban_format(char *userhost) +{ + int sz,n,pos; + +#ifdef DEBUG + debug("(screwban_format) %s\n",userhost); +#endif /* DEBUG */ + + if ((sz = strlen(userhost)) < 8) + return; + + n = RANDOM(4,sz); + while(--n) + { + pos = RANDOM(0,(sz - 1)); + if (!STRCHR("?!@*",userhost[pos])) + { + userhost[pos] = (RANDOM(0,3) == 0) ? '*' : '?'; + } + } +} + +void deop_screwban(Chan *chan, ChanUser *victim) +{ + char *mask; + int i; + + for(i=2;--i;) + { + mask = format_uh(get_nuh(victim),FUH_USERHOST); + screwban_format(mask); + deop_ban(chan,victim,mask); + } +} + +int is_nick(const char *nick) +{ + uchar *p; + + p = (uchar*)nick; + if ((attrtab[*p] & FNICK) != FNICK) + return(FALSE); + while(*p) + { + if ((attrtab[*p] & NICK) != NICK) + return(FALSE); + p++; + } + return(TRUE); +} + +int capslevel(char *text) +{ + int sz,upper; + + if (!*text) + return(0); + + sz = upper = 0; + while(*text) + { + if ((*text >= 'A' && *text <= 'Z') || (*text == '!')) + upper++; + sz++; + text++; + } + sz = sz / 2; + return(upper >= sz); +} + +__attr(CORE_SEG,__regparm(1)) +int a2i(char *anum) +{ + int res = 0,neg; + + errno = EINVAL; + + if (!anum || !*anum) + return(-1); + + neg = (*anum == '-') ? 1 : 0; + anum += neg; + + while(*anum) + { + if (0 == (attrtab[(uchar)*anum] & NUM)) + return(-1); + res = (res * 10) + *(anum++) - '0'; + } + errno = 0; + return((neg) ? -res : res); +} + +int get_number(const char *rest) +{ + const char *src = NULL; + int n = 0; + + while(*rest) + { + if (*rest >= '0' && *rest <= '9') + n = (n * 10) + (*(src = rest) - '0'); + else + if (src) + return(n); + rest++; + } + return(n); +} + +__attr(CORE_SEG,__regparm(1)) +void fix_config_line(char *text) +{ + char *s,*space; + + space = NULL; + for(s=text;*s;s++) + { + if (*s == '\t') + *s = ' '; + if (!space && *s == ' ') + space = s; + if (space && *s != ' ') + space = NULL; + } + if (space) + *space = 0; +} + +/* + * returns NULL or non-zero length string + */ +__attr(CORE_SEG,__regparm(1)) +char *chop(char **src) +{ + char *tok,*cut = *src; + + while(*cut && *cut == ' ') + cut++; + + if (*cut) + { + tok = cut; + while(*cut && *cut != ' ') + cut++; + *src = cut; + while(*cut && *cut == ' ') + cut++; + **src = 0; + *src = cut; + } + else + { + tok = NULL; + } + return(tok); +} + +/* + * remove all '\0' in an array bounded by two pointers + */ +__attr(CORE_SEG,__regparm(2)) +void unchop(char *orig, char *rest) +{ + for(;orig orig));m--) + ; + if ((*m == '*') && (m > orig) && (m[-1] != '\\')) + return(FALSE); + if (wild) + { + m = (uchar *)mask; + n = (uchar *)++text; + } + else + return(TRUE); + } + else + if (!*n) + { + while(*m == '*') + m++; + return(*m != 0); + } + + if (*m == '*') + { + while (*m == '*') + m++; + wild = 1; + mask = (char *)m; + text = (char *)n; + } + + if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) + { + m++; + q = 1; + } + else + q = 0; + + if ((tolowertab[(uchar)*m] != tolowertab[(uchar)*n]) && ((*m != '?') || q)) + { + if (wild) + { + m = (uchar *)mask; + n = (uchar *)++text; + } + else + return(TRUE); + } + else + { + if (*m) + m++; + if (*n) + n++; + } + goto loop; +} + +__att2(CORE_SEG,const,__regparm(2)) +int num_matches(const char *mask, const char *text) +{ + const char *p = mask; + int n; + + n = !matches(mask,text); + + if (n) + { + do { + if (*p != '*') + n++; + } + while(*++p); + } + return(n); +} + +Strp *e_table = NULL; + +void table_buffer(const char *format, ...) +{ + Strp **sp; + va_list msg; + int sz; + + va_start(msg,format); + sz = sizeof(Strp) + vsprintf(gsockdata,format,msg); + va_end(msg); + + for(sp=&e_table;*sp;sp=&(*sp)->next) + ; + + set_mallocdoer(table_buffer); + *sp = (Strp*)Calloc(sz); + /* Calloc sets to zero (*sp)->next = NULL; */ + Strcpy((*sp)->p,gsockdata); +} + +__attr(CMD1_SEG, __regparm (2) ) +void table_send(const char *from, const int space) +{ + char message[MAXLEN]; + Strp *sp,*next; + char *src,*o,*end; + int i,u,g,x,z[6]; + + z[0] = z[1] = z[2] = z[3] = z[4] = z[5] = 0; + for(sp=e_table;sp;sp=sp->next) + { + u = i = 0; + src = o = sp->p; + while(*src) + { + if (*src == '\037' || *src == '\002') + u++; + if (*src == '\t' || *src == '\r') + { + x = (src - o) - u; + if (x > z[i]) + z[i] = x; + i++; + o = src+1; + u = 0; + } + src++; + } + } + + for(sp=e_table;sp;sp=next) + { + next = sp->next; + + o = message; + src = sp->p; + g = x = i = 0; + while(*src) + { + if (g) + { + end = src; + while(*end && *end != '\t' && *end != '\r') + end++; + g -= (end - src); + while(g-- > 0) + *(o++) = ' '; + } + if (*src == '\037' || *src == '\002') + x++; + if (*src == '\t' || *src == '\r') + { + if (*src == '\r') + g = z[i+1]; + src++; + x += (z[i++] + space); + while(o < (message + x)) + *(o++) = ' '; + } + else + *(o++) = *(src++); + } + *o = 0; + to_user(from,FMT_PLAIN,message); + + Free((char**)&sp); + } + e_table = NULL; +} + +int is_safepath(const char *path) +{ + struct stat st; + ino_t ino; + char tmp[PATH_MAX]; + const char *src; + char *dst; + + int path_token_check(void) + { + *dst = 0; + lstat(tmp,&st); + if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) + return(FALSE); + if (st.st_ino == parent_inode) + return(FALSE); + return(S_ISDIR(st.st_mode) ? 2 : 1); + } + + if (*(src = path) == '/') + return(FALSE); + + dst = tmp; + while(*src) + { + if (*src == '/' && !path_token_check()) + return(FALSE); + if (dst == tmp + PATH_MAX-1) + return(FALSE); + *dst++ = *src++; + } + return(path_token_check() == 1 ? TRUE : FALSE); +} diff --git a/src/gencmd.c b/src/gencmd.c new file mode 100644 index 0000000..fa6068d --- /dev/null +++ b/src/gencmd.c @@ -0,0 +1,433 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define GENCMD_C +#include "config.h" +#include "structs.h" + +/* + + These are defined in config.h + + DCC 0x00100 requires DCC + CC 0x00200 requires commandchar + PASS 0x00400 requires password / authentication + CARGS 0x00800 requires args + NOPUB 0x01000 ignore in channel (for password commands) + NOCMD 0x02000 not allowed to be executed thru CMD + GAXS 0x04000 check global access + CAXS 0x08000 check channel access + REDIR 0x10000 may be redirected + LBUF 0x20000 can be linebuffered to server + CBANG 0x40000 command may be prefixed with a bang (!) + ACCHAN 0x80000 needs an active channel + + CLEVEL 0x000ff + +*/ + +#define CCPW CC|PASS + +struct +{ + int pass; + char *name; + char *func; + ulong flags; + char *cmdarg; + +} pre_mcmd[] = +{ + /* + * public access commands + */ + { 0, "VERIFY", "do_auth", 0 | NOPUB }, +#ifdef TOYBOX + { 0, "8BALL", "do_8ball", 0 | CBANG }, +#endif /* TOYBOX */ + + /* + * Level 10 + */ + { 0, "ACCESS", "do_access", 10 | CCPW }, + { 0, "BYE", "do_bye", 10 | CC }, + { 0, "CHAT", "do_chat", 10 | CCPW | NOCMD }, +#ifdef RAWDNS + { 0, "DNS", "do_dns", 10 | CCPW | GAXS | CARGS }, +#endif /* RAWDNS */ + { 0, "DOWN", "do_opdeopme", 10 | CC | CAXS }, + { 0, "ECHO", "do_echo", 10 | CCPW | CARGS }, + { 0, "HELP", "do_help", 10 | CCPW | REDIR | LBUF }, + { 0, "PASSWD", "do_passwd", 10 | PASS | NOPUB | CARGS }, +#ifdef DCC_FILE + { 0, "SEND", "do_send", 10 | CC | NOCMD | CBANG | CARGS }, +#endif /* DCC_FILE */ + { 0, "USAGE", "do_usage", 10 | CCPW | REDIR | CARGS }, + + /* + * Level 20 + */ + { 0, "ONTIME", "do_upontime", 20 | CCPW , "Ontime: %s" }, + { 0, "UPTIME", "do_upontime", 20 | CCPW , "Uptime: %s" }, + { 0, "VER", "do_version", 20 | CCPW }, + { 0, "WHOM", "do_whom", 20 | CCPW | REDIR | LBUF }, +#ifdef SEEN + { 0, "SEEN", "do_seen", 20 | CCPW | CBANG }, +#endif /* SEEN */ + + /* + * Level 40 + */ + { 0, "BAN", "do_kickban", 40 | CCPW | CAXS | CARGS | ACCHAN , "\\x00ban\\0bann" }, + { 0, "BANLIST", "do_banlist", 40 | CCPW | CAXS | DCC | REDIR | LBUF | ACCHAN }, + { 0, "CCHAN", "do_cchan", 40 | CCPW }, /* global_access ? */ + { 0, "CSERV", "do_cserv", 40 | CCPW }, + { 0, "CHANNELS", "do_channels", 40 | CCPW | DCC }, + { 0, "DEOP", "do_opvoice", 40 | CCPW | CAXS | CARGS , "o-" }, + { 0, "ESAY", "do_esay", 40 | CCPW | CAXS | CARGS }, + { 0, "IDLE", "do_idle", 40 | CCPW | CARGS }, + { 0, "INVITE", "do_invite", 40 | CCPW | CAXS | ACCHAN }, + { 0, "KB", "do_kickban", 40 | CCPW | CAXS | CARGS | ACCHAN , "\\x04kickban\\0kickbann" }, + { 0, "KICK", "do_kickban", 40 | CCPW | CAXS | CARGS | ACCHAN , "\\x07kick\\0kick" }, + { 0, "LUSERS", "do_irclusers", 40 | CCPW | DCC | REDIR | LBUF }, + { 0, "ME", "do_sayme", 40 | CCPW | CARGS }, + { 0, "MODE", "do_mode", 40 | CCPW | CARGS }, + { 0, "NAMES", "do_names", 40 | CCPW }, + { 0, "OP", "do_opvoice", 40 | CCPW | CAXS , "o+" }, + { 0, "SAY", "do_sayme", 40 | CCPW | CARGS }, + { 0, "SCREW", "do_kickban", 40 | CCPW | CAXS | CARGS | ACCHAN , "\\x02screwban\\0screwbann" }, + { 0, "SET", "do_set", 40 | CCPW }, + { 0, "SITEBAN", "do_kickban", 40 | CCPW | CAXS | CARGS | ACCHAN , "\\x01siteban\\0sitebann" }, + { 0, "SITEKB", "do_kickban", 40 | CCPW | CAXS | CARGS | ACCHAN , "\\x05sitekickban\\0sitekickbann" }, + { 0, "TIME", "do_time", 40 | CCPW }, + { 0, "TOPIC", "do_topic", 40 | CCPW | CAXS | CARGS | ACCHAN }, + { 0, "UNBAN", "do_unban", 40 | CCPW | CAXS }, + { 0, "UNVOICE", "do_opvoice", 40 | CCPW | CAXS | CARGS , "v-" }, + { 0, "UP", "do_opdeopme", 40 | CCPW | CAXS }, + { 0, "USER", "do_user", 40 | CCPW | CARGS }, + { 0, "USERHOST", "do_ircwhois", 40 | CCPW | CARGS }, + { 0, "VOICE", "do_opvoice", 40 | CCPW | CAXS , "v+" }, + { 0, "WALL", "do_wall", 40 | CCPW | CAXS | CARGS | ACCHAN }, + { 0, "WHO", "do_showusers", 40 | CCPW | CAXS | DCC }, + { 0, "WHOIS", "do_ircwhois", 40 | CCPW | CARGS | DCC | REDIR | LBUF }, +#ifdef NOTE + { 0, "NOTE", "do_note", 40 | CCPW | CARGS }, + { 0, "READ", "do_read", 40 | CCPW }, +#endif /* NOTE */ +#ifdef STATS + { 0, "INFO", "do_info", 40 | CCPW | CAXS | DCC }, +#endif /* STATS */ + + /* + * Level 50 + */ + { 0, "QSHIT", "do_shit", 50 | CCPW | CARGS }, + { 0, "RSHIT", "do_rshit", 50 | CCPW | CARGS }, + { 0, "SHIT", "do_shit", 50 | CCPW | CARGS }, + { 0, "SHITLIST", "do_shitlist", 50 | CCPW | DCC | REDIR | LBUF }, +#ifdef GREET + { 0, "GREET", "do_greet", 50 | CCPW | CARGS }, +#endif /* GREET */ +#ifdef TOYBOX + { 0, "INSULT", "do_random_msg", 50 | CCPW , RANDINSULTFILE }, + { 0, "PICKUP", "do_random_msg", 50 | CCPW , RANDPICKUPFILE }, + { 0, "RSAY", "do_random_msg", 50 | CCPW , RANDSAYFILE }, + { 0, "RT", "do_randtopic", 50 | CCPW | CAXS | ACCHAN }, +#endif /* TOYBOX */ +#ifdef TRIVIA + { 0, "TRIVIA", "do_trivia", 50 | CCPW | CAXS | CARGS | CBANG }, +#endif /* TRIVIA */ + + /* + * Level 60 + */ + { 0, "SHOWIDLE", "do_showidle", 60 | CCPW | CAXS | DCC | ACCHAN }, + { 0, "USERLIST", "do_userlist", 60 | CCPW | DCC }, +#ifdef CTCP + { 0, "CTCP", "do_ping_ctcp", 60 | CCPW | CARGS }, + { 0, "PING", "do_ping_ctcp", 60 | CCPW | CARGS }, +#endif /* CTCP */ + + /* + * Level 70 == JOINLEVEL + */ + { 0, "CYCLE", "do_cycle", 70 | CCPW | CAXS | ACCHAN }, + { 0, "FORGET", "do_forget", 70 | CCPW | CARGS }, + { 0, "JOIN", "do_join", 70 | CCPW | CARGS }, + { 0, "KS", "do_kicksay", 70 | CCPW | REDIR | LBUF }, + { 0, "PART", "do_part", 70 | CCPW | CAXS | ACCHAN }, + { 0, "RKS", "do_rkicksay", 70 | CCPW | CARGS }, + { 0, "SETPASS", "do_setpass", 70 | CCPW | NOPUB | CARGS }, +#ifdef NOTIFY + { 0, "NOTIFY", "do_notify", 70 | CCPW | DCC | GAXS | REDIR | LBUF }, +#endif /* NOTIFY */ + + /* + * Level 80 == ASSTLEVEL + */ + { 0, "AWAY", "do_away", 80 | CCPW | GAXS }, +#if defined(BOTNET) && defined(REDIRECT) + { 0, "CMD", "do_cmd", 80 | CCPW | CARGS }, +#endif /* BOTNET && REDIRECT */ + { 0, "LAST", "do_last", 80 | CCPW | DCC }, + { 0, "LOAD", "do_load", 80 | CCPW | GAXS }, + { 0, "MSG", "do_msg", 80 | CCPW | CARGS }, + { 0, "NEXTSERVER", "do_server", 80 | CCPW | GAXS }, + { 0, "SAVE", "do_save", 80 | CCPW | GAXS }, + { 0, "SERVER", "do_server", 80 | CCPW | GAXS | REDIR | LBUF }, + { 0, "SERVERGROUP", "do_servergroup", 80 | CCPW | GAXS | REDIR | LBUF }, + { 0, "STATS", "do_ircstats", 80 | CCPW | DCC | CARGS }, +#ifdef ALIAS + { 0, "ALIAS", "do_alias", 80 | CCPW | GAXS }, + { 0, "UNALIAS", "do_unalias", 80 | CCPW | GAXS | CARGS }, +#endif /* ALIAS */ +#ifdef TOYBOX + { 0, "BIGSAY", "do_bigsay", 80 | CCPW | CAXS | CARGS }, +#endif /* TOYBOX */ + + /* + * Level 90 + */ + { 0, "CLEARSHIT", "do_clearshit", 90 | CCPW | GAXS }, + { 0, "DO", "do_do", 90 | CCPW | GAXS | CARGS }, + { 0, "NICK", "do_nick", 90 | CCPW | GAXS | CARGS }, + { 0, "RSPY", "do_rspy", 90 | CCPW | CARGS }, + { 0, "SPY", "do_spy", 90 | CCPW }, +#ifdef BOTNET + { 0, "LINK", "do_link", 90 | CCPW | GAXS }, +#endif /* BOTNET */ +#ifdef DYNCMD + { 0, "CHACCESS", "do_chaccess", 90 | CCPW | GAXS | CARGS }, +#endif /* DYNCMD */ +#ifdef UPTIME + { 0, "UPSEND", "do_upsend", 90 | CCPW | GAXS }, +#endif /* UPTIME */ + + /* + * Level 100 + */ +#ifdef RAWDNS + { 0, "DNSSERVER", "do_dnsserver", 100 | CCPW | GAXS }, + { 0, "DNSROOT", "do_dnsroot", 100 | CCPW | GAXS | CARGS }, +#endif /* RAWDNS */ + { 0, "CORE", "do_core", 100 | CCPW | DCC }, + { 0, "DIE", "do_die", 100 | CCPW | GAXS }, + { 0, "RESET", "do_reset", 100 | CCPW | GAXS | NOCMD }, + { 0, "SHUTDOWN", "do_shutdown", 100 | CCPW | GAXS | NOPUB | NOCMD }, +#ifdef DEBUG + { 0, "DEBUG", "do_debug", 100 | CCPW | GAXS }, +#endif /* DEBUG */ +#ifdef PYTHON +#ifdef PLEASE_HACK_MY_SHELL + { 0, "PYTHON", "do_python", 100 | CCPW | GAXS | CARGS }, +#endif /* PLEASE_HACK_MY_SHELL */ + { 0, "PYTHONSCRIPT", "do_pythonscript", 100 | CCPW | GAXS | CARGS }, +#endif /* PYTHON */ +#ifdef TCL +#ifdef PLEASE_HACK_MY_SHELL + { 0, "TCL", "do_tcl", 100 | CCPW | GAXS | CARGS }, +#endif /* PLEASE_HACK_MY_SHELL */ + { 0, "TCLSCRIPT", "do_tcl", 100 | CCPW | GAXS | CARGS }, +#endif /* TCL */ + /*---*/ + { 0, NULL, NULL, 0 }, +}; + +#define __define_strng 4 +#define __define_print 3 +#define __struct_acces 2 +#define __struct_print 1 + +int main(int argc, char **argv) +{ + FILE *of; + usercombo combo; + char tmp[100]; + char *pt,*tabs; + int i,j,wh; + int pass; + int ct; + int sl; + OnMsg v; + + pass = __define_strng; + ct = 0; + + printf("/""* This file is automatically generated from gencmd.c *""/\n"); + printf("#ifndef MCMD_H\n#define MCMD_H 1\n\n"); + + while(pass) + { + if (pass == __struct_print) + { + printf("LS const OnMsg mcmd[] =\n{\n"); + } + if (pass == __struct_acces) + { + printf("LS OnMsg_access acmd[] = \n{\n"); + } + for(i=0;pre_mcmd[i].name;i++) + { + pt = 0; + wh = 0; + for(j=0;pre_mcmd[j].name;j++) + { + if (pre_mcmd[j].pass != pass) + { + pt = pre_mcmd[j].name; + wh = j; + break; + } + } + for(j=0;pre_mcmd[j].name;j++) + { + if ((pre_mcmd[j].pass != pass) && (strcmp(pt,pre_mcmd[j].name) > 0)) + { + pt = pre_mcmd[j].name; + wh = j; + } + } + if (pass == __define_strng) + { + //printf("#define S_%s%s\t\"%s\"\n",pt,((strlen(pt) + 2) < 8) ? "\t" : "",pt); + } + if (pass == __define_print) + { + //printf("#define C_%s%s\tmcmd[%i].name\n",pt,((strlen(pt) + 2) < 8) ? "\t" : "",ct); + printf("BEG const char C_%s[]%s\tMDEF(\"%s\");\n",pt,((strlen(pt) + 3) < 8) ? "\t" : "",pt); + ct++; + } + if (pass == __struct_acces) + { + printf("\t%i,\t/""* %s *""/\n", + pre_mcmd[wh].flags & CLEVEL, + pt); + } + if (pass == __struct_print) + { + memset(&v,0,sizeof(v)); + + v.defaultaccess = pre_mcmd[wh].flags & CLEVEL; + /* + defaultaccess */ + v.dcc = (pre_mcmd[wh].flags & DCC) ? 1 : 0; + v.cc = (pre_mcmd[wh].flags & CC) ? 1 : 0; + v.pass = (pre_mcmd[wh].flags & PASS) ? 1 : 0; + v.args = (pre_mcmd[wh].flags & CARGS) ? 1 : 0; + v.nopub = (pre_mcmd[wh].flags & NOPUB) ? 1 : 0; + v.nocmd = (pre_mcmd[wh].flags & NOCMD) ? 1 : 0; + v.gaxs = (pre_mcmd[wh].flags & GAXS) ? 1 : 0; + v.caxs = (pre_mcmd[wh].flags & CAXS) ? 1 : 0; + v.redir = (pre_mcmd[wh].flags & REDIR) ? 1 : 0; + v.lbuf = (pre_mcmd[wh].flags & LBUF) ? 1 : 0; + v.cbang = (pre_mcmd[wh].flags & CBANG) ? 1 : 0; + v.acchan = (pre_mcmd[wh].flags & ACCHAN) ? 1 : 0; + + sprintf(tmp,"%3i,%2i,%2i,%2i,%2i,%2i,%2i,%2i,%2i,%2i,%2i,%2i,%2i", + v.defaultaccess, + v.dcc, + v.cc, + v.pass, + v.args, + v.nopub, + v.nocmd, + v.gaxs, + v.caxs, + v.redir, + v.lbuf, + v.cbang, + v.acchan + ); + + sl = strlen(pre_mcmd[wh].func) + 1; + tabs = "\t\t\t"; + + sl = (sl & ~7) / 8; + tabs += sl; + + printf( (pre_mcmd[wh].cmdarg) ? "{ C_%s,%s\t%s,%s%s\t, \"%s\"\t},\n" : "{ C_%s,%s\t%s,%s%s\t},\n", + pt, + ((strlen(pt) + 5) < 8) ? "\t" : "", + pre_mcmd[wh].func, + tabs, + tmp, + pre_mcmd[wh].cmdarg + ); + } + pre_mcmd[wh].pass = pass; + } + if (pass == __define_strng) + { + /* nothing */ + } + if (pass == __define_print) + { + printf("\n#ifdef MAIN_C\n\n"); + } + if (pass == __struct_print) + { + printf("{ NULL, }};\n\n"); + } + if (pass == __struct_acces) + { + printf("};\n\n"); + } + pass--; + } + printf("#define LOCALHOST_ULONG %lu\n",inet_addr("127.1")); + printf("#else /""* MAIN_C *""/\n\n"); + printf("extern OnMsg mcmd[];\n"); + printf("extern OnMsg_access acmd[];\n\n"); + printf("#endif /""* MAIN_C *""/\n\n"); + printf("#endif /""* MCMD_H *""/\n\n"); + + unlink("usercombo.h"); + of = fopen("usercombo.h","w"); + + fprintf(of,"/""* This file is automatically generated from gencmd.c *""/\n"); + +#ifdef BOTNET + combo.comboflags = 0; combo.x.noshare = 1; + fprintf(of,"#define COMBO_NOSHARE\t0x%x\n",combo.comboflags); + combo.comboflags = 0; combo.x.readonly = 1; + fprintf(of,"#define COMBO_READONLY\t0x%x\n",combo.comboflags); +#endif /* BOTNET */ + +#ifdef GREET + combo.comboflags = 0; combo.x.greetfile = 1; + fprintf(of,"#define COMBO_GREETFILE\t0x%x\n",combo.comboflags); + combo.comboflags = 0; combo.x.randline = 1; + fprintf(of,"#define COMBO_RANDLINE\t0x%x\n",combo.comboflags); +#endif /* GREET */ + +#ifdef BOUNCE + combo.comboflags = 0; combo.x.bounce = 1; + fprintf(of,"#define COMBO_BOUNCE\t0x%x\n",combo.comboflags); +#endif /* BOUNCE */ + + combo.comboflags = 0; combo.x.echo = 1; + fprintf(of,"#define COMBO_ECHO\t0x%x\n",combo.comboflags); + combo.comboflags = 0; combo.x.aop = 1; + fprintf(of,"#define COMBO_AOP\t0x%x\n",combo.comboflags); + combo.comboflags = 0; combo.x.avoice = 1; + fprintf(of,"#define COMBO_AVOICE\t0x%x\n",combo.comboflags); + + fclose(of); + return(0); +} diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..8ed9857 --- /dev/null +++ b/src/global.h @@ -0,0 +1,450 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef GLOBAL_H +#define GLOBAL_H 1 + +#ifdef MAIN_C + +#define MDEF(x) = x +#define BEG LS + +#else /* MAIN_C */ + +#define MDEF(x) +#define BEG extern + +#endif /* MAIN_C */ + +/* + * + */ + +#define DEFAULTCMDCHAR '-' +#define MECHUSERLOGIN "v3.energymech.net" + +BEG const char VERSION[] MDEF("3.0.99p3"); +BEG const char SRCDATE[] MDEF("July 24th, 2009"); +#ifdef __CYGWIN__ +BEG const char BOTCLASS[] MDEF("WinMech"); +#else /* ! CYGWIN */ +BEG const char BOTCLASS[] MDEF("EnergyMech"); +#endif /* CYGWIN */ +BEG const char BOTLOGIN[] MDEF("emech"); + +BEG const char NULLSTR[] MDEF(""); + +BEG const char ERR_CHAN[] MDEF("I'm not on %s"); +BEG const char ERR_FILEOPEN[] MDEF("Couldn't open the file %s"); +BEG const char ERR_INIT[] MDEF("init: Warning:"); +BEG const char ERR_NICK[] MDEF("Invalid nickname: %s"); +BEG const char ERR_NOCHANNELS[] MDEF("I'm not active on any channels"); +BEG const char ERR_NOTOPPED[] MDEF("I'm not opped on %s"); +BEG const char ERR_UNKNOWN_COMMAND[] MDEF("Squeeze me?"); + +BEG const char __SPYSTR_RAWIRC[] MDEF("rawirc"); +BEG const char __SPYSTR_MESSAGE[] MDEF("message"); +BEG const char __SPYSTR_STATUS[] MDEF("status"); +BEG const char __SPYSTR_BOTNET[] MDEF("botnet"); +#define SPYSTR_RAWIRC (char*)__SPYSTR_RAWIRC +#define SPYSTR_MESSAGE (char*)__SPYSTR_MESSAGE +#define SPYSTR_STATUS (char*)__SPYSTR_STATUS +#define SPYSTR_BOTNET (char*)__SPYSTR_BOTNET + +BEG const char STR_MECHRESET[] MDEF("MECHRESET="); + +BEG Mech *botlist MDEF(NULL); +BEG Mech *current; + +BEG char *executable; +BEG char *configfile MDEF(CFGFILE); +BEG char *mechresetenv MDEF(NULL); + +BEG time_t uptime MDEF(0); +BEG int do_exec MDEF(FALSE); /* call mech_exec on mechexit */ +BEG int makecore MDEF(FALSE); +BEG int respawn MDEF(0); +BEG int sigmaster MDEF(0); +BEG int ctimeout MDEF(30); /* proc var */ +BEG ino_t parent_inode; + +BEG KillSock *killsocks MDEF(NULL); + +BEG Server *serverlist MDEF(NULL); +BEG ServerGroup *servergrouplist MDEF(NULL); +BEG ServerGroup *currentservergroup MDEF(NULL); +BEG int servergroupid MDEF(0); +BEG int serverident MDEF(1); + +BEG char CurrentNick[NUHLEN]; +BEG Client *CurrentDCC MDEF(NULL); +BEG Chan *CurrentChan MDEF(NULL); +BEG User *CurrentUser MDEF(NULL); +BEG Shit *CurrentShit MDEF(NULL); +BEG const OnMsg *CurrentCmd MDEF(NULL); +BEG User *cfgUser MDEF(NULL); + +BEG User __internal_users[2]; +#define CoreUser (__internal_users[0]) +#define LocalBot (__internal_users[1]) + +/* + * generic output buffer, can be used as buffer in any `leaf' function + * (functions that do not call any other non-trivial functions) + */ +BEG char gsockdata[MAXLEN]; + +BEG char nick_buf[MAXHOSTLEN]; +BEG char nuh_buf[NUHLEN]; + +/* + * select() stuff. + */ + +BEG fd_set read_fds; +BEG fd_set write_fds; +BEG int hisock; +BEG int short_tv; + +/* + * current UNIX timestamp + */ + +BEG time_t now; + +/* + * defined features + */ + +#ifdef ALIAS + +BEG Alias *aliaslist MDEF(NULL); + +#endif /* ALIAS */ + +#ifdef UPTIME +BEG char *defaultuptimehost MDEF("uptime.energymech.net"); +#endif /* UPTIME */ + +#ifdef BOTNET + +BEG const char UNKNOWNATUNKNOWN[] MDEF("unknown@unknown"); +#define UNKNOWN (&UNKNOWNATUNKNOWN[8]) + +BEG BotNet *botnetlist MDEF(NULL); +BEG NetCfg *netcfglist MDEF(NULL); +BEG char *linkpass MDEF(NULL); /* proc var */ +BEG int linkport MDEF(0); /* proc var */ +BEG int autolink MDEF(0); /* proc var */ +BEG time_t last_autolink MDEF(0); +BEG NetCfg *autolink_cfg MDEF(NULL); +BEG int global_tick MDEF(0); + +#endif /* BOTNET */ + +#ifdef BOUNCE + +BEG ircLink *bnclist MDEF(NULL); +BEG int bounce_sock MDEF(-1); +BEG int bounce_port MDEF(0); /* proc var */ + +#endif /* BOUNCE */ + +#ifdef CTCP + +BEG time_t ctcp_slot[CTCP_SLOTS]; + +#endif /* CTCP */ + +#ifdef DEBUG + +BEG char debugbuf[MAXLEN]; +BEG char *debugfile MDEF(NULL); +BEG int dodebug MDEF(FALSE); +BEG int debug_fd MDEF(-1); +BEG int debug_on_exit MDEF(FALSE); +BEG aMEA *mrrec; +BEG void *mallocdoer; + +#endif /* DEBUG */ + +#ifdef NOTE + +BEG Note *notelist MDEF(NULL); + +#endif /* NOTE */ + +#ifdef SCRIPTING + +BEG Hook *hooklist MDEF(NULL); + +#endif /* SCRIPTING */ + +#ifdef REDIRECT + +LS struct +{ + char *to; + int method; +#ifdef BOTNET + int guid; +#endif /* BOTNET */ + +} redirect; + +#endif /* REDIRECT */ + +#ifdef SEEN + +BEG char *seenfile MDEF(NULL); /* proc var */ +BEG Seen *seenlist MDEF(NULL); + +#endif /* SEEN */ + +#ifdef TRIVIA + +BEG int triv_qdelay MDEF(30); /* proc var */ +BEG char *triv_qfile MDEF(NULL); /* proc var */ +BEG char triv_qchar MDEF('*'); /* proc var */ +BEG TrivScore *scorelist MDEF(NULL); + +#endif /* TRIVIA */ + +#ifdef UPTIME + +BEG int uptimeport MDEF(9969); /* proc var */ +BEG char *uptimehost MDEF(NULL); /* proc var */ +BEG char *uptimenick MDEF(NULL); /* proc var */ +BEG int uptimesock; +BEG ulong uptimeip MDEF((ulong)-1); +BEG ulong uptimecookie; +BEG ulong uptimeregnr MDEF(0); +BEG time_t uptimelast MDEF(0); + +#endif /* UPTIME */ + +#ifdef WEB + +BEG int websock MDEF(-1); +BEG int webport MDEF(0); + +#endif /* WEB */ + +#ifdef RAWDNS + +BEG dnsList *dnslist MDEF(NULL); +BEG dnsAuthority *dnsroot MDEF(NULL); +BEG struct in_addr ia_ns[MAX_NAMESERVERS]; +BEG struct in_addr ia_default; + +#ifdef SESSION +BEG Strp *dnsrootfiles MDEF(NULL); +#endif /* SESSION */ +#endif /* RAWDNS */ + +#ifndef I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS + +BEG int spawning_lamer MDEF(0); + +#endif /* I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS */ + +/* + * attrtab defines + */ +#define NUM 0x01 +#define NICK 0x02 +#define FIRST 0x04 +#define CRLF 0x08 + +#define FNICK (NICK|FIRST) +#define NNICK (NICK|NUM) + +#ifdef MAIN_C + +/* + * tolowertab blatantly ripped from ircu2.9.32 + */ +LS const uchar tolowertab[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 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 +}; + +/* + * be wary, this is not a normal upper-to-lower table... + */ +LS const uchar nickcmptab[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + ' ', 0x00, '"', '#', '$', '%', '&', 0x27, /* <-- observe! the '!' changed to 0x00 */ + '(', ')', '*', '+', ',', '-', '.', '/', + '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 +}; + +LS const uchar attrtab[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x07 */ + 0, 0, CRLF, 0, 0, CRLF, 0, 0, /* 0x08 - 0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x18 - 0x1F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x27 */ + 0, 0, 0, 0, 0, NICK, 0, 0, /* 0x28 - 0x2F */ + NNICK, NNICK, NNICK, NNICK, NNICK, NNICK, NNICK, NNICK, /* 0x30 - 0x37 */ + NNICK, NNICK, 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */ + 0, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x40 - 0x47 */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x48 - 0x4F */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x50 - 0x57 */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x58 - 0x5F */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x60 - 0x67 */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x68 - 0x6F */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, /* 0x70 - 0x77 */ + FNICK, FNICK, FNICK, FNICK, FNICK, FNICK, 0, 0, /* 0x78 - 0x7F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x87 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x88 - 0x8F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x97 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x98 - 0x9F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xA7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA8 - 0xAF */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xB7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB8 - 0xBF */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xC7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC8 - 0xCF */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xD7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD8 - 0xDF */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0 - 0xE7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE8 - 0xEF */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0 - 0xF7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF8 - 0xFF */ +}; + +/* + * user struct for the core client + */ +LS const Strp CMA = +{ + NULL, + "*" +}; + +/* + * client struct for the core client + */ +LS ShortClient CoreClient = +{ + NULL, /* next */ + (User*)&CoreUser, /* user */ + -1, /* socket */ + 0, /* flags */ + 0, /* inputcount */ + 0 /* lasttime */ +}; + +LS ShortChan CoreChan = +{ + NULL, + NULL +}; + +typedef struct coreServerGroup +{ + ServerGroup *next; + int servergroup; + char name[8]; +} coreServerGroup; + +LS coreServerGroup defaultServerGroup = +{ + NULL, /* next */ + 0, /* servergroup */ + "default" /* name */ +}; + +#else /* MAIN_C */ + +extern const uchar tolowertab[]; +extern const uchar nickcmptab[]; +extern const uchar attrtab[]; +extern const User xxCoreUser; +extern const User xxLocalBot; +extern ShortClient CoreClient; +extern ShortChan CoreChan; +extern ServerGroup defaultServerGroup; + +#endif /* MAIN_C */ + +#endif /* GLOBAL_H */ diff --git a/src/greet.c b/src/greet.c new file mode 100644 index 0000000..c8b657e --- /dev/null +++ b/src/greet.c @@ -0,0 +1,164 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define GREET_C +#include "config.h" + +#ifdef GREET + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * woo.. no args? we use CurrentChan, CurrentNick and CurrentUser. + */ +void greet(void) +{ + Strp *sp,**pp; + char linebuf[MSGLEN],readbuf[MSGLEN]; + char *str; + int fd,sz; + + pp = ¤t->sendq; + while(*pp) + pp = &(*pp)->next; + + if (CurrentUser->x.x.greetfile) + { + if ((fd = open(CurrentUser->greet,O_RDONLY)) < 0) + return; + + sz = sizeof(Strp) + 9 + strlen(CurrentNick); + + memset(readbuf,0,sizeof(readbuf)); + while(TRUE) + { + str = sockread(fd,readbuf,linebuf); + if (str) + { + *pp = sp = (Strp*)Calloc(sz + strlen(str)); + /* Calloc sets to zero sp->next = NULL; */ + pp = &sp->next; + sprintf(sp->p,"NOTICE %s :%s",CurrentNick,str); + } + else + if (errno != EAGAIN) + break; + } + + close(fd); + } + else + if (CurrentUser->x.x.randline) + { + if ((str = randstring(CurrentUser->greet))) + goto single_line; + return; + } + else + { + str = CurrentUser->greet; +single_line: + *pp = sp = (Strp*)Calloc(sizeof(Strp) + 13 + Strlen(CurrentChan->name,CurrentNick,str,NULL)); + sprintf(sp->p,"PRIVMSG %s :[%s] %s",CurrentChan->name,CurrentNick,str); + /* Calloc sets to zero sp->next = NULL; */ + } +} + +/* + * + * commands tied to the greeting feature + * + */ + +void do_greet(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + User *user; + char *handle; + int isfile; + + if ((handle = chop(&rest)) == NULL) + goto usage; + + if ((user = find_handle(handle)) == NULL) + { + to_user(from,TEXT_UNKNOWNUSER,handle); + return; + } + + if (!usercanmodify(from,user)) + { + to_user(from,TEXT_USEROWNSYOU,user->name); + return; + } + + isfile = FALSE; + + if (*rest == '@' || *rest == '%') + { + isfile = *rest; + rest++; + while(*rest == ' ') + rest++; + } + + if (*rest) + { + if (isfile) + { + if (!is_safepath(rest)) + goto usage; + } + + user->x.x.greetfile = (isfile == '@') ? TRUE : FALSE; + user->x.x.randline = (isfile == '%') ? TRUE : FALSE; + + set_mallocdoer(do_greet); + user->greet = Strdup(rest); + + to_user(from,"greeting for user %s has been set to: %s%s",user->name,user->greet, + (isfile == '@') ? " (file)" : ((isfile == '%') ? " (random line from file)" : "")); + current->ul_save++; + } + else + if (isfile) + { + goto usage; + } + else + if (user->greet) + { + Free((char**)&user->greet); + to_user(from,"greeting for user %s has been removed",user->name); + current->ul_save++; + } + return; +usage: + usage(from); /* usage for CurrentCmd->name */ +} + +#endif /* GREET */ diff --git a/src/h.h b/src/h.h new file mode 100644 index 0000000..e2d30e4 --- /dev/null +++ b/src/h.h @@ -0,0 +1,783 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef H_H +#define H_H 1 + +#define ischannel(x) (*x == '#') + +#define nullstr(x) (x) ? x : NULLSTR +#define nullbuf(x) (x && *x) ? x : NULLSTR + +#define chkhigh(x) if (x > hisock) { hisock = x; } + +#define COMMAND_ARGS char *from, char *to, char *rest, int cmdaccess + +#define STRCHR strchr +#define STREND(x) STRCHR(x,0) + +/* + * some default code for socket flags + */ +#ifdef ASSUME_SOCKOPTS + +#define unset_closeonexec(x) fcntl(x,F_SETFD,0); + +#else /* ASSUME_SOCKOPTS */ + +#define unset_closeonexec(x) fcntl(x,F_SETFD,(~FD_CLOEXEC) & fcntl(x,F_GETFD)); + +#endif /* ASSUME_SOCKOPTS */ + +/* + * Dont try this at home kids... + */ +#ifdef __ELF__ +#define __sect(x) __section__ (x) +#else +#define __sect(x) /* nothing */ +#endif +#ifdef __GNUC__ + +#define __vattr(x) __attribute__ (( x )) +#define __page(x) __attribute__ (( __sect(x) )) +#define __attr(x,y) __attribute__ (( __sect(x), y )) +#define __att2(x,y,z) __attribute__ (( __sect(x), y, z )) + +#else +#define __vattr(x) /* nothing */ +#define __page(x) /* nothing */ +#define __attr(x,y) /* nothing */ +#define __att2(x,y,z) /* nothing */ +#endif + +#if !defined(__profiling__) && defined(__i386__) +# define __regparm(x) regparm (x) +#else +# define __regparm(x) +#endif + +#define CORE_SEG ".text.a" +#define CFG1_SEG ".text.b" +#define CMD1_SEG ".text.c" +#define INIT_SEG ".text.d" +#define RARE_SEG ".text.e" +#define DBUG_SEG ".text.f" + +#ifdef DEBUG + +#define set_mallocdoer(x) mallocdoer = x; + +#define mechexit(x,y) \ +{ \ + if (debug_on_exit) \ + wrap_debug(); \ + if (do_exec) \ + mech_exec(); \ + y(x); \ +} + +#else /* not DEBUG */ + +#define set_mallocdoer(x) + +#define mechexit(x,y) \ +{ \ + if (do_exec) \ + mech_exec(); \ + y(x); \ +} + +#endif /* DEBUG */ + +LS Chan *find_channel_ac(char *) __attr(CORE_SEG, __regparm (1) ); +LS Chan *find_channel_ny(char *) __attr(CORE_SEG, __regparm (1) ); +LS ChanUser *find_chanuser(Chan *, const char *) __attr(CORE_SEG, __regparm (2) ); +LS Client *find_client(const char *) __page(CORE_SEG); +LS Mech *add_bot(int, char *) __page(CORE_SEG); +LS KickSay *find_kicksay(char *, char *) __page(CORE_SEG); +LS Server *add_server(char *, int, char *) __page(CFG1_SEG); +LS Server *find_server(int) __page(CORE_SEG); +LS ServerGroup *getservergroupid(int) __page(CMD1_SEG); +LS Shit *add_shit(char *, char *, char *, char *, int, int) __page(CMD1_SEG); +LS Shit *find_shit(const char *, const char *) __page(CORE_SEG); +LS Shit *get_shituser(char *, char *) __page(CORE_SEG); +LS User *add_user(char *, char *, int) __page(CFG1_SEG); +LS User *find_handle(char *) __page(CORE_SEG); +LS User *get_authuser(char *, char *) __page(CORE_SEG); +LS User *get_user(const char *, const char *) __page(CORE_SEG); +LS int get_authaccess(char *, char *) __page(CORE_SEG); +LS int get_protaction(Chan *, char *) __page(CORE_SEG); +LS int get_shitaction(const char *, const char *) __page(CORE_SEG); +LS int get_useraccess(char *, char *) __page(CORE_SEG); +LS int get_maxaccess(const char *) __page(CORE_SEG); + +LS int Strcasecmp(const char *, const char *) __att2(CORE_SEG, const, __regparm (2) ); +LS int Strcmp(const char *, const char *) __att2(CORE_SEG, const, __regparm (2) ); +LS char *Strcat(char *, const char *) __attr(CORE_SEG, __regparm (2) ); +LS char *Strchr(const char *, int) __att2(CORE_SEG, const, __regparm (2) ); +LS char *Strcpy(char *, const char *) __attr(CORE_SEG, __regparm (2) ); +LS char *Strdup(char *) __page(CORE_SEG); +LS void Strncpy(char *, const char *, int) __attr(CORE_SEG, __regparm (3) ); +LS char *chop(char **) __attr(CORE_SEG, __regparm (1) ); +LS int get_number(const char *) __page(CORE_SEG); +LS int nickcmp(const char *, const char *) __att2(CORE_SEG, const, __regparm (2) ); +LS char *nickcpy(char *, const char *) __attr(CORE_SEG, __regparm (2) ); + +LS char *cipher(char *) __page(CMD1_SEG); +LS char *cluster(char *) __page(CMD1_SEG); +LS char *find_nuh(char *) __page(CORE_SEG); +LS char *format_uh(char *, int) __page(CMD1_SEG); +LS char *get_channel(char *, char **) __attr(CMD1_SEG, __regparm (2) ); +LS char *get_channel2(char *, char **) __page(CMD1_SEG); +LS char *get_nuh(ChanUser *) __page(CORE_SEG); +LS char *get_token(char **, const char *) __page(CORE_SEG); +LS char *getuh(char *) __page(CORE_SEG); +LS char *idle2str(time_t, int) __page(CORE_SEG); +LS char *makepass(char *) __page(CMD1_SEG); +LS char *nick2uh(char *, char *) __page(CMD1_SEG); +LS char *randstring(char *) __page(CORE_SEG); +LS char *sockread(int, char *, char *) __page(CORE_SEG); +LS char *logtime(time_t) __page(CORE_SEG); +LS void table_buffer(const char *, ...) __attr(CMD1_SEG, format (printf, 1, 2) ); +LS void table_send(const char *, const int) __attr(CMD1_SEG, __regparm (2) ); +LS char *time2away(time_t) __page(CORE_SEG); +LS char *time2medium(time_t) __page(CORE_SEG); +LS char *time2small(time_t) __page(CMD1_SEG); +LS char *time2str(time_t) __page(CMD1_SEG); +LS char *tolowercat(char *dest, const char *src) __attr(CMD1_SEG, __regparm (2) ); + +/* + * socket.c + */ +LS ulong get_ip(const char *) __page(CORE_SEG); +LS int SockAccept(int) __page(CORE_SEG); +LS int SockConnect(char *, int, int) __page(CORE_SEG); +LS void SockFlags(int) __page(CORE_SEG); +LS int SockListener(int) __page(CORE_SEG); +LS int SockOpts(void) __page(CORE_SEG); + +LS int capslevel(char *) __page(CORE_SEG); +LS int check_mass(Chan *, ChanUser *, int) __page(CORE_SEG); +LS int compile_timer(HookTimer *, char *) __page(CORE_SEG); /* SCRIPTING */ +LS int conf_callback(char *) __page(INIT_SEG); /* INIT */ +LS int do_help_callback(char *) __page(CMD1_SEG); +LS int find_setting(char *) __page(CMD1_SEG); +LS int is_bot(const char *) __page(CORE_SEG); +LS int is_nick(const char *) __page(CORE_SEG); +LS int killsock(int) __page(CORE_SEG); +LS int access_needed(char *) __page(CMD1_SEG); +LS int mode_effect(Chan *, qMode *) __page(CORE_SEG); +LS int passmatch(char *, char *) __page(CMD1_SEG); +LS int randstring_count(char *) __page(CORE_SEG); +LS int randstring_getline(char *) __page(CORE_SEG); +LS int read_seenlist(void) __page(CFG1_SEG); +LS int read_seenlist_callback(char *) __page(CFG1_SEG); +LS int read_userlist(char *) __page(CFG1_SEG); +LS int read_userlist_callback(char *) __page(CFG1_SEG); +LS int reverse_mode(char *, Chan *, int, int) __page(CORE_SEG); +LS int to_file(int, const char *, ...) __attr(CORE_SEG, format (printf, 2, 3) ); +LS int try_server(Server *, char *) __page(CORE_SEG); +LS int usercanmodify(const char *, const User *) __attr(CORE_SEG, __regparm (2) ); +LS int write_seenlist(void) __page(CORE_SEG); +LS int write_session(void) __page(CORE_SEG); +LS int write_userlist(char *) __page(CORE_SEG); +LS void var_resolve_host(const struct Setting *) __page(CFG1_SEG); + +LS ulong stringhash(char *) __page(CORE_SEG); + +/* + * function.c + */ +LS void *Calloc(int) __attr(CORE_SEG, __regparm (1) ); +LS void Free(char **) __attr(CORE_SEG, __regparm (1) ); +LS int Strlen(const char *, ...) __page(CORE_SEG); +LS int Strlen2(const char *, const char *) __attr(CORE_SEG, __regparm (2) ); +LS int matches(const char *, const char *) __att2(CORE_SEG, const, __regparm (2) ); +LS int num_matches(const char *, const char *) __att2(CORE_SEG, const, __regparm (2) ); +LS int a2i(char *) __attr(CORE_SEG, __regparm (1) ); +LS int is_safepath(const char *) __page(CORE_SEG); + +LS void afmt(char *, const char *, const char *) __page(CMD1_SEG); +LS void aucheck(User *) __attr(CORE_SEG, __regparm (1) ); +LS void change_authnick(char *, char *) __page(CORE_SEG); +LS void change_pass(User *, char *) __page(CMD1_SEG); +LS void chan_modestr(Chan *, char *) __page(CMD1_SEG); +LS void channel_massmode(Chan *, char *, int, char, char) __page(CMD1_SEG); +LS void channel_massunban(Chan *, char *, time_t) __page(CMD1_SEG); +LS void check_idlekick(void) __page(CORE_SEG); +LS void check_kicksay(Chan *, ChanUser *, char *) __page(CORE_SEG); +LS void check_shit(void) __page(CORE_SEG); +LS void common_public(Chan *, char *, char *, char *) __page(CORE_SEG); +LS void connect_to_server(void) __page(CORE_SEG); +LS void copy_vars(UniVar *, UniVar *) __page(CMD1_SEG); +LS void ctcp_dcc(char *, char *, char *) __page(CORE_SEG); +LS void cycle_channel(Chan *) __page(CORE_SEG); +LS void dcc_banner(Client *) __page(CORE_SEG); +LS void dcc_chat(char *) __page(CMD1_SEG); +LS int dcc_only_command(char *) __page(CMD1_SEG); +LS void debug(char *, ...) __attr(CORE_SEG, format (printf, 1, 2) ); +LS void delete_auth(char *) __page(RARE_SEG); /* rare */ +LS void delete_ban(Chan *, char *) __page(CORE_SEG); +LS void delete_modemask(Chan *, char *, int) __page(CORE_SEG); +LS void delete_client(Client *) __page(CORE_SEG); +LS void delete_vars(UniVar *, int) __page(CMD1_SEG); +LS void deop_ban(Chan *, ChanUser *, char *) __page(CMD1_SEG); +LS void deop_screwban(Chan *, ChanUser *) __page(CMD1_SEG); +LS void deop_siteban(Chan *, ChanUser *) __page(CMD1_SEG); + +/* + * user.c + */ +LS void cfg_chan(char *) __page(CFG1_SEG); +LS void cfg_greet(char *) __page(CFG1_SEG); +LS void cfg_mask(char *) __page(CFG1_SEG); +LS void cfg_note(char *) __page(CFG1_SEG); +LS void cfg_opt(char *) __page(CFG1_SEG); +LS void cfg_pass(char *) __page(CFG1_SEG); +LS void cfg_shit(char *) __page(CFG1_SEG); +LS void cfg_user(char *) __page(CFG1_SEG); +void mirror_user(User *) __page(CORE_SEG); +void mirror_userlist(void) __page(CORE_SEG); +LS void addtouser(Strp **, const char *) __attr(CORE_SEG, __regparm (2) ); +LS int remfromuser(Strp **, const char *) __attr(CORE_SEG, __regparm (2) ); + +/* + * commands + */ +LS void do_8ball(COMMAND_ARGS) __page(CMD1_SEG); /* TOYBOX */ +LS void do_access(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_alias(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_auth(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_away(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_banlist(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_bigsay(COMMAND_ARGS) __page(CMD1_SEG); /* TOYBOX */ +LS void do_bye(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_cchan(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_chaccess(COMMAND_ARGS) __page(CMD1_SEG); /* DYNCMDACCESS */ +LS void do_channels(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_chat(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_clearshit(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_cmd(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_core(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_cserv(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_cycle(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_debug(COMMAND_ARGS) __page(DBUG_SEG); /* DEBUG */ +LS void do_die(COMMAND_ARGS) __page(RARE_SEG); /* rare */ +LS void do_do(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_echo(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_esay(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_forget(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_greet(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_help(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_idle(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_invite(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_irclusers(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_ircstats(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_ircuserhost(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_ircwhois(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_join(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_kick(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_kickban(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_kicksay(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_last(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_link(COMMAND_ARGS) __page(CMD1_SEG); /* BOTNET */ +LS void do_load(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_mode(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_msg(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_names(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_nick(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_note(COMMAND_ARGS) __page(CMD1_SEG); /* NOTE */ +LS void do_notify(COMMAND_ARGS) __page(CMD1_SEG); /* NOTIFY */ +LS void do_opdeopme(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_opvoice(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_part(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_passwd(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_ping_ctcp(COMMAND_ARGS) __page(CMD1_SEG); /* CTCP */ +LS void do_random_msg(COMMAND_ARGS) __page(CMD1_SEG); /* TOYBOX */ +LS void do_randtopic(COMMAND_ARGS) __page(CMD1_SEG); /* TOYBOX */ +LS void do_read(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_reset(COMMAND_ARGS) __page(RARE_SEG); /* rare */ +LS void do_rkicksay(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_rshit(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_rspy(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_save(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_sayme(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_seen(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_server(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_servergroup(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_set(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_setpass(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_shit(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_shitlist(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_showidle(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_showusers(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_shutdown(COMMAND_ARGS) __page(RARE_SEG); /* rare */ +LS void do_siteban(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_spy(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_time(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_topic(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_trivia(COMMAND_ARGS) __page(CMD1_SEG); /* TRIVIA */ +LS void do_unalias(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_unban(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_upsend(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_upontime(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_usage(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_user(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_userlist(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_version(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_wall(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_whom(COMMAND_ARGS) __page(CMD1_SEG); +/* + * end of commands + */ +LS void fix_config_line(char *) __attr(CORE_SEG, __regparm (1) ); +LS void greet(void) __page(CMD1_SEG); +LS void join_channel(char *, char *) __page(CORE_SEG); +LS void kill_all_bots(char *) __attr(RARE_SEG, __noreturn__ ); /* rare */ +LS int make_auth(const char *, const User *) __page(CORE_SEG); +LS Ban *make_ban(Ban **, char *, char *, time_t) __page(CORE_SEG); +LS void make_chanuser(char *, char *) __attr(CORE_SEG, __regparm (2) ); +LS void make_ireq(int, char *, char *) __page(CMD1_SEG); +LS void mass_action(Chan *, ChanUser *) __page(CORE_SEG); +LS void mech_exec(void) __attr(RARE_SEG, __noreturn__ ); /* rare */ +LS void on_action(char *, char *, char *) __page(CORE_SEG); +LS void on_ctcp(char *, char *, char *) __page(CORE_SEG); +LS void on_join(Chan *, char *) __page(CORE_SEG); +LS void on_kick(char *, char *) __page(CORE_SEG); +LS void on_mode(char *, char *, char *) __page(CORE_SEG); +LS void on_msg(char *, char *, char *) __page(CORE_SEG); +LS void on_nick(char *, char *) __page(CORE_SEG); +LS void parse_213(char *, char *) __page(CMD1_SEG); /* stats C */ +LS void parse_219(char *, char *) __page(CMD1_SEG); /* end of stats */ +LS void parse_251(char *, char *) __page(CORE_SEG); +LS void parse_252(char *, char *) __page(CORE_SEG); +LS void parse_253(char *, char *) __page(CORE_SEG); +LS void parse_254(char *, char *) __page(CORE_SEG); +LS void parse_255(char *, char *) __page(CORE_SEG); +LS void parse_301(char *, char *) __page(CORE_SEG); +LS void parse_311(char *, char *) __page(CORE_SEG); +LS void parse_312(char *, char *) __page(CORE_SEG); +LS void parse_313(char *, char *) __page(CORE_SEG); +LS void parse_315(char *, char *) __page(CORE_SEG); +LS void parse_317(char *, char *) __page(CORE_SEG); +LS void parse_318(char *, char *) __page(CORE_SEG); +LS void parse_319(char *, char *) __page(CORE_SEG); +LS void parse_324(char *, char *) __page(CORE_SEG); +LS void parse_352(char *, char *) __page(CORE_SEG); +LS void parse_367(char *, char *) __page(CORE_SEG); +LS void parse_376(char *, char *) __page(CORE_SEG); +LS void parse_401(char *, char *) __page(CMD1_SEG); /* no such nick/channel */ +LS void parse_433(char *, char *) __page(CORE_SEG); +LS void parse_451(char *, char *) __page(CORE_SEG); +LS void parse_471(char *, char *) __page(CORE_SEG); +LS void parse_473(char *, char *) __page(CORE_SEG); +LS void parse_dcc(Client *) __page(CORE_SEG); +LS void parse_error(char *, char *) __page(CORE_SEG); +LS void parse_invite(char *, char *) __page(CMD1_SEG); +LS void parse_join(char *, char *) __page(CORE_SEG); +LS void parse_mode(char *, char *) __page(CORE_SEG); +LS void parse_notice(char *, char *) __page(CORE_SEG); +LS void parse_part(char *, char *) __page(CORE_SEG); +LS void parse_ping(char *, char *) __page(CORE_SEG); +LS void parse_privmsg(char *, char *) __page(CORE_SEG); +LS void parse_quit(char *, char *) __page(CORE_SEG); +LS void parse_topic(char *, char *) __page(CMD1_SEG); +LS void parse_wallops(char *, char *) __page(CORE_SEG); +LS void parse_server_input(void) __page(CORE_SEG); +LS void parseline(char *) __page(CORE_SEG); +LS void partyline_broadcast(Client *, char *, char *) __page(CORE_SEG); +LS void print_help(char *, char *, int) __page(CMD1_SEG); +LS void process_dcc(void) __page(CORE_SEG); +LS void prot_action(Chan *, char *, ChanUser *, char *, ChanUser *) __page(CORE_SEG); +LS void purge_banlist(Chan *) __page(CORE_SEG); +LS void purge_chanusers(Chan *) __page(CORE_SEG); +LS void purge_shitlist(void) __page(RARE_SEG); /* rare */ +LS void purge_kicklist(void) __page(RARE_SEG); /* rare */ +LS void push_kicks(Chan *) __page(CORE_SEG); +LS void push_modes(Chan *, int) __page(CORE_SEG); +LS void readcfgfile(void) __page(INIT_SEG); /* INIT */ +LS void readline(int s, int (*callback)(char *)) __page(CORE_SEG); +LS void register_with_server(void) __page(CORE_SEG); +LS void remove_auth(Auth *) __page(CORE_SEG); +LS void remove_chan(Chan *) __page(CORE_SEG); +LS void remove_chanuser(Chan *, char *) __attr(CORE_SEG, __regparm (2) ); +LS void remove_kicksay(KickSay *) __page(CMD1_SEG); +LS void remove_ks(KillSock *) __page(CORE_SEG); +LS void remove_shit(Shit *) __page(CMD1_SEG); +LS void remove_user(User *) __page(CMD1_SEG); +LS void reset_userlink(User *, User *) __page(CMD1_SEG); +LS void reverse_topic(Chan *, char *, char *) __page(CORE_SEG); +LS void screwban_format(char *) __page(CMD1_SEG); +LS void send_global(const char *, const char *, ...) __attr(CORE_SEG, format (printf, 2, 3) ); +LS void send_kick(Chan *, const char *, const char *, ...) __attr(CORE_SEG, format (printf, 3, 4) ); +LS void send_mode(Chan *, int, int, char, char, void *) __page(CORE_SEG); +LS void send_pa(int, const char *, const char *, ...) __page(CORE_SEG); +LS void send_spy(const char *, const char *, ...) __attr(CORE_SEG, format (printf, 2, 3) ); +LS void send_uptime(int) __page(CORE_SEG); +LS void set_binarydefault(UniVar *) __page(CFG1_SEG); +LS void set_str_varc(Chan *, int, char *) __page(CFG1_SEG); +LS void setbotnick(Mech *, char *) __page(CORE_SEG); +LS void shit_action(Chan *, ChanUser *) __page(CORE_SEG); +LS void signoff(char *, char *) __page(RARE_SEG); /* rare */ +LS void spy_typecount(Mech *) __page(CMD1_SEG); +LS void to_server(char *, ...) __attr(CORE_SEG, format (printf, 1, 2) ); +LS void to_user(const char *, const char *, ...) __attr(CORE_SEG, format (printf, 2, 3) ); +LS void to_user_q(const char *, const char *, ...) __attr(CORE_SEG, format (printf, 2, 3) ); +LS void unchop(char *, char *) __attr(CORE_SEG, __regparm (2) ); +LS void unmode_chanuser(Chan *, ChanUser *) __page(CORE_SEG); +LS void update(SequenceTime *) __page(CORE_SEG); +LS void update_modes(Chan *) __page(CORE_SEG); +LS void usage(char *) __attr(CMD1_SEG, __regparm (1) ); +LS void usage_command(char *, const char *) __page(CMD1_SEG); +LS void user_sync(void) __page(CFG1_SEG); +LS void whom_printbot(char *, BotInfo *, char *) __page(CMD1_SEG); + +/* + * signals + */ +LS int sig_hup_callback(char *) __page(RARE_SEG); +LS void do_sighup(void) __page(CMD1_SEG); +LS void do_sigint(void) __page(RARE_SEG); +LS void do_sigusr1(void) __page(CMD1_SEG); +LS void sig_alrm(int) __page(RARE_SEG); +LS void sig_child(int) __page(RARE_SEG); +LS void sig_bus(int) __page(CMD1_SEG); +LS void sig_hup(int) __page(RARE_SEG); +LS void sig_int(int) __page(RARE_SEG); +LS void sig_pipe(int) __page(CORE_SEG); +LS void sig_segv(int) __page(RARE_SEG); +LS void sig_term(int) __page(RARE_SEG); +LS void sig_usr1(int) __page(CMD1_SEG); +LS void sig_usr2(int) __page(DBUG_SEG); /* DEBUG */ +LS void sig_suicide() __attr(RARE_SEG, __noreturn__); + +/* + * BOTNET prototypes + */ +#ifdef BOTNET + +LS BotInfo *make_botinfo(int, int, char *, char *, char *) __page(CORE_SEG); +LS ChanUser *find_chanbot(Chan *, char *) __page(CORE_SEG); +LS Mech *get_netbot(void) __page(CORE_SEG); +LS NetCfg *find_netcfg(int) __page(CORE_SEG); +LS int connect_to_bot(NetCfg *cfg) __page(CORE_SEG); +LS void basicAuth(BotNet *, char *) __page(CORE_SEG); +LS void basicAuthOK(BotNet *, char *) __page(CORE_SEG); +LS void basicBanner(BotNet *, char *) __page(CORE_SEG); +LS void basicLink(BotNet *, char *) __page(CORE_SEG); +LS void basicQuit(BotNet *, char *) __page(CORE_SEG); +LS void netchanNeedop(BotNet *, char *) __page(CORE_SEG); +LS void partyAuth(BotNet *, char *) __page(CORE_SEG); +LS void partyCommand(BotNet *, char *) __page(CORE_SEG); +LS void partyMessage(BotNet *, char *) __page(CORE_SEG); +LS void ushareDelete(BotNet *, char *) __page(CMD1_SEG); +LS void ushareTick(BotNet *, char *) __page(CORE_SEG); +LS void ushareUser(BotNet *, char *) __page(CORE_SEG); +LS void botnet_binfo_relay(BotNet *, BotInfo *) __attr(CORE_SEG, __regparm (2) ); +LS void botnet_binfo_tofile(int, BotInfo *) __attr(CORE_SEG, __regparm (2) ); +LS void botnet_deaduplink(BotNet *) __page(CORE_SEG); +LS void botnet_dumplinklist(BotNet *) __page(CORE_SEG); +LS void botnet_newsock(void) __page(CORE_SEG); +LS void botnet_parse(BotNet *, char *) __page(CORE_SEG); +LS void botnet_refreshbotinfo(void) __page(CORE_SEG); +LS void botnet_relay(BotNet *, char *, ...) __page(CORE_SEG); +LS void check_botinfo(BotInfo *, const char *) __page(CORE_SEG); +LS void check_botjoin(Chan *, ChanUser *) __page(CORE_SEG); +LS void select_botnet(void) __page(CORE_SEG); +LS void process_botnet(void) __page(CORE_SEG); +LS void reset_linkable(int) __page(CORE_SEG); + +#endif /* BOTNET */ + +/* + * bounce baby bounce! + */ +#ifdef BOUNCE + +LS void new_port_bounce(const struct Setting *) __page(RARE_SEG); +LS void bounce_cleanup(void) __page(RARE_SEG); /* rare */ +LS void bounce_parse(ircLink *, char *) __page(CORE_SEG); +LS void process_bounce(void) __page(CORE_SEG); +LS void select_bounce(void) __page(CORE_SEG); + +#endif /* BOUNCE */ + +/* + * + */ +#ifdef CHANBAN + +LS void do_chanban(COMMAND_ARGS) __page(CMD1_SEG); +LS void process_chanbans(void) __page(CORE_SEG); + +#endif /* CHANBAN */ + +/* + * + */ +#ifdef CTCP + +LS void ctcp_finger(char *, char *, char *) __page(CORE_SEG); +LS void ctcp_ping(char *, char *, char *) __page(CORE_SEG); +LS void ctcp_version(char *, char *, char *) __page(CORE_SEG); + +#endif /* CTCP */ + +/* + * + */ +#ifdef DEBUG + +LS char *atime(time_t) __page(DBUG_SEG); +LS const char *proc_lookup(void *, int) __page(DBUG_SEG); +LS const char *strdef(const DEFstruct *, int) __page(DBUG_SEG); +LS void run_debug(void) __page(DBUG_SEG); +LS int wrap_debug(void) __page(DBUG_SEG); +LS void debug_botinfo(BotInfo *) __page(DBUG_SEG); +LS void debug_botnet(void) __page(DBUG_SEG); +LS void debug_core(void) __page(DBUG_SEG); +LS void debug_memory(void) __page(DBUG_SEG); +LS void debug_server(Server *, char *) __page(DBUG_SEG); +LS void debug_settings(UniVar *, int) __page(DBUG_SEG); +LS void memreset(void) __page(DBUG_SEG); +LS void memtouch(void *) __page(DBUG_SEG); +LS char *ulong2bin(int, ulong) __page(DBUG_SEG); + +#endif /* DEBUG */ + +#ifdef DCC_FILE + +LS int dcc_sendfile(char *, char *) __page(CMD1_SEG); +LS void do_send(COMMAND_ARGS) __page(CMD1_SEG); + +#endif /* DCC_FILE */ + +/* + * + */ +#ifdef DYNAMODE + +LS void check_dynamode(Chan *) __page(CORE_SEG); + +#endif /* DYNAMODE */ + +/* + * + */ +#ifdef IDWRAP + +LS void unlink_identfile(void) __page(CORE_SEG); + +#endif /* IDWRAP */ + +/* + * + */ +#ifdef NOTE + +LS int catch_note(char *, char *, char *) __page(CMD1_SEG); + +#endif /* NOTE */ + +/* + * + */ +#ifdef NOTIFY + +LS int mask_check(Notify *, char *) __page(CORE_SEG); +LS int notify_callback(char *) __page(CMD1_SEG); +LS int notifylog_callback(char *) __page(CFG1_SEG); +LS int read_notify(char *) __page(CFG1_SEG); +LS void catch_ison(char *) __page(CORE_SEG); +LS void catch_whois(char *, char *, char *) __page(CORE_SEG); +LS void nfshow_brief(Notify *) __page(CMD1_SEG); +LS void nfshow_full(Notify *) __page(CMD1_SEG); +LS void parse_303(char *, char *) __page(CORE_SEG); +LS void purge_notify(void) __page(CFG1_SEG); +LS void read_notifylog(void) __page(CFG1_SEG); +LS void send_ison(void) __page(CORE_SEG); +LS void write_notifylog(void) __page(CORE_SEG); + +#endif /* NOTIFY */ + +/* + * Python + */ +#ifdef PYTHON +#if defined(DEBUG_C) || defined(MAIN_C) || defined(PYTHON_C) + +LS void do_python(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_pythonscript(COMMAND_ARGS) __page(CMD1_SEG); +LS int python_parse_jump(char *, char *, Hook *) __page(CORE_SEG); +LS int python_timer_jump(Hook *) __page(CORE_SEG); +LS void python_dcc_complete(Client *, int) __page(CORE_SEG); +LS PyObject *python_hook(PyObject *, PyObject *, PyObject *) __page(CMD1_SEG); +LS PyObject *python_unhook(PyObject *, PyObject *, PyObject *) __page(CMD1_SEG); +LS PyObject *python_to_server(PyObject *, PyObject *, PyObject *) __page(CORE_SEG); +LS PyObject *python_to_file(PyObject *, PyObject *, PyObject *) __page(CORE_SEG); +LS PyObject *python_userlevel(PyObject *, PyObject *, PyObject *) __page(CORE_SEG); +LS PyObject *python_debug(PyObject *, PyObject *) __page(DBUG_SEG); +LS void init_python(void) __page(CFG1_SEG); +LS void free_python(void) __page(CFG1_SEG); + +#endif /* defined */ +#endif /* PYTHON */ + +/* + * + */ +#ifdef RAWDNS + +LS void do_dns(COMMAND_ARGS) __page(CMD1_SEG); +LS void init_rawdns(void) __page(INIT_SEG); /* INIT */ +LS void process_rawdns(void) __page(CORE_SEG); +LS void rawdns(const char *) __page(CORE_SEG); +LS void select_rawdns(void) __page(CORE_SEG); +LS char *poll_rawdns(char *) __page(CORE_SEG); +LS void parse_query(int, dnsQuery *) __page(CORE_SEG); +LS void do_dnsserver(COMMAND_ARGS) __page(CMD1_SEG); +LS void do_dnsroot(COMMAND_ARGS) __page(CMD1_SEG); +LS int read_dnsroot(char *) __page(CFG1_SEG); +LS ulong rawdns_get_ip(const char *) __page(CORE_SEG); + +#endif /* RAWDNS */ + +/* + * fun with pipes (aka, REDIRECTS) + */ +#ifdef REDIRECT + +LS int begin_redirect(char *, char *) __page(CORE_SEG); +LS void send_redirect(char *) __page(CMD1_SEG); +LS void end_redirect(void) __page(CORE_SEG); + +#endif /* REDIRECT */ + +/* + * RESET recover is not a defined feature? + */ +LS char *recover_client(char *) __page(INIT_SEG); /* INIT */ +LS char *recover_debug(char *) __page(INIT_SEG); /* INIT */ +LS char *recover_server(char *) __page(INIT_SEG); /* INIT */ +LS void recover_reset(void) __page(INIT_SEG); /* INIT */ + +/* + * + */ +#ifdef SEEN + +LS void make_seen(char *, char *, char *, char *, time_t, int) __page(CORE_SEG); /* SEEN */ + +#endif /* SEEN */ + +/* + * + */ +#ifdef STATS + +LS void stats_loghour(Chan *, char *, int) __page(CORE_SEG); +LS void stats_plusminususer(Chan *, int) __page(CORE_SEG); +LS void do_info(COMMAND_ARGS) __page(CMD1_SEG); + +#endif /* STATS */ + +/* + * Scripts-R-Us + */ +#ifdef TCL + +LS void do_tcl(COMMAND_ARGS) __page(CMD1_SEG); /* TCL + PLEASE_HACK_MY_SHELL */ +LS char *tcl_var_read() __page(CORE_SEG); +LS char *tcl_var_write() __page(CORE_SEG); +LS int tcl_parse_jump() __page(CORE_SEG); +LS int tcl_timer_jump() __page(CORE_SEG); +LS void tcl_dcc_complete(Client *, int) __page(CORE_SEG); +LS int tcl_hook() __page(CMD1_SEG); +LS int tcl_unhook() __page(CMD1_SEG); +LS int tcl_to_server() __page(CORE_SEG); +LS int tcl_to_file() __page(CORE_SEG); +LS int tcl_userlevel() __page(CORE_SEG); +LS int tcl_debug() __page(DBUG_SEG); +LS void init_tcl(void) __page(CFG1_SEG); + +#endif /* TCL */ + +#ifdef TELNET + +int check_telnet(int, char *) __page(CMD1_SEG); +void check_telnet_pass(Client *, char *) __page(CMD1_SEG); + +#endif /* TELNET */ + +#ifdef TOYBOX + +LS int read_bigcharset(char *) __page(CMD1_SEG); +LS int read_bigcharset_callback(char *) __page(CMD1_SEG); + +#endif /* TOYBOX */ + +#ifdef TRIVIA + +LS char *random_question(char *) __page(CORE_SEG); +LS int trivia_score_callback(char *) __page(CMD1_SEG); +LS void hint_one(void) __page(CMD1_SEG); +LS void hint_three(void) __page(CMD1_SEG); +LS void hint_two(void) __page(CMD1_SEG); +LS void read_triviascore(void) __page(CMD1_SEG); +LS void trivia_check(Chan *, char *) __page(CORE_SEG); +LS void trivia_cleanup(void) __page(CORE_SEG); +LS void trivia_no_answer(void) __page(CORE_SEG); +LS void trivia_question(void) __page(CORE_SEG); +LS void trivia_tick(void) __page(CORE_SEG); +LS void trivia_week_toppers(void) __page(CORE_SEG); +LS void write_triviascore(void) __page(CMD1_SEG); + +#endif /* TRIVIA */ + +/* + * UPTIME prototypes + */ +#ifdef UPTIME + +LS void init_uptime(void) __page(INIT_SEG); /* INIT */ +LS void process_uptime(void) __page(CORE_SEG); +LS void uptime_death(int) __page(RARE_SEG); /* rare */ + +#endif /* UPTIME */ + +/* + * WEB prototypes + */ +#ifdef WEB + +LS char *webread(int, char *, char *) __page(CORE_SEG); +LS void eml_fmt(WebSock *, char *) __page(CORE_SEG); +LS void select_web(void) __page(CORE_SEG); +LS void parse(WebSock *, char *) __page(CORE_SEG); +LS void process_web(void) __page(CORE_SEG); +LS void web_404(WebSock *, char *) __page(CORE_SEG); +LS void web_raw(WebSock *, char *) __page(CORE_SEG); +LS void web_botstatus(WebSock *, char *) __page(CORE_SEG); +LS void web_debug(WebSock *, char *) __page(DBUG_SEG); + +#endif /* WEB */ + +#endif /* H_H */ diff --git a/src/help.c b/src/help.c new file mode 100644 index 0000000..6de9791 --- /dev/null +++ b/src/help.c @@ -0,0 +1,262 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define HELP_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" +#include "usage.h" + +void print_help(char *from, char *line, int len) +{ + char *lp; + int tl; + + tl = TRUE; + if ((strlen(line) + len) > 70) + { + lp = line; + while(*lp) + { + if (*lp == '\037') + tl = !tl; + if (tl) + *lp = tolowertab[(uchar)*lp]; + lp++; + } + to_user(from,FMT_PLAIN,line); + *line = 0; + } +} + +int do_help_callback(char *line) +{ +#ifdef DEBUG + debug("(do_help_callback) `%s'\n",line); +#endif /* DEBUG */ + table_buffer(FMT_PLAIN,line); + return(FALSE); +} + +void do_help(COMMAND_ARGS) +{ + char line[MSGLEN]; + char *pt; + int i,level,axs; + int cur,nxt,count,ci,tl; + int in; + + if (dcc_only_command(from)) + return; + + axs = get_maxaccess(from); + + if (!*rest) + { + cur = -1; + *line = 0; +help_loop: + count = 0; + nxt = OWNERLEVEL; + + for(i=0;mcmd[i].name;i++) + { + tl = acmd[i]; + if ((tl < nxt) && (tl > cur)) + nxt = tl; + if (tl != cur) + continue; + if (count == 0) + sprintf(line,"\037Level %3i\037: ",cur); + count++; + } + if (!count) + { + cur = nxt; + goto help_loop; + } + ci = count; + for(i=0;mcmd[i].name;i++) + { + tl = acmd[i]; + if (tl != cur) + continue; + if (ci != count) + Strcat(line,", "); + print_help(from,line,strlen(mcmd[i].name)); + if (*line == 0) + Strcpy(line," "); + Strcat(line,(char*)mcmd[i].name); + count--; + } + print_help(from,line,500); + if ((cur != OWNERLEVEL) && (nxt <= axs)) + cur = nxt; + else + return; + goto help_loop; + } + + level = a2i(rest); + if (!errno) + { + if ((level > axs) || (level < 0)) + level = axs; + to_user(from,"\037Commands available at Level %i:\037",level); + *line = 0; + ci = 0; + for(i=0;mcmd[i].name;i++) + { + if (acmd[i] <= level) + { + if (ci != 0) + Strcat(line,", "); + ci++; + print_help(from,line,strlen(mcmd[i].name)); + Strcat(line,(char*)mcmd[i].name); + } + } + if (ci) + print_help(from,line,58); + else + to_user(from,TEXT_NONE); + return; + } + + if (STRCHR(rest,'*')) + { + line[0] = 0; + ci = 0; + to_user(from,"\037Commands that match query %s\037:",rest); + for(i=0;mcmd[i].name;i++) + { + if ((!matches(rest,(char*)mcmd[i].name)) && (acmd[i] <= axs)) + { + if (ci != 0) + Strcat(line,", "); + ci++; + print_help(from,line,strlen(mcmd[i].name)); + Strcat(line,(char*)mcmd[i].name); + } + } + if (ci) + print_help(from,line,500); + else + to_user(from,TEXT_NONE); + return; + } + + /* + * We dont want to show help for "../../../../../../etc/passwd" + */ + if (!is_safepath(rest)) + return; + + pt = Strcpy(line,HELPDIR); + for(i=0;(rest[i]);i++) + { + if (rest[i] >= 'a' && rest[i] <= 'z') + *pt = rest[i] - 0x20; + else + *pt = rest[i]; + pt++; + } + *pt = 0; +#ifdef DEBUG + debug("(do_help) help file check: %s\n",line); +#endif /* DEBUG */ + if ((in = open(line,O_RDONLY)) < 0) + { + to_user(from,"No help found for \"%s\"",rest); + return; + } + +#ifdef DEBUG + debug("(do_help) helpfile for = '%s'\n",rest); +#endif /* DEBUG */ + + table_buffer("\037Help on %s\037",rest); + level = access_needed(rest); + if (level > 200) + table_buffer("Level needed: Command disabled"); + else + if (level > 0) + table_buffer("Level needed: %i",level); + for(i=0;ulist[i].command;i++) + { + if (!Strcasecmp(rest,ulist[i].command)) + { + pt = (ulist[i].usage) ? ulist[i].usage : ""; + table_buffer("Usage: %s %s",ulist[i].command,pt); + break; + } + } + readline(in,&do_help_callback); /* readline closes in */ + table_send(from,0); +} + +void usage_command(char *to, const char *arg) +{ + char *pt; + int i; + + for(i=0;ulist[i].command;i++) + { + if (!Strcasecmp(arg,ulist[i].command)) + { + pt = ulist[i].usage; + to_user_q(to,(pt) ? "Usage: %s %s" : "Usage: %s",ulist[i].command,pt); + return; + } + } + to_user(to,"Usage: (missing)"); +} + +__attr(CMD1_SEG,__regparm(1)) +void usage(char *to) +{ + CurrentChan = NULL; + usage_command(to,CurrentCmd->name); +} + +void do_usage(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *cmd; + int i; + + cmd = chop(&rest); + for(i=0;mcmd[i].name;i++) + { + if (!Strcasecmp(cmd,mcmd[i].name)) + { + usage_command(from,mcmd[i].name); + return; + } + } + to_user_q(from,"Unknown command: %s",cmd); +} diff --git a/src/irc.c b/src/irc.c new file mode 100644 index 0000000..99226d9 --- /dev/null +++ b/src/irc.c @@ -0,0 +1,137 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define IRC_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * nick can be NULL + */ +void make_ireq(int t, char *from, char *nick) +{ + IReq *ir; + char *pt; + + set_mallocdoer(make_ireq); + ir = (IReq*)Calloc(sizeof(IReq) + Strlen2(from,nick)); + + ir->t = t; + ir->when = now; + + pt = Strcat(ir->from,from) + 1; + if (nick) + { + ir->nick = pt; + Strcpy(ir->nick,nick); + } + + ir->next = current->parselist; + current->parselist = ir; +} + +void send_pa(int type, const char *nick, const char *format, ...) +{ + char text[MAXLEN]; + va_list vargs; + IReq *ir,**pp; + int pr,end; + + pr = 0; + end = type & PA_END; + type = type & PA_TYPE; + + for(pp=¤t->parselist;(ir = *pp);) + { +#ifdef RAWDNS + if (ir->t == PA_DNS && !Strcasecmp(nick,ir->nick)) + { +#ifdef DEBUG + debug("(send_pa) PA_DNS %s\n",ir->nick); + ir->nick = (char *)nick; +#endif /* DEBUG */ + } +#endif /* RAWDNS */ + if (ir->t == type && (!nick || !nickcmp(nick,ir->nick))) + { + if (format) + { + if (!pr++) + { + va_start(vargs,format); + vsprintf(text,format,vargs); + va_end(vargs); + } + to_user(ir->from,FMT_PLAIN,text); + } + if (end) + { + *pp = ir->next; + Free((char**)&ir); + continue; + } + } + pp=&ir->next; + } +} + +/* + * + * commands that parse irc replies + * + */ + +void do_irclusers(COMMAND_ARGS) +{ + to_server("LUSERS\n"); + make_ireq(PA_LUSERS,from,NULL); +} + +void do_ircstats(COMMAND_ARGS) +{ + /* + * on_msg checks: DCC + CARGS + */ + char *line,*serv; + + line = chop(&rest); + serv = chop(&rest); + + to_server((serv) ? "STATS %s %s\n" : "STATS %s\n",line,serv); + make_ireq(PA_STATS,from,NULL); +} + +void do_ircwhois(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *nick; + + nick = chop(&rest); + to_server("WHOIS %s\n",nick); + make_ireq((CurrentCmd->name == C_WHOIS) ? PA_WHOIS : PA_USERHOST,from,nick); +} diff --git a/src/kicksay.c b/src/kicksay.c new file mode 100644 index 0000000..e956607 --- /dev/null +++ b/src/kicksay.c @@ -0,0 +1,265 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define KICKSAY_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +KickSay *find_kicksay(char *text, char *channel) +{ + KickSay *kick,*save; + int num,best; + + save = NULL; + best = 0; + for(kick=current->kicklist;kick;kick=kick->next) + { + if (!channel || *kick->chan == '*' || !Strcasecmp(channel,kick->chan)) + { + num = num_matches(kick->mask,text); + if (num > best) + { + best = num; + save = kick; + } + } + } + return(save); +} + +void check_kicksay(Chan *chan, ChanUser *doer, char *text) +{ + KickSay *kick,*save; + char *mask; + int action; + + save = NULL; + action = -1; + for(kick=current->kicklist;kick;kick=kick->next) + { + if (*kick->chan == '*' || !Strcasecmp(chan->name,kick->chan)) + { + if (!matches(kick->mask,text)) + { + if (kick->action > action) + { + action = kick->action; + save = kick; + } + } + } + } + if (save) + { + if (!action) + { + if (doer->flags & CU_KSWARN) + action = 1; + if (!(doer->flags & CU_KSWARN)) + { + doer->flags |= CU_KSWARN; + to_server("NOTICE %s :%s\n",doer->nick,save->reason); + } + } + if (action > 1) + { + mask = format_uh(get_nuh(doer),FUH_USERHOST); + if (action > 2) + { + add_shit("Auto KS",chan->name,mask,save->reason,2,now+3600); + } + if (!(doer->flags & CU_BANNED)) + { + doer->flags |= CU_BANNED; + send_mode(chan,90,QM_RAWMODE,'+','b',mask); + } + } + if (action && !(doer->flags & CU_KICKED)) + { + doer->flags |= CU_KICKED; + send_kick(chan,CurrentNick,FMT_PLAIN,save->reason); + } + } +} + +void remove_kicksay(KickSay *kick) +{ + KickSay **pp; + + pp = ¤t->kicklist; + while(*pp) + { + if (*pp == kick) + { + *pp = kick->next; + Free((char**)&kick); + return; + } + pp = &(*pp)->next; + } +} + +void purge_kicklist(void) +{ + while(current->kicklist) + remove_kicksay(current->kicklist); +} + +/* + * + * kicksay commands + * + */ + +#ifdef NEWBIE +char *ks_actions[MAX_KS_LEVEL+1] = { "warn (0)","kick (1)","kickban (2)","autoshit (3)" }; +#else +char *ks_actions[MAX_KS_LEVEL+1] = { "warn","kick","kickban","autoshit" }; +#endif /* NEWBIE */ + +void do_kicksay(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + KickSay *kick; + char *channel,*mask; + int inum; + + channel = chop(&rest); + if (!channel) + { + if (!current->kicklist) + { + to_user(from,"Kicksay list is empty"); + return; + } + + if (dcc_only_command(from)) + return; + + table_buffer("\037channel\037\t\037action\037\t\037string\037\t\037kick reason\037"); + for(kick=current->kicklist;kick;kick=kick->next) + { + table_buffer("%s\t%s\t%s\t%s",kick->chan,ks_actions[kick->action],kick->mask,kick->reason); + } + table_send(from,2); + return; + } + + if (ischannel(channel) || *channel == '*') + { + + if (get_useraccess(from,channel) < cmdaccess) + return; + + inum = DEFAULT_KS_LEVEL; + if (*rest != '"') + { + inum = a2i(chop(&rest)); + if (errno || inum < 0 || inum > MAX_KS_LEVEL) + return; + } + + mask = get_token(&rest,"\""); + + if (!mask || !*mask) + goto usage; + + /* + * check for previously existing kicks + */ + if ((kick = find_kicksay(mask,channel)) != NULL) + { + to_user(from,"I'm already kicking on \"%s\"",kick->mask); + return; + } + + /* + * dig out the reason (the rest) + */ + while(rest && *rest == ' ') + rest++; + if (!*rest) + goto usage; + + /* + * add it to the list + */ + set_mallocdoer(do_kicksay); + kick = (KickSay*)Calloc(sizeof(KickSay) + Strlen(channel,mask,rest,NULL)); + + kick->next = current->kicklist; + current->kicklist = kick; + kick->action = inum; + + if (!matches("\\*?*\\*",mask)) + kick->chan = Strcpy(kick->mask,mask) + 1; + else + { + kick->mask[0] = '*'; + Strcpy(kick->mask+1,mask); + kick->chan = Strcat(kick->mask,MATCH_ALL) + 1; + } + kick->reason = Strcpy(kick->chan,channel) + 1; + Strcpy(kick->reason,rest); + + to_user(from,"Now kicking on \"%s\" on %s",mask,channel); + current->ul_save++; + return; + } +usage: + usage(from); /* usage for CurrentCmd->name */ +} + +void do_rkicksay(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + KickSay *kick; + char *channel; + + channel = chop(&rest); /* cant be NULL (CARGS) */ + if (!ischannel(channel) && *channel != '*') + goto usage; + + if (!*rest) + goto usage; + + if (!(kick = find_kicksay(rest,channel))) + { + to_user(from,"I'm not kicking on that"); + return; + } + to_user(from,"No longer kicking on %s",kick->mask); + remove_kicksay(kick); + current->ul_save++; + return; +usage: + usage(from); /* usage for CurrentCmd->name */ +} + diff --git a/src/ld/mech.ldscript b/src/ld/mech.ldscript new file mode 100644 index 0000000..e3106ce --- /dev/null +++ b/src/ld/mech.ldscript @@ -0,0 +1,105 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SEARCH_DIR(/lib); +SEARCH_DIR(/usr/lib); +SEARCH_DIR(/usr/local/lib); +SECTIONS +{ + . = 0x08048000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.text : { *(.rel.text) *(.rel.gnu.linkonce.t*) } + .rela.text : { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rel.data : { *(.rel.data) *(.rel.gnu.linkonce.d*) } + .rela.data : { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rel.rodata : { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } + .rela.rodata : { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0x9090 + .plt : { *(.plt) } + .text : + { + *(.text) + *(.text.d) /* INIT */ + *(.text.e) /* RARE */ + *(.text.c) /* CMD1 */ + *(.text.a) /* CORE */ + *(.text.f) /* DBUG */ + *(.text.b) /* CFG1 */ + *(.stub) + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x9090 + _etext = .; + PROVIDE (etext = .); + .fini : { *(.fini) } =0x9090 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + . = ALIGN(0x1000) + (. & (0x1000 - 1)); + .data : + { + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(32 / 8); + _end = . ; + PROVIDE (end = .); + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..0ac8355 --- /dev/null +++ b/src/main.c @@ -0,0 +1,1039 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define MAIN_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * we generally try to mess around as little as possible here + */ +void mech_exec(void) +{ + char newprog[4096]; /* we're bloating the stack, so what? */ + char *argv[5],*envp[2]; + int i; + + argv[1] = argv[2] = argv[3] = argv[4] = NULL; + + if (respawn) + { + sprintf(newprog,"%s %i",executable,respawn); + argv[0] = newprog; + } + else + { + argv[0] = executable; + } + + i = 1; + + if (makecore) + argv[i++] = "-c"; + +#ifdef DEBUG + if (debug_on_exit) + argv[i++] = "-X"; +#endif /* DEBUG */ + + envp[0] = mechresetenv; + envp[1] = NULL; + +#ifdef DEBUG + debug("execve( %s, argv = { %s %s %s %s %s }, envp = { %s } )\n", + nullstr(executable), + nullstr(argv[0]),nullstr(argv[1]),nullstr(argv[2]),nullstr(argv[3]),nullstr(argv[4]), + nullstr(envp[0])); +#endif /* DEBUG */ + +#ifdef __profiling__ +#ifdef SIGPROF + signal(SIGPROF,SIG_IGN); +#endif /* SIGPROF */ +#endif /* __profiling__ */ + + execve(executable,argv,envp); + +#ifdef DEBUG + debug("execve() failed!\n"); + if (debug_on_exit) + run_debug(); +#endif /* DEBUG */ + exit(1); +} + +LS int r_ct; +LS char r_str[MSGLEN]; + +int randstring_count(char *line) +{ + r_ct++; + return(FALSE); +} + +int randstring_getline(char *line) +{ + if (--r_ct == 0) + { + Strcpy(r_str,line); + return(TRUE); + } + return(FALSE); +} + +char *randstring(char *file) +{ + int in; + + if ((in = open(file,O_RDONLY)) < 0) + return(NULL); + + r_ct = 0; + readline(in,&randstring_count); /* readline closes in */ + + if ((in = open(file,O_RDONLY)) < 0) + return(NULL); + + r_ct = RANDOM(1,r_ct); + readline(in,&randstring_getline); /* readline closes in */ + + return(r_str); +} + +/* + * Signal handlers + * ~~~~~~~~~~~~~~~ + * SIGHUP Read and execute all commands in `mech.msg' file. + * SIGCHLD Take care of zombies + * SIGALRM Ignore ALRM signals + * SIGPIPE Ignore PIPE signals + * SIGINT Exit gracefully on ^C + * SIGBUS (Try to) Exit gracefully on bus faults + * SIGSEGV (Try to) Exit gracefully on segmentation violations + * SIGTERM Exit gracefully when killed + * SIGUSR1 Jump (a) bot to a new server + * SIGUSR2 Call run_debug() (dump `everything' to a debug file) + */ + +LS struct +{ + ulong sighup:1, + sigint:1, + sigusr1:1; + +} sched_sigs; + +int sig_hup_callback(char *line) +{ + on_msg((char*)CoreUser.name,current->nick,line); + return(FALSE); +} + +void do_sighup(void) +{ + int in; + + sched_sigs.sighup = FALSE; + + CurrentShit = NULL; + CurrentChan = NULL; + CurrentUser = (User*)&CoreUser; + CurrentDCC = (Client*)&CoreClient; + *CurrentNick = 0; + + if ((in = open(MSGFILE,O_RDONLY)) >= 0) + { + readline(in,&sig_hup_callback); /* readline closes in */ + unlink(MSGFILE); + } + + CurrentDCC = NULL; +} + +void sig_hup(int crap) +{ +#ifdef DEBUG + debug("(sighup)\n"); +#endif /* DEBUG */ + signal(SIGHUP,sig_hup); + /* + * Schedule the doing of greater things + */ + sched_sigs.sighup = TRUE; +} + +#ifndef __linux__ + +/* + * Figured it out the hard way: + * DONT PUT THE signal() CALL BEFORE THE ACTUAL wait()!! + * Fucked up SunOS will hang/sigsegv! + */ +void sig_child(int crap) +{ +#ifdef DEBUG + debug("(sig_child)\n"); +#endif /* DEBUG */ + + while(1) + { + if (waitpid(-1,NULL,WNOHANG) <= 0) + break; + } + signal(SIGCHLD,sig_child); +} + +void sig_alrm(int signum) +{ +#ifdef DEBUG + debug("(sigalrm)\n"); +#endif /* DEBUG */ + signal(SIGALRM,sig_alrm); +} + +void sig_pipe(int dummy) +{ +#ifdef DEBUG + debug("(sigpipe)\n"); +#endif /* DEBUG */ + signal(SIGPIPE,sig_pipe); +} + +#endif /* not __linux__ */ + +void do_sigusr1(void) +{ + sched_sigs.sigusr1 = FALSE; + + if (current->connect == CN_ONLINE) + to_server(TEXT_SIGUSR1); + else + { + if (current->sock != -1) + { +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ + close(current->sock); + } + current->sock = -1; + } +} + +/* + * SIGUSR1 -- reconnect first bot to a new server + */ +void sig_usr1(int crap) +{ +#ifdef DEBUG + debug("(sigusr1)\n"); +#endif /* DEBUG */ + + sched_sigs.sigusr1 = TRUE; + signal(SIGUSR1,sig_usr1); +} + +#ifdef DEBUG + +/* + * SIGUSR2 -- dump debug info + */ +void sig_usr2(int crap) +{ + time(&now); + + debug("(sigusr2)\n"); + signal(SIGUSR2,sig_usr2); + run_debug(); +} + +#endif /* DEBUG */ + +/* + * signals that cause suicide... + */ + +#ifdef UPTIME +#define UP_ARGS , int uptype +#define UP_CALL(x) , x +#else +#define UP_ARGS /* nothing */ +#define UP_CALL(x) /* nothing */ +#endif + +void sig_suicide(char *text UP_ARGS) +{ +#ifdef TRIVIA + /* + * trivia is global and is saved in the same way (and places) as session + */ + write_triviascore(); +#endif /* TRIVIA */ + +#ifdef SESSION + write_session(); +#endif /* SESSION */ + +#ifdef UPTIME + uptime_death(uptype); +#endif /* UPTIME */ + + kill_all_bots(text); + /* NOT REACHED */ +} + +void do_sigint(void) +{ + /* + * We dont care about resetting sched_sigs.sigint, + * we're committing suicide here! + */ + + sig_suicide(TEXT_SIGINT /* comma */ UP_CALL(UPTIME_SIGINT)); + /* NOT REACHED */ +} + +void sig_int(int signum) +{ + if (sched_sigs.sigint) + { + /* + * something locked up, SIGINT twice + */ +#ifdef DEBUG + debug("(sigint) (twice)\n"); +#endif /* DEBUG */ + do_sigint(); + /* NOT REACHED */ + } +#ifdef DEBUG + debug("(sigint)\n"); +#endif /* DEBUG */ + + sched_sigs.sigint = TRUE; + signal(SIGINT,sig_int); +} + +/* + * SIGBUS is a real killer and cant be scheduled. + */ +void sig_bus(int crap) +{ + time(&now); + + respawn++; + if (respawn > 10) + mechexit(1,exit); + +#ifdef DEBUG + debug("(sigbus)\n"); +#endif /* DEBUG */ + + do_exec = TRUE; + sig_suicide(TEXT_SIGBUS /* comma */ UP_CALL(UPTIME_SIGBUS)); + /* NOT REACHED */ +} + +/* + * SIGSEGV shows no mercy, cant schedule it. + */ +void sig_segv(int crap) +{ + time(&now); + + respawn++; + if (respawn > 10) + mechexit(1,exit); + +#ifdef DEBUG + debug("(sigsegv)\n"); + if (debug_on_exit) + { + run_debug(); + debug_on_exit = FALSE; + } +#endif /* DEBUG */ + + do_exec = TRUE; + sig_suicide(TEXT_SIGSEGV /* comma */ UP_CALL(UPTIME_SIGSEGV)); + /* NOT REACHED */ +} + +/* + * SIGTERM + */ +void sig_term(int signum) +{ +#ifdef __profiling__ + exit(0); +#endif /* __profiling__ */ + + time(&now); + +#ifdef DEBUG + debug("(sigterm)\n"); +#endif /* DEBUG */ + + sig_suicide(TEXT_SIGTERM /* comma */ UP_CALL(UPTIME_SIGTERM)); + /* NOT REACHED */ +} + +/* + * + * The main loop + * + */ + +#ifdef __GNUC__ +LS void doit(void) __attribute__ ((__noreturn__, __sect(CORE_SEG))); +#endif +void doit(void) +{ + struct timeval tv; + Chan *chan; + Client *client; + SequenceTime this; + Strp *qm; + time_t last_update; + + last_update = now; + + /* + * init update times so that they dont all run right away + */ + this.tenminute = now / 600; + this.hour = now / 3600; + + /* + * The Main Loop + */ +mainloop: + /* + * signal processing + */ + for(current=botlist;current;current=current->next) + { + if (current->guid == sigmaster) + break; + } + if (!current) + current = botlist; + if (sched_sigs.sighup) + do_sighup(); + if (sched_sigs.sigint) + do_sigint(); + if (sched_sigs.sigusr1) + do_sigusr1(); + + /* + * check for regular updates + */ + if (last_update != now) + { + last_update = now; + update(&this); + } + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + hisock = -1; + +#ifdef BOTNET + select_botnet(); +#endif /* BOTNET */ + +#ifdef WEB + select_web(); +#endif /* WEB */ + +#ifdef RAWDNS + select_rawdns(); +#endif /* RAWDNS */ + +#ifdef BOUNCE + select_bounce(); +#endif /* BOUNCE */ + + /* + * unset here, reset if needed in bot loop + */ + short_tv &= ~(TV_SERVCONNECT|TV_LINEBUF); + for(current=botlist;current;current=current->next) + { + if (current->sock == -1) + { +#ifdef RAWDNS + if (current->connect == CN_DNSLOOKUP) + { + Server *sp; + char *host = NULL; + + if ((sp = find_server(current->server))) + { + if ((now - current->conntry) > ctimeout) + { +#ifdef DEBUG + debug("(doit) RAWDNS timed out (%s)\n",sp->name); +#endif /* DEBUG */ + current->connect = CN_NOSOCK; + sp->err = SP_NO_DNS; + goto doit_jumptonext; + } + if ((host = poll_rawdns(sp->name))) + { + char hosttemp[strlen(host)+1]; + +#ifdef DEBUG + debug("(doit) rawdns: %s ==> %s\n",sp->name,host); +#endif /* DEBUG */ + Strcpy(hosttemp,host); + host = hosttemp; + try_server(sp,host); + } + } + } + else + { + if (current->connect == CN_SPINNING) + { + if ((now - current->conntry) >= 60) + connect_to_server(); + } + else + { +doit_jumptonext: + short_tv |= TV_SERVCONNECT; + if ((now - current->conntry) >= 2) + connect_to_server(); + } + } +#else /* ! RAWDNS */ + if (current->connect == CN_SPINNING) + { + if ((now - current->conntry) >= 60) + connect_to_server(); + } + else + { + short_tv |= TV_SERVCONNECT; + if ((now - current->conntry) >= 2) + connect_to_server(); + } +#endif /* RAWDNS */ + } + if (current->sock != -1) + { + if (current->ip.s_addr == 0) + { + struct sockaddr_in sai; + int sz; + + sz = sizeof(sai); + if (getsockname(current->sock,(struct sockaddr *)&sai,&sz) == 0) + current->ip.s_addr = sai.sin_addr.s_addr; + } + if ((current->connect == CN_TRYING) || (current->connect == CN_CONNECTED)) + { + short_tv |= TV_SERVCONNECT; + if ((now - current->conntry) > ctimeout) + { +#ifdef DEBUG + debug("(doit) {%i} Connection timed out\n",current->sock); +#endif /* DEBUG */ +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ + close(current->sock); + current->sock = -1; + goto restart_dcc; + } + if (current->connect == CN_TRYING) + FD_SET(current->sock,&write_fds); + } + if (current->sendq) + { + short_tv |= TV_LINEBUF; + } + else + { + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->kicklist || chan->modelist) + { + short_tv |= TV_LINEBUF; + break; + } + } + } + chkhigh(current->sock); + FD_SET(current->sock,&read_fds); + } + + /* + * Clean out DCC_DELETE clients + */ +restart_dcc: + for(client=current->clientlist;client;client=client->next) + { + if (client->flags == DCC_DELETE) + { + delete_client(client); + goto restart_dcc; + } + } + + for(client=current->clientlist;client;client=client->next) + { + if (client->flags & DCC_ASYNC) + { + chkhigh(client->sock); + FD_SET(client->sock,&write_fds); + } + if (client->sock != -1) + { + chkhigh(client->sock); + FD_SET(client->sock,&read_fds); + } + } + } + +#ifdef UPTIME + if (uptimesock >= 0) + chkhigh(uptimesock); +#endif /* UPTIME */ + + /* + * Longer delay saves CPU but some features require shorter delays + */ +#ifdef NOTIFY + tv.tv_sec = (short_tv) ? 1 : 5; +#else /* NOTIFY */ + tv.tv_sec = (short_tv) ? 1 : 30; +#endif /* NOTIFY */ + tv.tv_usec = 0; + + if ((select(hisock+1,&read_fds,&write_fds,0,&tv) == -1) && (errno == EINTR)) + goto mainloop; + + /* + * Update current time + */ + time(&now); + + for(current=botlist;current;current=current->next) + { + /* + * sendq_time can never be smaller than the current time + * it is important that the check is done before anything + * else that could potentially send output to the server! + */ + if (current->sendq_time < now) + current->sendq_time = now; + } + + for(current=botlist;current;current=current->next) + { + if (current->clientlist) + process_dcc(); + + if (current->sock != -1) + parse_server_input(); + + if (current->connect == CN_ONLINE) + { + if (current->setting[TOG_NOIDLE].int_var) + { + if ((now - current->lastantiidle) > PINGSENDINTERVAL) + { + to_server("PRIVMSG * :0\n"); + current->lastantiidle = now; + } + } + /* + * check for HIGH priority modes + */ + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->modelist && chan->bot_is_op) + push_modes(chan,8); + } + /* + * check for waiting kicks + */ + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->kicklist && chan->bot_is_op) + push_kicks(chan); + } + /* + * check for LOW priority modes + */ + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->modelist && chan->bot_is_op) + push_modes(chan,0); + } + /* + * the un-important sendq only sends when sendq_time <= now + */ + if ((current->sendq) && (current->sendq_time <= now)) + { + qm = current->sendq; + to_server(FMT_PLAINLINE,qm->p); + current->sendq = qm->next; + Free((char**)&qm); + } + } + } + + /* + * Check for do_die()'d bots... + */ +restart_die: + for(current=botlist;current;current=current->next) + { + if (current->connect == CN_BOTDIE) + { + signoff(current->from,current->signoff); + /* + * signoff touches the botlist, so we need to restart + */ + goto restart_die; + } + } + +#ifdef BOTNET + if (botnetlist) + process_botnet(); +#endif /* BOTNET */ + +#ifdef BOUNCE + process_bounce(); +#endif /* BOUNCE */ + +#ifdef CHANBAN + process_chanbans(); +#endif /* CHANBAN */ + +#ifdef RAWDNS + process_rawdns(); +#endif /* RAWDNS */ + +#ifdef UPTIME + process_uptime(); +#endif /* UPTIME */ + +#ifdef WEB + process_web(); +#endif /* WEB */ + +#ifdef TRIVIA + trivia_tick(); +#endif /* TRIVIA */ + + /* + * Check killsocks + */ + if (killsocks) + killsock(-1); + + goto mainloop; +} + +/* + * main(), we love it and cant live without it + */ + +LS char *bad_exe = "init: Error: Improper executable name\n"; + +#ifdef __GNUC__ +int main(int argc, char **argv, char **envp) __attribute__ ((__noreturn__, __sect(INIT_SEG))); +#endif +int main(int argc, char **argv, char **envp) +{ + char *opt; + int do_fork = TRUE; + int versiononly = FALSE; + + uptime = time(&now); + + if ((getuid() == 0) || (geteuid() == 0)) + { + to_file(1,"init: Do NOT run EnergyMech as root!\n"); + _exit(1); + } + + { + struct stat st; // allocate temporary + + stat("..",&st); + parent_inode = st.st_ino; // used for is_safepath() + } + + srand(now+getpid()); + + while(*envp) + { + char *p1; + const char *p2; + + p1 = *envp; + p2 = STR_MECHRESET; + while(*p2) + { + if (*p1 != *p2) + break; + p1++; + p2++; + } + if (*p2 == 0) + { + mechresetenv = p1; + break; + } + envp++; + } + +#ifdef DEBUG + mrrec = calloc(sizeof(aMEA),1); +#endif /* DEBUG */ + + if (!*argv || !**argv) + { + to_file(1,bad_exe); + _exit(1); + } + if ((opt = STRCHR(*argv,' ')) != NULL) + { + *(opt++) = 0; + respawn = a2i(opt); + if (errno) + { + to_file(1,bad_exe); + _exit(1); + } + } + + executable = *argv; + + while((argc > 1) && (argv[1][0] == '-')) + { + argc--; + argv++; + opt = *argv; + switch(opt[1]) + { + case 'v': + versiononly = TRUE; + break; + case 'h': + to_file(1,TEXT_USAGE,executable); + to_file(1,TEXT_FSWITCH); + to_file(1,TEXT_CSWITCH); +#ifdef DEBUG + to_file(1," -d start mech in debug mode\n"); + to_file(1," -o write debug output to \n"); + to_file(1," -X write a debug file before exit\n"); +#endif /* DEBUG */ + to_file(1,TEXT_HSWITCH); + to_file(1,TEXT_VSWITCH); + _exit(0); + case 'c': + makecore = TRUE; + break; +#ifdef DEBUG + case 'd': + dodebug = TRUE; + do_fork = FALSE; + break; + case 'o': + if (opt[2] != 0) + { + debugfile = &opt[2]; + } + else + { + ++argv; + if (!*argv) + { + to_file(1,"init: No debugfile specified\n"); + _exit(0); + } + debugfile = *argv; + argc--; + } + do_fork = TRUE; + break; + case 'X': + debug_on_exit = TRUE; + break; +#endif /* DEBUG */ + case 'f': + if (opt[2] != 0) + { + configfile = &opt[2]; + } + else + { + ++argv; + if(!*argv) + { + to_file(1,ERR_MISSINGCONF); + _exit(0); + } + configfile = *argv; + argc--; + } + to_file(1,INFO_USINGCONF,configfile); + break; + default: + to_file(1,ERR_UNKNOWNOPT,opt); + _exit(1); + } + } + + servergrouplist = (ServerGroup*)&defaultServerGroup; + currentservergroup = (ServerGroup*)&defaultServerGroup; + + if (!mechresetenv) + { + to_file(1,TEXT_HDR_VERS,VERSION,SRCDATE); + to_file(1,TEXT_HDR_DATE); + to_file(1,TEXT_HDR_FEAT,__mx_opts); + } + + if (versiononly) + _exit(0); /* _exit() here because we dont want a profile file to be written */ + +#ifdef __linux__ + signal(SIGCHLD,SIG_IGN); + signal(SIGALRM,SIG_IGN); + signal(SIGPIPE,SIG_IGN); +#else + signal(SIGCHLD,sig_child); + signal(SIGALRM,sig_alrm); + signal(SIGPIPE,sig_pipe); +#endif + signal(SIGHUP,sig_hup); + signal(SIGINT,sig_int); + signal(SIGBUS,sig_bus); + signal(SIGTERM,sig_term); + signal(SIGUSR1,sig_usr1); +#ifdef DEBUG + signal(SIGUSR2,sig_usr2); +#else /* DEBUG */ + signal(SIGUSR2,SIG_IGN); +#endif /* DEBUG */ + +#ifdef RAWDNS + memset(&ia_ns,0,sizeof(ia_ns)); + ia_default.s_addr = LOCALHOST_ULONG; +#endif /* RAWDNS */ + + memset(&__internal_users,0,sizeof(User)*2); + CoreUser.x.x.access = 100; + LocalBot.x.x.access = 200; + LocalBot.x.x.aop = 1; + LocalBot.chan = CoreUser.chan = (Strp*)&CMA; + + readcfgfile(); + +#ifndef I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS + if (spawning_lamer > 4) + { + to_file(1,"init: I refuse to run more than 4 bots!\n"); + _exit(1); + } +#endif /* I_HAVE_A_LEGITIMATE_NEED_FOR_MORE_THAN_4_BOTS */ + + for(current=botlist;current;current=current->next) + { + if ((opt = current->setting[STR_USERFILE].str_var)) + read_userlist(opt); + } + for(current=botlist;current;current=current->next) + { + mirror_userlist(); + } +#ifdef SEEN + read_seenlist(); +#endif /* SEEN */ + + if (!mechresetenv) + to_file(1,INFO_RUNNING); + + if (!mechresetenv && do_fork) + { + close(0); + close(1); + close(2); + + switch(fork()) + { + case 0: + break; + default: +#ifdef DEBUG + debug_on_exit = FALSE; +#endif /* DEBUG */ + case -1: + mechexit(0,_exit); + } + setsid(); + } + + /* + * save pid to `mech.pid' + */ + if ((do_fork = open(PIDFILE,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) >= 0) + { + to_file(do_fork,"%i\n",(int)(getpid())); + close(do_fork); + } + +#ifdef CTCP + memset(&ctcp_slot,0,sizeof(ctcp_slot)); +#endif /* CTCP */ + +#ifdef UPTIME + init_uptime(); +#endif /* UPTIME */ + +#ifdef BOTNET + last_autolink = now + 30 + (rand() >> 27); /* + 0-31 seconds */ +#endif /* BOTNET */ + + if (mechresetenv) + recover_reset(); + + /* + * wait until after recover_reset() cuz it might change makecore + */ + if (!makecore) + signal(SIGSEGV,sig_segv); + + doit(); +} diff --git a/src/md5/md5.c b/src/md5/md5.c new file mode 100644 index 0000000..77b8454 --- /dev/null +++ b/src/md5/md5.c @@ -0,0 +1,400 @@ +/* md5.c */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +#ifndef MD5_CRYPT_C +#define MD5_CRYPT_C + +#include /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void +byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif + +/* md5crypt.c */ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ +/* + * Ported from FreeBSD to Linux, only minimal changes. --marekm + */ + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void +to64(char *s, unsigned long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ +char *md5_crypt(const char *pw, const char *salt) +{ + static char *magic = "$1$"; /* + * This string is magic for + * this algorithm. Having + * it this way, we can get + * get better later on + */ + static char passwd[120], *p; + static const char *sp,*ep; + unsigned char final[16]; + int sl,pl,i,j; + MD5_CTX ctx,ctx1; + unsigned long l; + + /* Refine the Salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + if(!strncmp(sp,magic,strlen(magic))) + sp += strlen(magic); + + /* It stops at the first '$', max 8 chars */ + for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) + continue; + + /* get the length of the true salt */ + sl = ep - sp; + + MD5Init(&ctx); + + /* The password first, since that is what is most unknown */ + MD5Update(&ctx,pw,strlen(pw)); + + /* Then our magic string */ + MD5Update(&ctx,magic,strlen(magic)); + + /* Then the raw salt */ + MD5Update(&ctx,sp,sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5Init(&ctx1); + MD5Update(&ctx1,pw,strlen(pw)); + MD5Update(&ctx1,sp,sl); + MD5Update(&ctx1,pw,strlen(pw)); + MD5Final(final,&ctx1); + for(pl = strlen(pw); pl > 0; pl -= 16) + MD5Update(&ctx,final,pl>16 ? 16 : pl); + + /* Don't leave anything around in vm they could use. */ + memset(final,0,sizeof final); + + /* Then something really weird... */ + for (j=0,i = strlen(pw); i ; i >>= 1) + if(i&1) + MD5Update(&ctx, final+j, 1); + else + MD5Update(&ctx, pw+j, 1); + + /* Now make the output string */ + strcpy(passwd,magic); + strncat(passwd,sp,sl); + strcat(passwd,"$"); + + MD5Final(final,&ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for(i=0;i<1000;i++) { + MD5Init(&ctx1); + if(i & 1) + MD5Update(&ctx1,pw,strlen(pw)); + else + MD5Update(&ctx1,final,16); + + if(i % 3) + MD5Update(&ctx1,sp,sl); + + if(i % 7) + MD5Update(&ctx1,pw,strlen(pw)); + + if(i & 1) + MD5Update(&ctx1,final,16); + else + MD5Update(&ctx1,pw,strlen(pw)); + MD5Final(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; + l = final[11] ; to64(p,l,2); p += 2; + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final,0,sizeof final); + + return passwd; +} + +#endif /* MD5_CRYPT_C */ diff --git a/src/md5/md5.h b/src/md5/md5.h new file mode 100644 index 0000000..e264f68 --- /dev/null +++ b/src/md5/md5.h @@ -0,0 +1,27 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef __alpha +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32 buf[4], uint32 const in[16]); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +#endif /* !MD5_H */ diff --git a/src/mega.c b/src/mega.c new file mode 100644 index 0000000..4d34a39 --- /dev/null +++ b/src/mega.c @@ -0,0 +1,53 @@ +#define MAIN_C +#include "config.h" +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" +#undef MAIN_C +#define VARS_C +#include "settings.h" +#undef VARS_C + +#include "alias.c" +#include "auth.c" +#include "bounce.c" +#include "chanban.c" +#include "channel.c" +#include "core.c" +#include "ctcp.c" +#include "debug.c" +#include "dns.c" +#include "dynamode.c" +#include "function.c" +#include "greet.c" +#include "help.c" +#include "irc.c" +#include "kicksay.c" +#include "main.c" +#include "net.c" +#include "net_chan.c" +#include "note.c" +#include "notify.c" +#include "ons.c" +#include "parse.c" +#include "perl.c" +#include "prot.c" +#include "python.c" +#include "redirect.c" +#include "reset.c" +#include "seen.c" +#include "shit.c" +#include "socket.c" +#include "spy.c" +#include "stats.c" +#include "tcl.c" +#include "telnet.c" +#include "toybox.c" +#include "trivia.c" +#include "uptime.c" +#include "user.c" +#include "vars.c" +#include "web.c" diff --git a/src/net.c b/src/net.c new file mode 100644 index 0000000..6030f8c --- /dev/null +++ b/src/net.c @@ -0,0 +1,1585 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define NET_C +#include "config.h" + +#ifdef BOTNET + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#ifdef MD5CRYPT +#define md5banneropt " MD5" +char *CRYPT_FUNC(const char *, const char *); +#else +#define md5banneropt +#endif /* MD5CRYPT */ + +#ifdef TELNET +char *telnetprompt = TEXT_ENTERNICKNAME; +#endif /* TELNET */ +/* + * this is a partial copy of the BotNet struct + */ +LS struct +{ + struct BotNet *next; + + int sock; + int status; + int has_data; + +} linksock = { NULL, -1, BN_LINKSOCK, 0 }; + +/* + * command lists + */ +typedef struct LinkCmd +{ + char c1; + char c2; + void (*func)(BotNet *, char *); + +} LinkCmd; + +LS const LinkCmd basicProto[] = +{ +{ 'B', 'A', basicAuth }, +{ 'B', 'B', basicBanner }, +{ 'B', 'K', basicAuthOK }, +{ 'B', 'L', basicLink }, +{ 'B', 'Q', basicQuit }, +{ 'C', 'O', netchanNeedop }, +{ 'P', 'A', partyAuth }, +#ifdef REDIRECT +{ 'P', 'C', partyCommand }, +#endif /* REDIRECT */ +{ 'P', 'M', partyMessage }, +{ 'U', 'T', ushareTick }, +{ 'U', 'U', ushareUser }, +{ 'U', 'D', ushareDelete }, +{ 0, 0, NULL }, +}; + +LS int deadlinks = FALSE; + +/* + * + * misc + * + */ + +Mech *get_netbot(void) +{ + Mech *netbot,*bot; + int uid; + + netbot = NULL; + uid = INT_MAX; + for(bot=botlist;bot;bot=bot->next) + { + if (bot->guid < uid) + { + uid = bot->guid; + netbot = bot; + } + } + return(netbot); +} + +void reset_linkable(int guid) +{ + NetCfg *cfg; + + for(cfg=netcfglist;cfg;cfg=cfg->next) + { + if (cfg->guid == guid) + { +#ifdef DEBUG + debug("(reset_linkable) guid %i reset to linkable\n",guid); +#endif /* DEBUG */ + cfg->linked = FALSE; + return; + } + } +} + +void botnet_deaduplink(BotNet *bn) +{ + bn->status = BN_DEAD; + deadlinks = TRUE; + reset_linkable(bn->guid); +} + +NetCfg *find_netcfg(int guid) +{ + NetCfg *cfg; + + for(cfg=netcfglist;cfg;cfg=cfg->next) + { + if (cfg->guid == guid) + return(cfg); + } + return(NULL); +} + +BotInfo *make_botinfo(int guid, int hops, char *nuh, char *server, char *version) +{ + BotInfo *new; + + set_mallocdoer(make_botinfo); + new = (BotInfo*)Calloc(sizeof(BotInfo) + Strlen(nuh,server,version,NULL)); + + new->guid = guid; + new->hops = hops; + + new->server = Strcat(new->nuh,nuh) + 1; + new->version = Strcat(new->server,server) + 1; + Strcpy(new->version,version); + + return(new); +} + +void botnet_relay(BotNet *source, char *format, ...) +{ + BotNet *bn; + va_list msg; + int sz = 0; + + for(bn=botnetlist;bn;bn=bn->next) + { + if ((bn == source) || (bn->status != BN_LINKED)) + continue; + + if (!sz) + { + va_start(msg,format); + vsprintf(gsockdata,format,msg); + va_end(msg); + sz = strlen(gsockdata); + } + + if (write(bn->sock,gsockdata,sz) < 0) + botnet_deaduplink(bn); +#ifdef DEBUG + debug("[bnr] {%i} %s",bn->sock,gsockdata); +#endif /* DEBUG */ + } +} + +void botnet_refreshbotinfo(void) +{ + Server *sv; + + sv = find_server(current->server); + botnet_relay(NULL,"BL%i 0 %s!%s %s:%i %s %s\n", current->guid,current->nick, + (current->userhost) ? current->userhost : UNKNOWNATUNKNOWN, + (sv) ? ((*sv->realname) ? sv->realname : sv->name) : UNKNOWN, + (sv) ? sv->port : 0,BOTCLASS,VERSION); +#ifdef DEBUG + debug("(botnet_refreshbotinfo) sent refreshed information to botnet\n"); +#endif /* DEBUG */ +} + +__attr(CORE_SEG, __regparm (2)) +void botnet_binfo_relay(BotNet *source, BotInfo *binfo) +{ + botnet_relay(source,"BL%i %i %s %s %s\n",binfo->guid,(binfo->hops + 1), + (binfo->nuh) ? binfo->nuh : UNKNOWNATUNKNOWN, + (binfo->server) ? binfo->server : UNKNOWN, + (binfo->version) ? binfo->version : "-"); +} + +__attr(CORE_SEG, __regparm (2)) +void botnet_binfo_tofile(int sock, BotInfo *binfo) +{ + to_file(sock,"BL%i %i %s %s %s\n",binfo->guid,(binfo->hops + 1), + (binfo->nuh) ? binfo->nuh : UNKNOWNATUNKNOWN, + (binfo->server) ? binfo->server : UNKNOWN, + (binfo->version) ? binfo->version : "-"); +} + +void botnet_dumplinklist(BotNet *bn) +{ + BotInfo *binfo; + BotNet *bn2; + Mech *bot; + Server *sv; + + /* + * send link lines + */ + for(bot=botlist;bot;bot=bot->next) + { + /* + * tell the other side who the local bots are + */ + sv = find_server(bot->server); + to_file(bn->sock,"BL%i %c %s!%s %s:%i %s %s\n",bot->guid, + (bot == bn->controller) ? '0' : '1',bot->nick, + (bot->userhost) ? bot->userhost : UNKNOWNATUNKNOWN, + (sv) ? ((*sv->realname) ? sv->realname : sv->name) : UNKNOWN, + (sv) ? sv->port : 0,BOTCLASS,VERSION); + } + for(bn2=botnetlist;bn2;bn2=bn2->next) + { + if ((bn2 == bn) || (bn2->status != BN_LINKED) || !(bn2->list_complete)) + continue; + for(binfo=bn2->botinfo;binfo;binfo=binfo->next) + botnet_binfo_tofile(bn->sock,binfo); + } + /* + * tell remote end to sync + */ + to_file(bn->sock,"BL-\n"); +} + +int connect_to_bot(NetCfg *cfg) +{ + BotNet *bn; + int s; + +#ifdef DEBUG + debug("(connect_to_bot) NetCfg* "mx_pfmt" = { \"%s\", guid = %i, port = %i, ... }\n", + (mx_ptr)cfg,nullstr(cfg->host),cfg->guid,cfg->port); +#endif /* DEBUG */ + + if ((s = SockConnect(cfg->host,cfg->port,FALSE)) < 0) + return(-1); + + /* + * set linked status + */ + cfg->linked = TRUE; + + set_mallocdoer(connect_to_bot); + bn = (BotNet*)Calloc(sizeof(BotNet)); + + bn->sock = s; + bn->status = BN_CONNECT; + bn->when = now; + bn->guid = cfg->guid; + + bn->next = botnetlist; + botnetlist = bn; + + return(0); +} + +/* + * + * protocol routines + * + */ + +void basicAuth(BotNet *bn, char *rest) +{ + NetCfg *cfg; + char *pass; + int authtype = -1; + + if (bn->status != BN_WAITAUTH) + return; + + if ((pass = chop(&rest))) + { + if (!Strcmp(pass,"PTA")) + authtype = BNAUTH_PLAINTEXT; +#ifdef MD5CRYPT + if (!Strcmp(pass,"MD5")) + authtype = BNAUTH_MD5; +#endif /* MD5CRYPT */ + } + + /* + * find the other bots' password so we can check if it matches + */ + pass = NULL; + for(cfg=netcfglist;cfg;cfg=cfg->next) + { + if (cfg->guid == bn->guid) + { + pass = cfg->pass; + break; + } + } + if (!pass || !*pass) + goto badpass; + + switch(authtype) + { + case BNAUTH_PLAINTEXT: + if (Strcmp(pass,rest)) + goto badpass; + break; +#ifdef MD5CRYPT + case BNAUTH_MD5: + if (linkpass && *linkpass) + { + char *enc,temppass[24 + Strlen2(pass,linkpass)]; + + /* "mypass theirpass REMOTEsid LOCALsid" */ + sprintf(temppass,"%s %s %i %i",linkpass,pass,bn->rsid,bn->lsid); +#ifdef DEBUG + debug(">> md5 pass exchange: \"%s\"\n",temppass); +#endif /* DEBUG */ + enc = CRYPT_FUNC(temppass,rest); +#ifdef DEBUG + debug("(basicAuth) their = %s, mypass = %s :: md5 = %s\n", + pass,linkpass,enc); +#endif /* DEBUG */ + if (!Strcmp(enc,rest)) + break; + } +#endif /* MD5CRYPT */ + default: + badpass: + /* + * we can/should use deaduplink here since we set cfg->linked = TRUE in basicBanner() + */ + botnet_deaduplink(bn); +#ifdef DEBUG + debug("(basicAuth) bad password [ guid = %i ]\n",bn->guid); +#endif /* DEBUG */ + return; + } + + to_file(bn->sock,"BK\n"); + bn->status = BN_LINKED; + botnet_dumplinklist(bn); +#ifdef DEBUG + debug("(basicAuth) bn->tick = 0\n"); +#endif /* DEBUG */ + bn->tick = 0; + bn->tick_last = now - 580; /* 10 minutes (10*60) - 20 seconds */ +} + +void basicAuthOK(BotNet *bn, char *rest) +{ + if (bn->status != BN_WAITLINK) + return; + + bn->status = BN_LINKED; + botnet_dumplinklist(bn); +#ifdef DEBUG + debug("(basicAuthOK) bn->tick = 0\n"); +#endif /* DEBUG */ + bn->tick = 0; + bn->tick_last = now - 580; /* 10 minutes (10*60) - 20 seconds */ +} + +void basicBanner(BotNet *bn, char *rest) +{ + Mech *netbot; + NetCfg *cfg; + char *p; + int guid; + int authtype = -1; + + /* + * we're only prepared to listen to banners in the first connection phase + */ + if (bn->status != BN_BANNERSENT && bn->status != BN_UNKNOWN) + return; + + /* + * find out who's calling + */ + p = chop(&rest); + guid = a2i(p); + /* + * bad guid received + */ + if (errno) + { + if (bn->guid) + { + /* + * we know who its supposed to be, ergo sum, we've set cfg->linked = TRUE, lets undo that + */ + reset_linkable(bn->guid); + } + bn->status = BN_DEAD; + deadlinks = TRUE; + return; + } + + if (bn->guid && bn->guid != guid) + { + /* + * its not who we expected! + */ +#ifdef DEBUG + debug("(basicBanner) {%i} calling guid %i but got guid %i!\n", + bn->sock,bn->guid,guid); +#endif /* DEBUG */ + botnet_deaduplink(bn); + return; + } + + /* + * either (bn->guid == 0) || (bn->guid == guid), we dont need to check + */ + bn->guid = guid; + + /* + * prevent circular linking + */ + if (bn->status == BN_UNKNOWN) + { + /* + * they are connecting to us + */ + if ((cfg = find_netcfg(guid))) + { + if (cfg->linked) + { + /* + * we already think this remote bot is connected and it's still connecting to us! + */ + bn->status = BN_DEAD; + deadlinks = TRUE; +#ifdef DEBUG + debug("(basicBanner) {%i} guid %i (connecting to us) is already linked!\n", + bn->sock,bn->guid); +#endif /* DEBUG */ + return; + } + /* + * its not linked? well it is now! + */ + cfg->linked = TRUE; + } + } + + /* + * get a session number + */ + p = chop(&rest); + bn->rsid = a2i(p); + if (errno) + { + botnet_deaduplink(bn); + return; + } + + /* + * parse banner options + */ + while((p = chop(&rest))) + { + if (!Strcmp(p,"PTA")) + bn->opt.pta = TRUE; +#ifdef MD5CRYPT + if (!Strcmp(p,"MD5")) + bn->opt.md5 = TRUE; +#endif /* MD5CRYPT */ + } + + /* + * update timestamp + */ + bn->when = now; + + /* + * if the remote bot initiated the connection we need a valid pass from them + * before we send our own password to validate + */ + if (bn->status == BN_UNKNOWN) + { + bn->controller = netbot = get_netbot(); + to_file(bn->sock,"BB%i %i PTA" md5banneropt "\n",netbot->guid,bn->lsid); + bn->status = BN_WAITAUTH; + return; + } + + /* + * we're the one that initiated the connection, we now send our password + */ + if (!linkpass || !*linkpass) + { + botnet_deaduplink(bn); + return; + } + + /* + * select authentication method + */ + if (bn->opt.pta && (BNAUTH_PLAINTEXT > authtype)) + authtype = BNAUTH_PLAINTEXT; +#ifdef MD5CRYPT + if (bn->opt.md5 && (BNAUTH_MD5 > authtype)) + authtype = BNAUTH_MD5; +#endif /* MD5CRYPT */ + + switch(authtype) + { + case BNAUTH_PLAINTEXT: + to_file(bn->sock,"BAPTA %s\n",linkpass); + break; +#ifdef MD5CRYPT + case BNAUTH_MD5: + if ((cfg = find_netcfg(guid))) + { + if (cfg->pass && *cfg->pass) + { + char *enc,salt[8]; + char temppass[24 + Strlen2(cfg->pass,linkpass)]; + + /* "theirpass mypass LOCALsid REMOTEsid" */ + sprintf(temppass,"%s %s %i %i",cfg->pass,linkpass,bn->lsid,bn->rsid); +#ifdef DEBUG + debug(">> md5 pass exchange: \"%s\"\n",temppass); +#endif /* DEBUG */ + sprintf(salt,"$1$%04x",(rand() >> 16)); + enc = CRYPT_FUNC(temppass,salt); + to_file(bn->sock,"BAMD5 %s\n",enc); + break; + } + } +#endif /* MD5CRYPT */ + default: + botnet_deaduplink(bn); + return; + } + bn->status = BN_WAITLINK; +} + +void basicLink(BotNet *bn, char *version) +{ + BotInfo *binfo,*delete,**pp; + NetCfg *cfg; + char *nuh,*server; + int guid,hops; + + if (bn->status != BN_LINKED) + return; + + /* + * BL- + */ + if (*version == '-') + { + /* + * check all links for conflicts + */ + binfo = (bn->botinfo) ? bn->botinfo->next : NULL; + for(;binfo;binfo=binfo->next) + { + if ((cfg = find_netcfg(binfo->guid)) == NULL) + continue; + if (cfg->linked == TRUE) + break; + } + if (binfo) + { +#ifdef DEBUG + debug("(basicLink) circular linking: guid == %i\n",binfo->guid); +#endif /* DEBUG */ + /* + * drop everything we've gotten sofar... + */ + botnet_deaduplink(bn); + return; + } + + /* + * we're done, we're fine, we're linked! + */ + for(binfo=bn->botinfo;binfo;binfo=binfo->next) + { + botnet_binfo_relay(bn,binfo); + check_botinfo(binfo,NULL); + if ((cfg = find_netcfg(binfo->guid)) == NULL) + continue; + cfg->linked = TRUE; + } + bn->list_complete = TRUE; + return; + } + + /* + * BL ! + */ + nuh = chop(&version); + guid = a2i(nuh); + if (errno) + return; + + nuh = chop(&version); + hops = a2i(nuh); + if (errno) + return; + + nuh = chop(&version); + server = chop(&version); + + if (!*version) + return; + + binfo = make_botinfo(guid,hops,nuh,server,version); + + if (bn->botinfo == NULL) + send_global(SPYSTR_BOTNET,"connecting to %s [guid %i]",nickcpy(NULL,nuh),bn->guid); + pp = &bn->botinfo; + while(*pp) + { + delete = *pp; + if (guid == delete->guid) + { + *pp = delete->next; + Free((char**)&delete); + break; + } + pp = &delete->next; + } + binfo->next = *pp; + *pp = binfo; + + if (bn->list_complete) + { + if ((cfg = find_netcfg(guid))) + cfg->linked = TRUE; + /* + * broadcast the new link + */ + botnet_binfo_relay(bn,binfo); + check_botinfo(binfo,NULL); + } +} + +void basicQuit(BotNet *bn, char *rest) +{ + BotInfo *binfo,**pp_binfo; + int guid; + + if (bn->status != BN_LINKED) + return; + + guid = a2i(rest); + if (errno) + return; + + pp_binfo = &bn->botinfo; + while(*pp_binfo) + { + binfo = *pp_binfo; + if (binfo->guid == guid) + { + send_global(SPYSTR_BOTNET,"quit: %s (from guid %i)",rest,bn->guid); + *pp_binfo = binfo->next; + reset_linkable(guid); + Free((char**)&binfo); + break; + } + pp_binfo = &binfo->next; + } + botnet_relay(bn,"BQ%s\n",rest); +} + +/* + * + */ + +void partyAuth(BotNet *bn, char *rest) +{ + User *user; + Strp *ump; + char *name,*userhost,*checksum; + int m; + + name = chop(&rest); + userhost = chop(&rest); + if ((checksum = chop(&rest)) == NULL) + checksum = ""; + + for(current=botlist;current;current=current->next) + { + for(user=current->userlist;user;user=user->next) + { + for(ump=user->mask;ump;ump=ump->next) + { + if (!matches(ump->p,userhost)) + { +#ifdef DEBUG + debug("(partyAuth) testing auth on %s\n",user->name); +#endif /* DEBUG */ + m = 0; + if (user->pass) + { + sprintf(gsockdata,"%s %s %s",userhost,user->name,user->pass); + m = passmatch(gsockdata,checksum); + } + if (m) + { + make_auth(userhost,user); + } + } + } + } + } +} + +#ifdef REDIRECT + +int commandlocal(int dg, int sg, char *from, char *command) +{ + Client mydcc; + User *user; + Strp *sp; + char tempdata[MSGLEN]; + char *p1,*p2,*uh; + +#ifdef DEBUG + debug("(commandlocal) %i %i %s %s\n",dg,sg,from,command); +#endif /* DEBUG */ + + uh = getuh(from); + nickcpy(CurrentNick,from); + for(current=botlist;current;current=current->next) + { + if (dg != -1 && current->guid != dg) + continue; + + user = get_authuser(from,ANY_CHANNEL); + if (!user && (user = find_handle(CurrentNick))) + { + for(sp=user->mask;sp;sp=sp->next) + { + if (!Strcmp(sp->p,uh)) + break; + } + } + if (user) + { + mydcc.user = CurrentUser = user; + CurrentDCC = &mydcc; + CurrentShit = NULL; + CurrentChan = NULL; + + redirect.method = R_BOTNET; + redirect.guid = sg; + set_mallocdoer(commandlocal); + redirect.to = Strdup(CurrentNick); + + p1 = tempdata; + p2 = Strcpy(p1,from); + p2++; /* skip past '0' */ + *p2 = current->setting[CHR_CMDCHAR].char_var; + Strcpy((*p2 == *command) ? p2 : p2+1,command); + + on_msg(p1,current->nick,p2); + CurrentDCC = NULL; + } + if (dg == -1) + return(TRUE); + } + return(FALSE); +} + +/* + * PC + */ +void partyCommand(BotNet *bn, char *rest) +{ + BotNet *bn2; + BotInfo *binfo; + char *dguid,*sguid,*userhost; + int idguid,isguid; + +#ifdef DEBUG + debug("(partyCommand) %s\n",rest); +#endif /* DEBUG */ + + dguid = chop(&rest); + sguid = chop(&rest); + if ((userhost = chop(&rest)) == NULL || *rest == 0) + return; + + isguid = a2i(sguid); + if (errno) + return; + + idguid = a2i(dguid); + if (errno) /* == "*" */ + { + commandlocal(-1,isguid,userhost,rest); + } + else + { + if (commandlocal(idguid,isguid,userhost,rest)) + return; + for(bn2=botnetlist;bn2;bn2=bn2->next) + { + if (bn2->status != BN_LINKED) + continue; + for(binfo=bn2->botinfo;binfo;binfo=binfo->next) + { + if (idguid == binfo->guid) + { + to_file(bn2->sock,"PC%s %s %s %s\n",dguid,sguid,userhost,rest); + return; + } + } + } + } + botnet_relay(bn,"PC%s %s %s %s\n",dguid,sguid,userhost,rest); +} + +#endif /* REDIRECT */ + +/* + * PM + */ +void partyMessage(BotNet *bn, char *rest) +{ + BotNet *bn2; + BotInfo *binfo; + Client *client; + char *src,*dst,*userhost; + int guid; + + dst = chop(&rest); + userhost = chop(&rest); + if ((src = chop(&rest)) == NULL || *rest == 0) + return; + + guid = a2i(dst); + if (errno) /* == "*" */ + { + Strncpy(CurrentNick,src,NUHLEN-1); + /* + * partyline_broadcast() uses CurrentNick for the first %s in the format + */ + if (*rest == 1) + partyline_broadcast(NULL,"* %s %s\n",rest+1); + else + partyline_broadcast(NULL,"<%s> %s\n",rest); + botnet_relay(bn,"PM* * %s %s\n",src,rest); + } + else + { + for(current=botlist;current;current=current->next) + { + if (guid == current->guid) + { + if ((client = find_client(userhost))) + { + to_file(client->sock,"[%s] %s\n",src,rest); + return; + } + } + } + for(bn2=botnetlist;bn2;bn2=bn2->next) + { + if (bn2->status != BN_LINKED) + continue; + for(binfo=bn2->botinfo;binfo;binfo=binfo->next) + { + if (guid == binfo->guid) + { + to_file(bn2->sock,"PM%s %s %s %s\n",dst,userhost,src,rest); + return; + } + } + } + botnet_relay(bn,"PM%s %s %s %s\n",dst,userhost,src,rest); + } +} + +/* + * + */ + +void ushareUser(BotNet *bn, char *rest) +{ + User *user,tempuser; + char c,*handle,*pass; + int i,tick,modcount,uaccess; + + c = *(rest++); + tick = a2i(chop(&rest)); + if (errno) + return; + if ((c != '-' && bn->tick >= tick) && (c != '+' && bn->tick != tick)) + return; + switch(c) + { + case '+': + if (tick > bn->tick) + { +#ifdef DEBUG + debug("(ushareUser) bumping tick to %i\n",tick); +#endif /* DEBUG */ + bn->tick = tick; + } + /* `UU+tick modcount access handle chan pass' */ + /* UU+0 4 100 proton * $1$3484$AxMkHvZijkeqb8hA6h9AB/ */ + i = 0; + modcount = a2i(chop(&rest)); + i += errno; + uaccess = a2i(chop(&rest)); + i += errno; + handle = chop(&rest); + pass = chop(&rest); + if (i == 0 && handle && pass && *pass) + { + if (!Strcmp(pass,"none")) + pass = NULL; + i = 0; + bn->addsession = (rand() | 1); + for(current=botlist;current;current=current->next) + { + /* accept users from other bots */ + if (current->setting[TOG_NETUSERS].int_var == 0) + continue; + user = find_handle(handle); + if (user && user->x.x.readonly) + continue; + if (user && user->modcount < modcount) + { + /* user with higher modcount overwrites */ + remove_user(user); + user = NULL; + } + if (!user) + { +#ifdef DEBUG + debug("(ushareUser) user %s ++ re-creating\n",handle); +#endif /* DEBUG */ + user = add_user(handle,pass,uaccess); + user->modcount = modcount; + user->guid = bn->guid; /* we got new user from this session */ + user->tick = global_tick; + user->addsession = bn->addsession; + i++; + } + } + if (i) + { +#ifdef DEBUG + debug("(ushareUser) bn->tick = %i\n",tick); +#endif /* DEBUG */ + bn->tick = tick; + global_tick++; + } + } + break; + case '-': +#ifdef DEBUG + debug("(ushareUser) ticking to next user %i ++\n",bn->tick); +#endif /* DEBUG */ + for(current=botlist;current;current=current->next) + for(user=current->userlist;user;user=user->next) + if (user->guid == bn->guid && user->addsession) + { + user->addsession = 0; + mirror_user(user); // copy to other local bots + } + bn->addsession = 0; + bn->tick++; + to_file(bn->sock,"UT%i\n",bn->tick); + bn->tick_last = now; + break; + case '*': + case '#': + if ((rest = chop(&rest)) == NULL) + return; + for(current=botlist;current;current=current->next) + { + for(user=current->userlist;user;user=user->next) + { + if (user->guid == bn->guid && user->addsession == bn->addsession) + { +#ifdef DEBUG + debug("(ushareUser) user %s ++ mask/chan %s\n",user->name,rest); +#endif /* DEBUG */ + addtouser((c == '*') ? &user->mask : &user->chan,rest); + } + } + } + break; + case '!': + user = cfgUser; + cfgUser = memset(&tempuser,0,sizeof(User)); + cfg_opt(chop(&rest)); + cfgUser = user; + for(current=botlist;current;current=current->next) + { + for(user=current->userlist;user;user=user->next) + { + if (user->guid == bn->guid && user->addsession == bn->addsession) + { +#ifdef DEBUG + debug("(ushareUser) user %s ++ touching flags\n",user->name); + debug("(ushareUser) %s %s %s prot%i\n",(tempuser.x.x.aop) ? "aop" : "",(tempuser.x.x.echo) ? "echo" : "", + (tempuser.x.x.avoice) ? "avoice" : "",tempuser.x.x.prot); +#endif /* DEBUG */ + user->x.x.aop = tempuser.x.x.aop; + user->x.x.echo = tempuser.x.x.echo; + user->x.x.avoice = tempuser.x.x.avoice; +#ifdef BOUNCE + user->x.x.bounce = tempuser.x.x.bounce; +#endif /* BOUNCE */ + } + } + } + break; + } +#ifdef DEBUG + current = NULL; +#endif /* DEBUG */ +} + +void ushareTick(BotNet *bn, char *rest) +{ + Strp *pp; + Mech *mech; + User *user,*senduser; + int i; + + i = a2i(rest); + if (errno) + return; +#ifdef DEBUG + debug("(ushareTick) remote bot ticked %i\n",i); +#endif /* DEBUG */ + senduser = 0; + for(mech=botlist;mech;mech=mech->next) + { + for(user=mech->userlist;user;user=user->next) + { + if (i <= user->tick && !user->x.x.noshare) + { + if (!senduser) + senduser = user; + /* user->guid != bn->guid :: dont send users back to the bot we got them from */ + if (user->tick < senduser->tick && user->guid != bn->guid) + senduser = user; + } + } + } + if (senduser) + { + char *p,temp[8]; + + user = senduser; +#ifdef DEBUG + debug("(ushareTick) user %s, user->tick = %i (%i)\n",user->name,user->tick,i); +#endif /* DEBUG */ + to_file(bn->sock,"UU+%i %i %i %s %s\n",user->tick,user->modcount, + user->x.x.access,user->name,(user->pass) ? user->pass : "none"); + *(p = temp) = 0; + if (user->x.x.aop) + *(p++) = 'a'; +#ifdef BOUNCE + if (user->x.x.bounce) + *(p++) = 'b'; +#endif /* BOUNCE */ + if (user->x.x.echo) + *(p++) = 'e'; + if (user->x.x.avoice) + *(p++) = 'v'; + *(p++) = 'p'; + *(p++) = '0' + user->x.x.prot; + *p = 0; + to_file(bn->sock,"UU!%i %s\n",user->tick,temp); + for(pp=user->mask;pp;pp=pp->next) + { + to_file(bn->sock,"UU*%i %s\n",user->tick,pp->p); + } + for(pp=user->chan;pp;pp=pp->next) + { + to_file(bn->sock,"UU#%i %s\n",user->tick,pp->p); + } + to_file(bn->sock,"UU-%i\n",user->tick); + return; + } +} + +void ushareDelete(BotNet *bn, char *rest) +{ + User *user; + char *orig; + int modcount; + + orig = rest; + modcount = a2i(chop(&rest)); + if (errno) + return; + for(current=botlist;current;current=current->next) + { + if (current->setting[TOG_NETUSERS].int_var) + { + user = find_handle(rest); + if (user && user->modcount == modcount) + { + reset_userlink(user,NULL); + remove_user(user); + } + } + } + unchop(orig,rest); + botnet_relay(bn,"UD%s\n",orig); +} + +/* + * + */ + +void botnet_parse(BotNet *bn, char *rest) +{ + int i; + +#ifdef TELNET + if (bn->status == BN_UNKNOWN) + { + if (!Strcmp(rest,telnetprompt)) + return; + if (*rest != 'B') + { + if (check_telnet(bn->sock,rest)) + { + bn->sock = -1; + bn->status = BN_DEAD; + deadlinks = TRUE; + return; + } + } + } +#endif /* TELNET */ + + if (*rest != 'B' && bn->status != BN_LINKED) + return; + + for(i=0;basicProto[i].func;i++) + { + if (basicProto[i].c1 == rest[0] && basicProto[i].c2 == rest[1]) + { + basicProto[i].func(bn,rest+2); + return; + } + } + + /* + * relay to bots that know/want the protocol + */ +} + +void botnet_newsock(void) +{ + BotNet *bn; + int s; + + /* + * accept the connection + */ + if ((s = SockAccept(linksock.sock)) < 0) + return; + +#ifdef DEBUG + debug("(botnet_newsock) {%i} new socket\n",s); +#endif /* DEBUG */ + +#ifdef TELNET + to_file(s,FMT_PLAINLINE,telnetprompt); +#endif /* TELNET */ + + set_mallocdoer(botnet_newsock); + bn = (BotNet*)Calloc(sizeof(BotNet)); + + bn->sock = s; + bn->status = BN_UNKNOWN; + bn->lsid = rand(); + bn->when = now; + + bn->next = botnetlist; + botnetlist = bn; + + /* + * crude... but, should work + */ + last_autolink = now + AUTOLINK_DELAY; +} + +/* + * + */ + +void select_botnet(void) +{ + BotNet *bn; + + /* + * handle incoming connections + */ + if (linkport && linksock.sock == -1) + { + linksock.sock = SockListener(linkport); + if (linksock.sock != -1) + { + linksock.next = botnetlist; + botnetlist = (BotNet*)&linksock; +#ifdef DEBUG + debug("(doit) {%i} Linksocket is active (%i)\n",linksock.sock,linkport); +#endif /* DEBUG */ + } + } + + /* + * autolink + */ + if (autolink && (now > last_autolink)) + { + last_autolink = now + AUTOLINK_DELAY; + + if (autolink_cfg) + autolink_cfg = autolink_cfg->next; + if (!autolink_cfg) + autolink_cfg = netcfglist; + + if (autolink_cfg && !autolink_cfg->linked && + autolink_cfg->host && autolink_cfg->pass) + { + /* + * this thing isnt linked yet! + */ + connect_to_bot(autolink_cfg); + } + } + + short_tv &= ~TV_BOTNET; + for(bn=botnetlist;bn;bn=bn->next) + { + chkhigh(bn->sock); + if (bn->status == BN_CONNECT) + { + FD_SET(bn->sock,&write_fds); + short_tv |= TV_BOTNET; + } + else + { + FD_SET(bn->sock,&read_fds); + } + } +} + +void process_botnet(void) +{ + BotNet *bn,**pp; + BotInfo *binfo; + Mech *netbot; + char *rest,linebuf[MSGLEN]; + + for(bn=botnetlist;bn;bn=bn->next) + { + /* + * usersharing tick, 10 minute period + */ + if (bn->status == BN_LINKED && (bn->tick_last + 600) < now) + { +#ifdef DEBUG + debug("(process_botnet) {%i} periodic ushare tick\n",bn->sock); +#endif /* DEBUG */ + bn->tick_last = now; + to_file(bn->sock,"UT%i\n",bn->tick); + } + + /* + * + */ + if (bn->has_data) + goto has_data; + + /* + * outgoing connection established + */ + if (FD_ISSET(bn->sock,&write_fds)) + { + bn->lsid = rand(); + bn->controller = netbot = get_netbot(); + if (to_file(bn->sock,"BB%i %i PTA" md5banneropt "\n",netbot->guid,bn->lsid) < 0) + { + botnet_deaduplink(bn); + } + else + { + bn->status = BN_BANNERSENT; + bn->when = now; + } + /* write_fds is only set for sockets where reading is not needed */ + continue; + } + + /* + * incoming data read + */ + if (FD_ISSET(bn->sock,&read_fds)) + { + /* + * ye trusty old linksock + */ + if (bn->status == BN_LINKSOCK) + botnet_newsock(); + else + { + has_data: + /* + * Commands might change the botnetlist, + * so returns here are needed + */ + rest = sockread(bn->sock,bn->sockdata,linebuf); + bn->has_data = (rest) ? TRUE : FALSE; + if (rest) + { + botnet_parse(bn,rest); + if (!deadlinks) + goto has_data; /* process more lines if link list is unchanged */ + goto clean; + } + switch(errno) + { + default: +#ifdef DEBUG + debug("(process_botnet) {%i} sockread() errno = %i\n",bn->sock,errno); +#endif /* DEBUG */ + botnet_deaduplink(bn); + case EAGAIN: + case EINTR: + goto clean; + } + } + } + + if ((bn->status == BN_CONNECT) && ((now - bn->when) > LINKTIME)) + { +#ifdef DEBUG + debug("(process_botnet) {%i} Life is good; but not for this guy (guid == %i). Timeout!\n", + bn->sock,bn->guid); +#endif /* DEBUG */ + botnet_deaduplink(bn); + } + } +clean: + /* + * quit/delete BN_DEAD links + */ + if (!deadlinks) + return; + + pp = &botnetlist; + while(*pp) + { + bn = *pp; + if (bn->status == BN_DEAD) + { + *pp = bn->next; + send_global(SPYSTR_BOTNET,"quit: guid %i",bn->guid); +#ifdef DEBUG + debug("(process_botnet) botnet quit: guid %i\n",bn->guid); +#endif /* DEBUG */ + while((binfo = bn->botinfo)) + { + bn->botinfo = binfo->next; +#ifdef DEBUG + debug("(process_botnet) botnet quit: guid %i child of %i on socket %i\n", + binfo->guid,bn->guid,bn->sock); +#endif /* DEBUG */ + if (bn->list_complete) + { + send_global(SPYSTR_BOTNET,"quit: guid %i (child of %i)", + binfo->guid,bn->guid); + botnet_relay(bn,"BQ%i\n",binfo->guid); + reset_linkable(binfo->guid); + } + Free((char**)&binfo); + } + if (bn->list_complete) + { + botnet_relay(bn,"BQ%i\n",bn->guid); + } + close(bn->sock); + Free((char**)&bn); + continue; + } + pp = &bn->next; + } + deadlinks = FALSE; +} + +/* + * + * commands related to botnet + * + */ + +void do_link(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + */ + NetCfg *cfg,**pp; + char *guid,*pass,*host,*port; + int iguid,iport; + int mode; + + /* + * list all the known links + */ + if (!*rest) + { + table_buffer("guid\tpass\thost\tport"); + for(cfg=netcfglist;cfg;cfg=cfg->next) + { + table_buffer("%i\t%s\t%s\t%i",cfg->guid,(cfg->pass) ? cfg->pass : "", + (cfg->host) ? cfg->host : "",cfg->port); + } + table_send(from,2); + return; + } + + guid = chop(&rest); + if (*guid == '+' || *guid == '-') + mode = *guid++; + else + mode = 0; + + iguid = a2i(guid); + if (errno) + { +usage: + usage(from); /* usage for CurrentCmd->name */ + return; + } + + pp = &netcfglist; + while((cfg = *pp)) + { + if (cfg->guid == iguid) + break; + pp = &cfg->next; + } + + if (CurrentUser == &CoreUser || mode == '+') + { + if (cfg) + { + to_user(from,"guid %i already exists",iguid); + return; + } + pass = chop(&rest); + host = chop(&rest); + port = chop(&rest); + + iport = a2i(port); + if (!pass || (host && !port) || (port && (errno || iport < 1 || iport > 65535))) + goto usage; + + set_mallocdoer(do_link); + cfg = (NetCfg*)Calloc(sizeof(NetCfg) + Strlen2(pass,host)); + + cfg->guid = iguid; + cfg->port = iport; + cfg->host = Strcat(cfg->pass,pass) + 1; + + if (host) + Strcpy(cfg->host,host); + else + cfg->host = NULL; + + cfg->next = netcfglist; + netcfglist = cfg; + return; + } + + if (!cfg) + { + to_user(from,"unknown guid: %i",iguid); + return; + } + + if (mode == '-') + { + *pp = cfg->next; + Free((char**)&cfg); + return; + } + + if (!cfg->host) + { + to_user(from,"unknown host/port for guid %i",iguid); + return; + } + + if (cfg->linked) + { + to_user(from,"guid %i is already connected",iguid); + return; + } + + if (connect_to_bot(cfg) < 0) + { + to_user(from,"unable to create connection"); + return; + } + + send_global(SPYSTR_BOTNET,"connecting to guid %i [%s:%i]",iguid,cfg->host,cfg->port); +} + +#ifdef REDIRECT + +void do_cmd(COMMAND_ARGS) +{ + Mech *backup; + char tempdata[MAXLEN]; + char *target,*orig = rest; + int guid; + + target = chop(&rest); + guid = a2i(target); + if (errno) + { + unchop(orig,rest); + rest = orig; + target = MATCH_ALL; + } + else + if (*rest == 0) + { + usage(from); + return; + } + + if (STRCHR(from,'!')) + sprintf(tempdata,"%s %i %s %s",target,current->guid,from,rest); + else + sprintf(tempdata,"%s %i %s!%s %s",target,current->guid,from,CurrentUser->mask->p,rest); + backup = current; + partyCommand(NULL,tempdata); + current = backup; +} + +#endif /* REDIRECT */ +#endif /* BOTNET */ diff --git a/src/net_chan.c b/src/net_chan.c new file mode 100644 index 0000000..7e5bf19 --- /dev/null +++ b/src/net_chan.c @@ -0,0 +1,144 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define NET_C +#include "config.h" + +#ifdef BOTNET + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +ChanUser *find_chanbot(Chan *chan, char *nick) +{ + ChanUser *cu; + + if (chan->cacheuser && !nickcmp(nick,chan->cacheuser->nick) + && chan->cacheuser->user && chan->cacheuser->user->x.x.access == BOTLEVEL) + return(chan->cacheuser); + + for(cu=chan->users;cu;cu=cu->next) + { + if (cu->user && cu->user->x.x.access == BOTLEVEL) + { + if (!nickcmp(nick,cu->nick)) + return(chan->cacheuser = cu); + } + } + return(NULL); +} + +void check_botjoin(Chan *chan, ChanUser *cu) +{ + BotNet *bn; + BotInfo *binfo; + +#ifdef DEBUG + debug("(check_botjoin) chan = %s; cu = %s!%s\n",chan->name,cu->nick,cu->userhost); +#endif /* DEBUG */ + + for(bn=botnetlist;bn;bn=bn->next) + { + if (bn->status != BN_LINKED) + continue; + + for(binfo=bn->botinfo;binfo;binfo=binfo->next) + { + if (!nickcmp(cu->nick,binfo->nuh) && + !Strcasecmp(cu->userhost,getuh(binfo->nuh))) + { + if ((cu = find_chanbot(chan,binfo->nuh)) == NULL) + return; + cu->flags |= CU_NEEDOP; + send_mode(chan,50,QM_CHANUSER,'+','o',(void*)cu); +#ifdef DEBUG + debug("(check_botjoin) CU_NEEDOP set, mode pushed\n"); +#endif /* DEBUG */ + return; + } + } + } +} + +void check_botinfo(BotInfo *binfo, const char *channel) +{ + Chan *chan; + ChanUser *cu; + Mech *backup; + char *userhost; + + userhost = getuh(binfo->nuh); + + backup = current; + for(current=botlist;current;current=current->next) + { + for(chan=current->chanlist;chan;chan=chan->next) + { + if (channel && Strcasecmp(channel,chan->name)) + continue; + if ((cu = find_chanbot(chan,binfo->nuh)) == NULL) + continue; + if (!Strcasecmp(cu->userhost,userhost)) + { + cu->flags |= CU_NEEDOP; + send_mode(chan,50,QM_CHANUSER,'+','o',(void*)cu); + } + } + } + current = backup; +} + +/* + * + * protocol routines + * + */ + +void netchanNeedop(BotNet *source, char *rest) +{ + BotNet *bn; + BotInfo *binfo; + char *channel; + int guid; + + guid = a2i(chop(&rest)); + channel = chop(&rest); + if (errno || guid < 1 || !channel) + return; + + botnet_relay(source,"CO%i %s\n",guid,channel); + + for(bn=botnetlist;bn;bn=bn->next) + { + if (bn->status != BN_LINKED) + continue; + for(binfo=bn->botinfo;binfo;binfo=binfo->next) + { + if (binfo->guid == guid) + check_botinfo(binfo,channel); + } + } +} + +#endif /* BOTNET */ diff --git a/src/note.c b/src/note.c new file mode 100644 index 0000000..302f5e4 --- /dev/null +++ b/src/note.c @@ -0,0 +1,215 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define NOTE_C +#include "config.h" + +#ifdef NOTE + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +int catch_note(char *from, char *to, char *rest) +{ + User *u; + Note *n,**pp; + Strp *sp,**np; + +#ifdef DEBUG + debug("(catch_note) from = %s, to = %s, rest = %s\n",from,to,rest); +#endif /* DEBUG */ + + pp = ¬elist; + while(*pp) + { + n = *pp; +#ifdef DEBUG + debug("(catch_note) n->from = %s, n->to = %s\n",n->from,n->to); +#endif /* DEBUG */ + if (!Strcasecmp(from,n->from) && !Strcasecmp(to,n->to)) + { +#ifdef DEBUG + debug("(catch_note) note to user = %s\n",n->user); +#endif /* DEBUG */ + if (!Strcasecmp(rest,".")) + { + to_user(from,"Note for %s has been saved",n->user); + *pp = n->next; + Free((char**)&n); + return(TRUE); + } + if (!(u = find_handle(n->user))) + return(TRUE); + np = &u->note; + while(*np) + np = &(*np)->next; + set_mallocdoer(catch_note); + *np = sp = (Strp*)Calloc(sizeof(Strp) + strlen(rest)); + /* Calloc sets to zero sp->next = NULL; */ + Strcpy(sp->p,rest); + return(TRUE); + } + if ((now - n->start) > 120) + { + *pp = n->next; + Free((char**)&n); + return(TRUE); + } + pp = &(*pp)->next; + } + return(FALSE); +} + +/* + * + * + * + */ + +void do_note(COMMAND_ARGS) +{ + User *u; + Note *n; + Strp *sp,**np; + char header[MSGLEN]; + + /* + * no-args is handled in on_msg() + */ + if (!(u = find_handle(rest))) + { + to_user(from,TEXT_UNKNOWNUSER,rest); + return; + } + to_user(from,"Enter your note for %s, end with \".\" on a line by itself", + u->name); + + set_mallocdoer(do_note); + n = Calloc(sizeof(Note) + Strlen(from,to,u->name,NULL)); + n->start = now; + n->next = notelist; + notelist = n; + + /* + * custom Strcat makes it sooooo easy + */ + n->to = Strcat(n->from,from) + 1; + n->user = Strcat(n->to,to) + 1; + Strcpy(n->user,rest); + + /* + * add a note header + */ + sprintf(header,"\001%s %s",from,time2str(now)); + np = &u->note; + while(*np) + np = &(*np)->next; + set_mallocdoer(do_note); + *np = sp = (Strp*)Calloc(sizeof(Strp) + strlen(header)); + /* Calloc sets to zero sp->next = NULL; */ + Strcpy(sp->p,header); +} + +void do_read(COMMAND_ARGS) +{ + Strp *sp,**pp; + User *user; + char *opt,*sender; + int which,n,sz; + + if (CurrentDCC) + user = CurrentUser; + else + if ((user = get_authuser(from,ANY_CHANNEL)) == NULL) + return; + + sz = n = 0; + for(sp=user->note;sp;sp=sp->next) + { + if (*sp->p == 1) + n++; + else + sz += strlen(sp->p); + } + + if (rest && (opt = chop(&rest))) + { + which = a2i(opt); + if (errno || !which) + goto read_usage; + + if (which < -n || which > n) + { + to_user(from,"invalid message number"); + return; + } + + n = 0; + pp = &user->note; + while(*pp) + { + sp = *pp; + if (*sp->p == 1) + n++; + + if (which == n) + { + opt = sp->p + 1; + sender = chop(&opt); + to_user(from,"From: %s on %s",sender,opt); + opt[-1] = ' '; + to_user(from," "); + sp = sp->next; + while(sp && *sp->p != 1) + { + to_user(from,FMT_PLAIN,sp->p); + sp = sp->next; + } + to_user(from," "); + return; + } + else + if (which == -n) + { + while(TRUE) + { + *pp = sp->next; + Free((char**)&sp); + sp = *pp; + if (!sp || *sp->p == 1) + break; + } + to_user(from,"message number %i has been deleted",-which); + return; + } + } + } + + to_user(from,(n) ? "you have %i message%s (%i bytes)" : "you have no messages",n,(n == 1) ? "" : "s",sz); + return; +read_usage: + usage(from); /* usage for CurrentCmd->name */ +} + +#endif /* NOTE */ diff --git a/src/notify.c b/src/notify.c new file mode 100644 index 0000000..ae4b3ac --- /dev/null +++ b/src/notify.c @@ -0,0 +1,755 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define NOTIFY_C +#include "config.h" + +#ifdef NOTIFY + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" + +#define CHOOSE_NUMBER 25 +#define CHOOSE_MOVETO (CHOOSE_NUMBER - 2) + +#define LOGFILENAMEFMT "notify-guid%i.log" +#define LOGFILENAMEBUF 32 /* need to recalculate this if LOGFILENAMEFMT is changed */ + +LS Notify **endoflist; +LS int lock_ison = FALSE; + +void purge_notify(void) +{ + Notify *nf; + nfLog *nlog; + + /* + * empty the notifylist + */ + while(current->notifylist) + { + nf = current->notifylist; + current->notifylist = nf->next; + while(nf->log) + { + nlog = nf->log; + nf->log = nlog->next; + Free((char**)&nlog); + } + Free((char**)&nf); + } +} + +int mask_check(Notify *nf, char *userhost) +{ + char *mask,*rest; + int ret; + + /* + * no mask (NULL) + */ + if (!nf->mask) + return(NF_MASKONLINE); + + ret = NF_NOMATCH; + if (nf->endofmask) + { + /* + * multiple masks separated by spaces + */ + mask = rest = nf->mask; + chop(&rest); + } + else + { + /* + * single mask + */ + if (!matches(nf->mask,userhost)) + ret = NF_MASKONLINE; + return(ret); + } + while(mask) + { + if (!matches(mask,userhost)) + { + ret = NF_MASKONLINE; + break; + } + mask = chop(&rest); + } + if (nf->endofmask) + { + /* + * undo the chop()'s + */ + for(mask=nf->mask;maskendofmask;mask++) + { + if (*mask == 0) + *mask = ' '; + } + } + return(ret); +} + +void send_ison(void) +{ + Notify *nf,*chosen[CHOOSE_NUMBER]; + char isonmsg[MSGLEN]; + char *p,*src; + int i,x,period; + + /* + * dont send nicks to ISON too often + */ + period = current->setting[INT_ISONDELAY].int_var; + x = now - current->lastnotify; + if ((x < period) || (lock_ison && (x < 600))) + return; + + current->lastnotify = now; + + /* + * the nature of the code makes it so that the first NULL is + * pushed further down the list as more entries are added, + * so no need to blank the whole list + */ + chosen[0] = NULL; + + /* + * select the oldest (CHOOSE_NUMBER) nicks for an update + */ + for(nf=current->notifylist;nf;nf=nf->next) + { + for(i=0;ichecked > nf->checked)) + { + for(x=CHOOSE_MOVETO;x>=i;x--) + chosen[x+1] = chosen[x]; + chosen[i] = nf; + break; + } + } + } + if (chosen[0]) + { + p = isonmsg; + for(i=0;ichecked = 1; + src = chosen[i]->nick; + if (i) *(p++) = ' '; + while((*p = *(src++))) p++; + } + to_server("ISON :%s\n",isonmsg); + lock_ison = TRUE; + } +} + +void catch_ison(char *rest) +{ + Notify *nf; + char whoismsg[MSGLEN]; + char *nick,*dst; + + lock_ison = FALSE; + *whoismsg = 0; + dst = whoismsg; + while((nick = chop(&rest))) + { + for(nf=current->notifylist;nf;nf=nf->next) + { + if (!nickcmp(nf->nick,nick)) + { + nf->checked = now; + /* + * /whois user to get user@host + realname + */ + if (nf->status == NF_OFFLINE) + { + if (*whoismsg) *(dst++) = ','; + *dst = 0; + dst = Strcat(dst,nf->nick); + nf->status = NF_WHOIS; + } + } + } + } + + if (*whoismsg) + to_server("WHOIS %s\n",whoismsg); + + for(nf=current->notifylist;nf;nf=nf->next) + { + if (nf->checked == 1) + { + nf->checked = now; + if (nf->status >= NF_WHOIS) + { + /* + * close the log entry for this online period + */ + if (nf->log && nf->log->signon && !nf->log->signoff) + nf->log->signoff = now; + /* + * announce that the user is offline if its a mask match + */ + if (nf->status == NF_MASKONLINE) + send_spy(SPYSTR_STATUS,"Notify: %s is offline",nf->nick); + nf->status = NF_OFFLINE; + } + } + } +} + +void catch_whois(char *nick, char *userhost, char *realname) +{ + Notify *nf; + nfLog *nlog; + + if (!realname || !*realname) + realname = "unknown"; + + for(nf=current->notifylist;nf;nf=nf->next) + { + if ((nf->status == NF_WHOIS) && !nickcmp(nf->nick,nick)) + { + /* + * put in a new log entry + */ + set_mallocdoer(catch_whois); + nlog = (nfLog*)Calloc(sizeof(nfLog) + Strlen2(userhost,realname)); + nlog->signon = now; + nlog->next = nf->log; + nf->log = nlog; + nlog->realname = Strcat(nlog->userhost,userhost) + 1; + Strcpy(nlog->realname,realname); + /* + * if there is a mask, we need a match to announce the online status + */ + nf->status = mask_check(nf,userhost); + if (nf->status == NF_MASKONLINE) + send_spy(SPYSTR_STATUS,"Notify: %s (%s) is online",nf->nick,userhost); + return; + } + } +} + +/* + * + * saving and loading notify information + * + */ + +int notifylog_callback(char *rest) +{ + Notify *nf; + nfLog *nlog,**pp; + time_t on,off; + char *nick,*userhost; + + if (*rest == COMMENT_CHAR) + return(FALSE); + + nick = chop(&rest); + + on = a2i(chop(&rest)); + if (errno) + return(FALSE); + + off = a2i(chop(&rest)); + if (errno) + return(FALSE); + + userhost = chop(&rest); + + if (rest && *rest == ':') + rest++; + if (!*rest) + return(FALSE); + + for(nf=current->notifylist;nf;nf=nf->next) + { + if (!nickcmp(nick,nf->nick)) + { + pp = &nf->log; + while(*pp) + { + if ((*pp)->signon < on) + break; + pp = &(*pp)->next; + } + set_mallocdoer(notifylog_callback); + nlog = (nfLog*)Calloc(sizeof(nfLog) + Strlen2(userhost,rest)); + nlog->signon = on; + nlog->signoff = off; + nlog->next = *pp; + *pp = nlog; + nlog->realname = Strcat(nlog->userhost,userhost) + 1; + Strcpy(nlog->realname,rest); + break; + } + } + return(FALSE); +} + +void read_notifylog(void) +{ + char fname[LOGFILENAMEBUF]; + int fd; +#ifdef DEBUG + int dd; +#endif /* DEBUG */ + + sprintf(fname,LOGFILENAMEFMT,current->guid); + if ((fd = open(fname,O_RDONLY)) < 0) + return; +#ifdef DEBUG + dd = dodebug; + dodebug = FALSE; +#endif /* DEBUG */ + + readline(fd,¬ifylog_callback); /* readline closes fd */ + +#ifdef DEBUG + dodebug = dd; +#endif /* DEBUG */ +} + +void write_notifylog(void) +{ + Notify *nf; + nfLog *nlog; + char fname[LOGFILENAMEBUF]; + int fd; +#ifdef DEBUG + int dd; +#endif /* DEBUG */ + + sprintf(fname,LOGFILENAMEFMT,current->guid); + if ((fd = open(fname,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0) + return; + +#ifdef DEBUG + dd = dodebug; + dodebug = FALSE; +#endif /* DEBUG */ + for(nf=current->notifylist;nf;nf=nf->next) + { + to_file(fd,COMMENT_STRCHR "\n" COMMENT_STRCHR " Nick: %s\n",nf->nick); + if (nf->info) + to_file(fd,COMMENT_STRCHR " Note: %s\n",nf->info); + if (nf->mask) + to_file(fd,COMMENT_STRCHR " Mask: %s\n",nf->mask); + to_file(fd,COMMENT_STRCHR "\n"); + for(nlog=nf->log;nlog;nlog=nlog->next) + { + to_file(fd,"%s %lu %lu %s :%s\n",nf->nick,nlog->signon, + (nlog->signoff) ? nlog->signoff : now, + nlog->userhost,nlog->realname); + } + } + close(fd); +#ifdef DEBUG + dodebug = dd; +#endif /* DEBUG */ +} + + +int notify_callback(char *rest) +{ + Notify *nf; + char *nick; + char *src,*dst; + char *lspace; + + errno = EINVAL; + + if (!rest || *rest == COMMENT_CHAR) + return(FALSE); + fix_config_line(rest); + if ((nick = chop(&rest)) == NULL) + return(FALSE); + +#ifdef DEBUG + debug("(notify_callback) parsing %s `%s'\n",nick,nullstr(rest)); +#endif /* DEBUG */ + + lspace = rest - 1; + src = dst = rest; + while(*src) + { + if (*src == ' ') + { + if (!lspace) + { + lspace = dst; + *(dst++) = *src; + } + src++; + } + else + if (*src == ':' && lspace) + { + *lspace = 0; + break; + } + else + { + lspace = NULL; + *(dst++) = *(src++); + } + } + + if (*src == ':') + *(src++) = 0; + if (!*src) + src = NULL; + +#ifdef DEBUG + debug("(notify_callback) creating struct\n"); +#endif /* DEBUG */ + + /* + * nick = nick + * rest = mask(s) or *rest == 0 + * src = description or NULL + */ + set_mallocdoer(notify_callback); + nf = (Notify*)Calloc(sizeof(Notify) + Strlen(nick,rest,src,NULL)); + dst = Strcat(nf->nick,nick); + if (*rest) + { + nf->mask = dst + 1; + dst = Strcat(nf->mask,rest); + if (STRCHR(nf->mask,' ')) + nf->endofmask = dst; + } + if (src) + { + nf->info = dst + 1; + Strcpy(nf->info,src); + } + /* put it at the end of notifylist */ + *endoflist = nf; + endoflist = &nf->next; + + errno = 0; + return(FALSE); +} + +int read_notify(char *filename) +{ + int fd; + + if (!filename) + return(FALSE); + + if ((fd = open(filename,O_RDONLY)) < 0) + return(FALSE); + + /* + * save online logs + */ + if (current->notifylist) + write_notifylog(); + + /* + * delete the whole list + */ + purge_notify(); + + endoflist = ¤t->notifylist; + readline(fd,¬ify_callback); /* readline closes fd */ + + /* + * read back online logs + */ + read_notifylog(); + + return(TRUE); +} + +/* + * + * commands... + * + */ + +#define NF_OPTIONS 7 + +LS const char notify_opt[NF_OPTIONS][10] = +{ +"-ALL", +"-NOMATCH", +"-RELOAD", +"-FULL", +"-SEEN", +}; + +#define NFF_ALL 1 +#define NFF_NOMATCH 2 +#define NFF_RELOAD 4 +#define NFF_FULL 8 +#define NFF_SEEN 16 + +LS char *nf_from; +LS int nf_header; + +void nfshow_brief(Notify *nf) +{ + time_t when; + char mem[40]; + char *s; + int d,h,m; + + if (nf->status == NF_NOMATCH) + s = " nickname in use"; + else + if (nf->status >= NF_WHOIS) + s = " online now"; + else + if (nf->log && nf->log->signoff) + { + s = mem; + when = now - nf->log->signoff; + d = when / 86400; + h = (when -= d * 86400) / 3600; + m = (when -= h * 3600) / 60; + sprintf(mem,"%2i day%1s %02i:%02i ago",d,EXTRA_CHAR(d),h,m); + } + else + s = " never seen"; + + if (!nf_header) + to_user(nf_from,"\037nick\037 \037last seen\037 \037note\037"); + to_user(nf_from,(nf->info) ? "%-9s %-22s %s" : "%-9s %s", + nf->nick,s,nf->info); + nf_header++; +} + +void nfshow_full(Notify *nf) +{ + char mem[MSGLEN]; + nfLog *nlog; + char *s,*opt; + + if (nf_header) + to_user(nf_from," "); + to_user(nf_from,(nf->status == NF_MASKONLINE) ? "Nick: \037%s\037" : "Nick: %s",nf->nick); + if (nf->info) + to_user(nf_from,"Note: %s",nf->info); + if (nf->mask) + to_user(nf_from,"Mask: %s",nf->mask); + if (nf->log) + { + to_user(nf_from,"Online history:"); + for(nlog=nf->log;nlog;nlog=nlog->next) + { + opt = mem; + s = time2away(nlog->signon); + if (s[1] == ':') + *(opt++) = ' '; + *opt = 0; + opt = Strcat(opt,s); + while(opt < (mem+18)) + *(opt++) = ' '; + *opt = 0; + opt = Strcat(opt," -- "); + if (nlog->signoff) + { + s = time2away(nlog->signoff); + if (s[1] == ':') + *(opt++) = ' '; + *opt = 0; + } + else + { + s = " online now"; + } + opt = Strcat(opt,s); + while(opt < (mem+41)) + *(opt++) = ' '; + *opt = 0; + s = (nlog->realname) ? "%s: %s (%s)" : "%s: %s"; + to_user(nf_from,s,mem,nlog->userhost,nlog->realname); + } + } + nf_header++; +} + +void sub_notifynick(char *from, char *rest) +{ + Notify *nf,**pp; + char *nick; + int i; + + nick = chop(&rest); + if (!nick) + { + usage(from); + return; + } + i = 0; + pp = ¤t->notifylist; + while(*pp) + { + nf = *pp; + if (!nickcmp(nick,nf->nick)) + { + *pp = nf->next; + Free((char**)&nf); + i++; + } + else + { + pp = &nf->next; + } + } + if (!i) + to_user(from,"Nick not found: %s",nick); + else + to_user(from,"Nick removed from notify: %s",nick); +} + +void do_notify(COMMAND_ARGS) +{ + char message[MSGLEN]; + Notify *nf; + nfLog *nlog; + char *opt; + int n,flags; + + nf_from = from; + flags = nf_header = 0; + *message = 0; + + if (*rest) + { + while((opt = chop(&rest))) + { + if (!Strcmp(opt,"+")) + { + endoflist = ¤t->notifylist; + while(*endoflist) + endoflist = &(*endoflist)->next; + notify_callback(rest); + return; + } + if (!Strcmp(opt,"-")) + { + sub_notifynick(from,rest); + return; + } + for(n=0;n=NF_OPTIONS) + { + for(nf=current->notifylist;nf;nf=nf->next) + { + if (!nickcmp(opt,nf->nick)) + { + if (flags & NFF_FULL) + nfshow_full(nf); + else + nfshow_brief(nf); + break; + } + } + if (!nf) + { + if (*message) + Strcat(message,", "); + Strcat(message,opt); + } + nf_header++; + } + } + } + + if (*message) + { +#ifdef DEBUG + debug("(do_notify) dumping errnames\n"); +#endif /* DEBUG */ + to_user(from,"User%s not found: %s",(STRCHR(message,',')) ? "s" : "",message); + } + + if (nf_header) + return; + + if (flags & NFF_RELOAD) + { + opt = current->setting[STR_NOTIFYFILE].str_var; + if (opt && read_notify(opt)) + { + flags = get_useraccess(from,""); + send_spy(SPYSTR_STATUS,"Notify: %s reloaded by %s[%i]", + opt,CurrentNick,flags); + to_user(from,"notify list read from file %s",opt); + } + else + { + to_user(from,"notify list could not be read from file %s",nullstr(opt)); + } + return; + } + + for(nf=current->notifylist;nf;nf=nf->next) + { + if ((nf->status == NF_MASKONLINE) || (flags & NFF_ALL) || ((flags & NFF_NOMATCH) && (nf->status == NF_NOMATCH))) + goto show_it; + if ((flags & NFF_SEEN) && nf->log) + { + for(nlog=nf->log;nlog;nlog=nlog->next) + { + if (mask_check(nf,nlog->userhost) == NF_MASKONLINE) + goto show_it; + } + } + continue; + show_it: + if (flags & NFF_FULL) + nfshow_full(nf); + else + nfshow_brief(nf); + } + if (!nf_header) + { + to_user(from,"no notify users are online"); + } +} + +#endif /* NOTIFY */ diff --git a/src/ons.c b/src/ons.c new file mode 100644 index 0000000..2762ecf --- /dev/null +++ b/src/ons.c @@ -0,0 +1,1163 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define COM_ONS_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * :nick!user@host KICK #channel kicknick :message + */ +void on_kick(char *from, char *rest) +{ + Chan *chan; + ChanUser *doer,*victim; + char *channel,*nick; + + channel = chop(&rest); + if ((CurrentChan = chan = find_channel_ac(channel)) == NULL) + return; + + nick = chop(&rest); + if (rest && *rest == ':') + rest++; + + nickcpy(CurrentNick,from); + + if (current->spy & SPYF_CHANNEL) + send_spy(chan->name,"*** %s was kicked by %s (%s)",nick,CurrentNick,rest); + + if (!nickcmp(current->nick,nick)) + { +#ifdef DEBUG + debug("(on_kick) I was kicked from %s\n",chan->name); +#endif /* DEBUG */ + Free(&chan->kickedby); + set_mallocdoer(on_kick); + chan->kickedby = Strdup(from); + chan->active = FALSE; + chan->sync = TRUE; + chan->bot_is_op = FALSE; + join_channel(chan->name,chan->key); + /* + * if we're kicked from the active channel, we need to find a new + * channel to set as the active one. + */ + if (chan == current->activechan) + { + for(chan=current->chanlist;chan;chan=chan->next) + { + if (chan->active) + break; + } + current->activechan = chan; + /* + * Might be set to NULL now, but its supposed to be checked whenever used. + * If not, we get a SEGV; and fix it. + */ + } +#ifdef STATS + if (chan && chan->stats) + chan->stats->flags |= CSTAT_PARTIAL; +#endif /* STATS */ + /* + * this is the only return we can do: its the bot itself! + * channel userlist will be reconstructed on rejoin + */ + return; + } + +#ifdef STATS + if (chan->setting[STR_STATS].str_var) + stats_plusminususer(chan,-1); + if (chan->stats) + chan->stats->kicks++; +#endif /* STATS */ + + /* + * making life easy for ourselves + */ + victim = find_chanuser(chan,nick); + doer = NULL; + + if (chan->bot_is_op) + { + /* + * are we supposed to check for channel mass kicks? + */ + if (chan->setting[INT_MPL].int_var) + { + doer = find_chanuser(chan,from); + if (check_mass(chan,doer,INT_MKL)) + mass_action(chan,doer); + } + /* + * are we supposed to protect users? + */ + if (chan->setting[INT_PROT].int_var) + { + if (victim->user && victim->user->x.x.prot) + { + /* + * doer might be NULL, prot_action() handles it + */ + prot_action(chan,from,doer,NULL,victim); + to_server("INVITE %s %s\n",nick,channel); + } + } + } + + /* + * cant delete users who arent there + */ + if (victim) + { +#ifdef SEEN + make_seen(nick,victim->userhost,from,rest,now,SEEN_KICKED); +#endif /* SEEN */ + + /* + * Dont delete the poor sod before all has been processed + */ + remove_chanuser(chan,nick); + } +} + +void on_join(Chan *chan, char *from) +{ + Ban *ban; + ChanUser *cu; + char *channel; + int vpri; + + /* + * Satisfy spies before we continue... + */ + if (current->spy & SPYF_CHANNEL) + send_spy(chan->name,"*** Joins: %s (%s)",CurrentNick,getuh(from)); + /* + * + */ +#ifdef GREET + if (!CurrentShit && CurrentUser && CurrentUser->greet) + greet(); +#endif /* GREET */ + /* + * No further actions to be taken if the bot isnt opped + */ + if (!chan->bot_is_op) + return; + + channel = chan->name; + cu = chan->users; + + /* + * Some stuff only applies to non-users + */ + if (!CurrentUser) + { + /* + * Kick banned (desynched) users if ABK is set + */ + if (chan->setting[TOG_ABK].int_var) + { + for(ban=chan->banlist;ban;ban=ban->next) + { + if (!matches(ban->banstring,from)) + break; + } + if (ban) + { + send_kick(chan,CurrentNick,KICK_BANNED); + return; + } + } + /* + * Kickban users with control chars in their ident + * (which doesnt violate RFC1413 but is bloody annoying) + */ + if (chan->setting[TOG_CTL].int_var) + { + if (STRCHR(from,'\031') || STRCHR(from,'\002') || STRCHR(from,'\022') || STRCHR(from,'\026')) + { + deop_siteban(chan,cu); + send_kick(chan,CurrentNick,KICK_BAD_IDENT); + return; + } + } + } + /* + * If they're shitted, they're not allowed to be opped or voiced + */ + if (CurrentShit) + { + shit_action(chan,cu); + return; + } + /* + * Check for +ao users if AOP is toggled on + */ + if (chan->setting[TOG_AOP].int_var) + { + if (cu->user && cu->user->x.x.aop) + { + send_mode(chan,140,QM_CHANUSER,'+','o',(void*)cu); + return; + } + } + /* + * If AVOICE eq 0 we have nothing more to do + */ + vpri = 200; + switch(chan->setting[INT_AVOICE].int_var) + { + case 1: + vpri = 150; + if (cu->user && cu->user->x.x.avoice) + break; + /* fall through */ + case 0: + return; + } + send_mode(chan,vpri,QM_CHANUSER,'+','v',(void*)cu); +} + +void on_nick(char *from, char *newnick) +{ + ChanUser *cu; + Chan *chan; + char newnuh[NUHLEN]; + int maxcount; + int isbot; + + nickcpy(CurrentNick,from); + +#ifdef FASTNICK + /* + * grab the nick *RIGHT NOW* + * this is a setting because this is risky, you might get collided as a result + */ + if (!nickcmp(CurrentNick,current->wantnick)) + to_server("NICK %s\n",current->wantnick); +#endif /* FASTNICK */ + + /* + * make the new From string + */ + sprintf(newnuh,"%s!%s",newnick,getuh(from)); + +#ifdef SEEN + make_seen(CurrentNick,from,newnick,NULL,now,SEEN_NEWNICK); +#endif /* SEEN */ + + /* + * snooping buggers + */ + if (current->spy & SPYF_CHANNEL) + send_spy(MATCH_ALL,"*** %s is now known as %s",CurrentNick,newnick); + + change_authnick(from,newnuh); + + if ((isbot = !nickcmp(current->nick,CurrentNick))) + { + setbotnick(current,newnick); + } + + for(chan=current->chanlist;chan;chan=chan->next) + { + if ((cu = find_chanuser(chan,from)) == NULL) + continue; + + /* + * only need to realloc the buffer if its too small + */ + if (strlen(cu->nick) >= strlen(newnick)) + { + Strcpy(cu->nick,newnick); + } + else + { + Free((char**)&cu->nick); + set_mallocdoer(on_nick); + cu->nick = Strdup(newnick); + } + + /* + * if the bot isnt opped, there's nothing more to do + */ + if (!chan->bot_is_op) + continue; + + /* + * if its the current bot, we dont do diddly squat + */ + if (isbot) + continue; + + shit_action(chan,cu); + + /* + * check for nick-change-flood + */ + if ((maxcount = chan->setting[INT_NCL].int_var) < 2) + continue; + + if ((now - cu->nicktime) > NICKFLOODTIME) + { + cu->nicktime = now + (NICKFLOODTIME / (maxcount - 1)); + cu->nicknum = 1; + } + else + { + cu->nicktime += (NICKFLOODTIME / (maxcount - 1)); + if (++cu->nicknum >= maxcount) + { + deop_ban(chan,cu,NULL); + send_kick(chan,newnick,KICK_NICKFLOOD); + } + } + } +} + +void on_msg(char *from, char *to, char *msg) +{ +#ifdef SCRIPTING + Hook *hook; +#endif /* SCRIPTING */ +#ifdef ALIAS + char amem[MSGLEN]; /* big buffers at the top */ + Alias *alias; + int arec; +#endif /* ALIAS */ +#ifdef REDIRECT + char *orig_to; +#endif /* REDIRECT */ + char *pt,*origstart,*command; + uchar *p1,*p2; + int has_cc,has_bang; + int uaccess; + int i,j; + + /* + * No line sent to this routine should be longer than MSGLEN + * Callers responsibility to check that from, to and msg is + * non-NULL and non-zerolength + */ + +#ifdef NOTE + if (notelist && catch_note(from,to,msg)) + return; +#endif /* NOTE */ + + /* + * If the message is for a channel and we dont accept + * public commands, we can go directly to common_public() + */ + if (CurrentChan && !CurrentChan->setting[TOG_PUB].int_var) + { + common_public(CurrentChan,from,"<%s> %s",msg); + return; + } + + if (CurrentDCC) + { + uaccess = CurrentUser->x.x.access; + } + else + if ((uaccess = get_authaccess(from,NULL)) > OWNERLEVEL) + { + /* + * If its a bot we want nothing to do with it + */ + return; + } + + /* + * remember where the string started + */ + origstart = msg; + + if (from == CoreUser.name) + { + has_cc = TRUE; + } + else + { + has_cc = (current->setting[TOG_CC].int_var) ? FALSE : TRUE; + } + + /* + * check for command bots nick replacing command char + */ + if ((p2 = (uchar*)(command = chop(&msg))) == NULL) + return; + + p1 = (uchar*)current->nick; + while(!(i = tolowertab[*(p1++)] - tolowertab[*p2]) && *(p2++)) + ; + + if (!i || ((p2 > (uchar*)command) && (*p2 == ':' || *p2 == ';' || *p2 == ',') && p2[1] == 0)) + { + if ((command = chop(&msg)) == NULL) + return; + has_cc = TRUE; + } + + has_bang = FALSE; + if (*command == current->setting[CHR_CMDCHAR].char_var) + { + has_cc = TRUE; + command++; + } + else + if (!has_cc && *command == '!') + { + has_bang = TRUE; + command++; + } + +#ifdef ALIAS + arec = 0; +recheck_alias: +#endif /* ALIAS */ + +#ifdef ALIAS + for(alias=aliaslist;alias;alias=alias->next) + { + if (!Strcasecmp(alias->alias,command)) + { + afmt(amem,alias->format,msg); +#ifdef DEBUG + debug("(on_msg) [ALIAS] %s %s --> %s\n",command,msg,amem); +#endif /* DEBUG */ + msg = amem; + pt = chop(&msg); + i = Strcasecmp(pt,command); + command = pt; + arec++; + if ((arec < MAXALIASRECURSE) && (i != 0)) + goto recheck_alias; + } + } +#endif /* ALIAS */ + +#ifdef REDIRECT + orig_to = to; +#endif /* REDIRECT */ + + i = 0; +#ifdef SCRIPTING + for(hook=hooklist;hook;hook=hook->next) + { + /* + * check if the hook applies to this particular bot + */ + if (hook->guid && hook->guid != current->guid) + continue; + /* + * does the hook match? + */ + if (hook->flags == HOOK_COMMAND && !Strcasecmp(command,hook->type.command)) + { + if (hook->func(from,msg,hook)) + /* if the hook returns non-zero, the input should not be parsed internally */ + i = 1; + } + } + if (i) return; +#endif /* SCRIPTING */ + + /* + * match "command" against internal command list + */ + for(;mcmd[i].name;i++) + { + if (!has_cc && mcmd[i].cc && !(has_bang && mcmd[i].cbang)) + continue; + if (uaccess < acmd[i]) + continue; + j = Strcasecmp(mcmd[i].name,command); + if (j < 0) + continue; + if (j > 0) + break; + +#if defined(BOTNET) && defined(REDIRECT) + if (mcmd[i].nocmd && redirect.to) + return; +#endif /* BOTNET && REDIRECT */ + + if (mcmd[i].nopub && CurrentChan) + { +#ifdef DEBUG + debug("(on_msg) Public command (%s) ignored\n",command); +#endif /* DEBUG */ + return; + } + CurrentCmd = &mcmd[i]; + + /* + * convert the command to uppercase + */ + Strcpy(command,mcmd[i].name); + + /* + * send statmsg with info on the command executed + */ + if (current->setting[TOG_SPY].int_var) + { + send_spy(SPYSTR_STATUS,":%s[%i]: Executing %s[%i]", + CurrentNick,uaccess,command,(int)acmd[i]); + } + + /* + * list of last LASTCMDSIZE commands + */ + if (from != CoreUser.name) + { + Free(¤t->lastcmds[LASTCMDSIZE-1]); + for(j=LASTCMDSIZE-2;j>=0;j--) + current->lastcmds[j+1] = current->lastcmds[j]; + if ((pt = STRCHR(from,'@')) == NULL) + pt = from; + set_mallocdoer(on_msg); + current->lastcmds[0] = (char*)Calloc(strlen(pt) + 45); + if (CurrentUser) + { + sprintf(current->lastcmds[0],"[%s] %s\r%s[%-3i]\t(*%s)", + time2medium(now),command,CurrentUser->name, + (CurrentUser->x.x.access),pt); + } + else + { + sprintf(current->lastcmds[0],"[%s] %s\r%s[---]\t(*%s)", + time2medium(now),command,CurrentNick,pt); + } + } + + /* + * CAXS check: first argument might be a channel + * check user access on target channel + */ + if (mcmd[i].caxs) + { + /* get channel name; 1: msg, 2: to, 3: active channel */ + to = get_channel(to,&msg); + if (!ischannel(to)) + return; + uaccess = get_authaccess(from,to); + if (uaccess < acmd[i]) + return; + CurrentChan = find_channel_ac(to); + if (mcmd[i].acchan && !CurrentChan) + { + to_user(from,ERR_CHAN,to); + return; + } + } + else + /* + * GAXS check: user needs global access + */ + if (mcmd[i].gaxs) + { + uaccess = get_authaccess(from,MATCH_ALL); + if (uaccess < acmd[i]) + return; + } + + /* + * CARGS check: at least one argument is required + */ + if (mcmd[i].args && !*msg) + { + if (uaccess) usage_command(from,command); + return; + } + +#ifdef REDIRECT + /* + * can this command be redirected? + */ + if (!redirect.to && mcmd[i].redir) + { + if (mcmd[i].lbuf && ischannel(orig_to)) + { + redirect.to = Strdup(to); + redirect.method = R_PRIVMSG; + } + else + if (begin_redirect(from,msg) < 0) + return; + } +#endif /* REDIRECT */ + + if (mcmd[i].dcc && dcc_only_command(from)) + return; + + mcmd[i].func(from,to,msg,acmd[i]); + +#ifdef DEBUG + CurrentCmd = NULL; +#endif /* DEBUG */ +#ifdef REDIRECT + end_redirect(); +#endif /* REDIRECT */ + + /* + * be quick to exit afterwards, there are "dangerous" commands like DIE and DEL (user) + */ + return; + } + + /* + * un-chop() the message string + */ + unchop(origstart,msg); + + if (CurrentChan) + { + common_public(CurrentChan,from,"<%s> %s",origstart); + } + else + if (has_cc && *command && uaccess) + { + to_user(from,ERR_UNKNOWN_COMMAND); + } + else + if (CurrentDCC) + { + partyline_broadcast(CurrentDCC,"<%s> %s\n",origstart); +#ifdef BOTNET + botnet_relay(NULL,"PM* * %s@%s %s\n",CurrentNick,current->nick,origstart); +#endif /* BOTNET */ + } + else + { + send_spy(SPYSTR_MESSAGE,"<%s> %s",CurrentNick,origstart); + } +} + +void on_mode(char *from, char *channel, char *rest) +{ + Chan *chan; + ChanUser *doer; + ChanUser *victim; + Shit *shit; + char templimit[20]; + char *nick; + char *parm,*nickuh,*mode; + int i,sign,enfm,maxprot; + + if ((chan = find_channel_ac(channel)) == NULL) + return; + channel = chan->name; + + if (current->spy & SPYF_CHANNEL) + send_spy(channel,"*** %s sets mode: %s",CurrentNick,rest); + + maxprot = chan->setting[INT_PROT].int_var; + sign = '+'; + + mode = chop(&rest); + + /* + * might be NULL but we have to handle that due to server modes + */ + doer = find_chanuser(chan,from); + +modeloop: + if (*mode == 'o' || *mode == 'v') + { + nick = chop(&rest); + if ((victim = find_chanuser(chan,nick)) == NULL) + { + mode++; + goto modeloop; + } + } + + switch(*mode) + { + case '+': + case '-': + sign = *mode; + break; + /* + * + * MODE +/-o + * + */ + case 'o': + i = (victim->user) ? victim->user->x.x.access : 0; +/* +o */ if (sign == '+') + { + victim->flags |= CU_CHANOP; + victim->flags &= ~CU_DEOPPED; + if (!i) + { + if (victim->shit || (chan->setting[TOG_SD].int_var && !doer) || + chan->setting[TOG_SO].int_var) + { + send_mode(chan,60,QM_CHANUSER,'-','o',victim); + } + } + else + if (!nickcmp(current->nick,nick)) + { + /* + * wooohoooo! they gave me ops!!! + */ + chan->bot_is_op = TRUE; + if (chan->kickedby) + { + if (chan->setting[TOG_RK].int_var) + send_kick(chan,nickcpy(NULL,chan->kickedby),KICK_REVENGE); + Free(&chan->kickedby); + } + check_shit(); + update_modes(chan); + } +#ifdef DEBUG + debug("(on_mode) %s!%s --> %i\n",victim->nick,victim->userhost,i); +#endif /* DEBUG */ + } +/* -o */ else + { + victim->flags &= ~(CU_CHANOP|CU_DEOPPED); + if (i == BOTLEVEL) + { + if (!nickcmp(current->nick,nick)) + { + /* + * they dont love me!!! :~( + */ + chan->bot_is_op = FALSE; + } + } + /* + * idiots deopping themselves + */ + if (!nickcmp(from,nick)) + break; + /* + * 1. Use enfm var to temporarily store users access + * 2. get_userlevel also checks is_localbot()... + */ + enfm = (doer && doer->user) ? doer->user->x.x.access : 0; + if (enfm == BOTLEVEL) + break; + if (check_mass(chan,doer,INT_MDL)) + mass_action(chan,doer); + if (maxprot && (victim->user && victim->user->x.x.prot) && !victim->shit) + { + /* + * FIXME: does it matter if the user is logged in or not? + */ + nickuh = get_nuh(victim); + if (get_authaccess(nickuh,channel)) + { + send_mode(chan,60,QM_CHANUSER,'+','o',victim); + prot_action(chan,from,doer,NULL,victim); + } + } + } + break; + /* + * + * MODE +/-v + * + */ + case 'v': + if (sign == '+') + victim->flags |= CU_VOICE; + else + victim->flags &= ~CU_VOICE; + break; +#ifdef IRCD_EXTENSIONS +/* +:joonicks!*@* MODE #emech +I *king*!*@* +:joonicks!*@* MODE #emech +e *kong*!*@* +*/ +#endif /* IRCD_EXTENSIONS */ + /* + * + * MODE +/-b + * + */ +#ifdef IRCD_EXTENSIONS + /* + * ircnet braindamage modes + */ + case 'I': + case 'e': +#endif /* IRCD_EXTENSIONS */ + case 'b': + parm = chop(&rest); +/* +b */ if (sign == '+') + { +#ifdef IRCD_EXTENSIONS + Ban *newban; + + newban = make_ban(&chan->banlist,from,parm,now); + if (*mode == 'I') newban->imode = TRUE; + if (*mode == 'e') newban->emode = TRUE; + /* + * I/e modes are low privilige and not checked for protection (yet?) + */ + break; +#else /* IRCD_EXTENSIONS */ + make_ban(&chan->banlist,from,parm,now); +#endif /* IRCD_EXTENSIONS */ + /* + * skip protection checks if the doer is myself or another known bot + */ + if (doer && doer->user && doer->user->x.x.access == BOTLEVEL) + break; + if (check_mass(chan,doer,INT_MBL)) + mass_action(chan,doer); + if (maxprot && get_protaction(chan,parm)) + { + shit = get_shituser(parm,channel); + if (!shit || !shit->action) + { + /* + * FIXME: do we have a CU for the `from' user? -- yes: doer + * bot_is_op checked: no + */ + send_mode(chan,160,QM_RAWMODE,'-','b',parm); + prot_action(chan,from,doer,parm,NULL); + } + } + } +/* -b */ else + { +#ifdef IRCD_EXTENSIONS + if (*mode == 'I' || *mode == 'e') + { + delete_modemask(chan,parm,*mode); + break; + } +#endif /* IRCD_EXTENSIONS */ + delete_ban(chan,parm); + if (!chan->setting[TOG_SHIT].int_var) + break; + + /* whats this??? */ + shit = get_shituser(parm,channel); // calls find_shit? clobbers get_nuh buffer + i = (shit) ? shit->action : 0; + if (i < SHIT_PERMABAN) + { + shit = find_shit(parm,channel); + i = (shit) ? shit->action : 0; + } + if (i == SHIT_PERMABAN) + { + send_mode(chan,160,QM_RAWMODE,'+','b',shit->mask); + } + } + break; + case 'p': + case 's': + case 'm': + case 't': + case 'i': + case 'n': + if (reverse_mode(from,chan,*mode,sign)) + { + send_mode(chan,160,QM_RAWMODE,(sign == '+') ? '-' : '+',*mode,NULL); + } + i = (sign == '+'); + switch(*mode) + { + case 'p': + chan->private = i; + break; + case 's': + chan->secret = i; + break; + case 'm': + chan->moderated = i; + break; + case 't': + chan->topprot = i; + break; + case 'i': + chan->invite = i; + break; + case 'n': + chan->nomsg = i; + break; + } + break; +/* k */ + case 'k': + parm = chop(&rest); + enfm = reverse_mode(from,chan,'k',sign); + if (sign == '+') + { + chan->keymode = TRUE; + /* + * Undernet clueless-coder-kludge + */ + chan->hiddenkey = (parm) ? FALSE : TRUE; + if (enfm && parm) + { + send_mode(chan,160,QM_RAWMODE,'-','k',parm); + } + Free(&chan->key); + set_mallocdoer(on_mode); + chan->key = Strdup((parm) ? parm : "???"); + } + else + { + if (enfm && parm) + { + send_mode(chan,160,QM_RAWMODE,'+','k',parm); + } + chan->keymode = FALSE; + } + break; +/* l */ + case 'l': + if (sign == '+') + { + parm = chop(&rest); + chan->limit = a2i(parm); + if (errno) + chan->limit = 0; + chan->limitmode = TRUE; + } + else + { + chan->limitmode = FALSE; + } + if (reverse_mode(from,chan,'l',sign)) + { + if (sign == '+') + { + send_mode(chan,160,QM_RAWMODE,'-','l',NULL); + } + else + { + + sprintf(templimit,"%i",chan->limit); + send_mode(chan,160,QM_RAWMODE,'+','l',templimit); + } + } + break; + case 0: + return; + } + mode++; + goto modeloop; +} + +void common_public(Chan *chan, char *from, char *spyformat, char *rest) +{ + ChanUser *doer; + + if (current->spy & SPYF_CHANNEL) + send_spy(chan->name,spyformat,CurrentNick,rest); + + if (!chan->bot_is_op) + return; + + doer = find_chanuser(chan,from); + + if (capslevel(rest)) + { + if (check_mass(chan,doer,INT_CKL)) + send_kick(chan,CurrentNick,KICK_CAPS); + } + + if (chan->setting[TOG_KS].int_var) + { + if (!CurrentUser || !CurrentUser->x.x.access) + check_kicksay(chan,doer,rest); + } + + if (check_mass(chan,doer,INT_FL)) + { + if (chan->setting[INT_FPL].int_var > 1) + deop_ban(chan,doer,NULL); + send_kick(chan,CurrentNick,KICK_TEXTFLOOD); + send_spy(SPYSTR_STATUS,"%s kicked from %s for flooding",from,chan->name); + } +} + +void on_action(char *from, char *to, char *rest) +{ + if (CurrentChan) + { + common_public(CurrentChan,from,"* %s %s",rest); + return; + } + if (CurrentDCC) + { + partyline_broadcast(CurrentDCC,"* %s %s\n",rest); +#ifdef BOTNET + botnet_relay(NULL,"PM* * %s@%s \001%s\n",CurrentNick,current->nick,rest); +#endif /* BOTNET */ + return; + } + if (current->spy & SPYF_MESSAGE) + send_spy(SPYSTR_MESSAGE,"* %s %s",CurrentNick,rest); +} + +#ifdef DYNCMD + +/* + * + * + * + */ + +void do_chaccess(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + CARGS + */ + int i,oldaccess,newaccess,uaccess,dis; + char *name,*axs; + + name = chop(&rest); + axs = chop(&rest); + + if (!axs && !name) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + + dis = FALSE; + newaccess = a2i(axs); + if (axs) + { + if (!Strcasecmp(axs,"disable")) + { + dis = TRUE; + newaccess = 100; + } + else + { + if (errno || (newaccess < 0) || (newaccess > OWNERLEVEL)) + { + to_user(from,"Command access level must be between 0 and %i",OWNERLEVEL); + return; + } + } + } + + uaccess = get_useraccess(from,ANY_CHANNEL); + if (newaccess > uaccess) + { + to_user(from,"Can't change access level to one higher than yours"); + return; + } + if (dis && uaccess < OWNERLEVEL) + { + to_user(from,"Insufficient access to disable commands"); + return; + } + + for(i=0;mcmd[i].name;i++) + { + if (!Strcasecmp(mcmd[i].name,name)) + { + oldaccess = acmd[i]; + if (dis || oldaccess > 200) + { + to_user(from,"The command \"%s\" has been permanently disabled",name); + acmd[i] = 250; /* unsigned char, max 255 */ + return; + } + if (newaccess == -1) + { + to_user(from,"The access level needed for that command is %i",oldaccess); + to_user(from,"To change it, specify new access level"); + return; + } + if (oldaccess > uaccess) + { + to_user(from,"Can't change an access level that is higher than yours"); + return; + } + if (oldaccess == newaccess) + to_user(from,"The access level was not changed"); + else + to_user(from,"Command access level changed from %i to %i",oldaccess,newaccess); + acmd[i] = newaccess; + return; + } + } + to_user(from,"Unknown command: %s",name); +} + +#endif /* DYNCMD */ + +int access_needed(char *name) +{ + int i; + + for(i=0;mcmd[i].name;i++) + { + if (!Strcasecmp(mcmd[i].name,name)) + { + return(acmd[i]); + } + } + return(-1); +} + +void do_last(COMMAND_ARGS) +{ + char *pt,*thenum; + int i,num; + + if (!rest || !*rest) + num = 5; + else + { + thenum = chop(&rest); + num = a2i(thenum); + } + if ((num < 1) || (num > LASTCMDSIZE)) + usage(from); /* usage for CurrentCmd->name */ + else + { + pt = (char*)1; + table_buffer(TEXT_LASTHDR,num); + for(i=0;ilastcmds[i]; + table_buffer(FMT_PLAIN,(pt) ? pt : TEXT_NONE); + } + table_send(from,1); + } +} diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..fbb2710 --- /dev/null +++ b/src/parse.c @@ -0,0 +1,1497 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define PARSE_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" + +void parse_error(char *from, char *rest) +{ + Server *sp; + + if (rest && (sp = find_server(current->server))) + { + if (!matches("*no authorization*",rest)) + sp->err = SP_NOAUTH; + else + if (!matches("*k*lined*",rest)) + sp->err = SP_KLINED; + else + if (!matches("*throttled*",rest)) + sp->err = SP_THROTTLED; + else + if (!matches("*connection*class*",rest)) + sp->err = SP_FULLCLASS; + else + if (!matches("*different port*",rest)) + sp->err = SP_DIFFPORT; + else + sp->err = SP_NULL; + } + close(current->sock); + current->sock = -1; +} + +void parse_invite(char *from, char *rest) +{ + char *chan; + int i; + + if ((chan = chop(&rest)) == NULL) + return; + if (*chan == ':') + chan++; + i = get_authaccess(from,chan); + if ((i >= JOINLEVEL) && (i < BOTLEVEL)) + { + join_channel(chan,NULL); + current->lastrejoin = now; + } +} + +void parse_join(char *from, char *rest) +{ + Chan *chan; + char *src,*dst; + + /* + * with the new reset feature its theoretically possible that the bot + * can see a join before it knows it is on the channel + * otherwise this would only happen if there was a bug somewhere + */ + if ((CurrentChan = chan = find_channel_ny(rest)) == NULL) + return; + + if (!nickcmp(current->nick,from)) + { +#ifdef DEBUG + debug("(parse_join) Im joining %s\n",chan->name); +#endif /* DEBUG */ + /* + * reset various flags + */ + chan->active = TRUE; + chan->private = FALSE; + chan->secret = FALSE; + chan->moderated = FALSE; + chan->topprot = FALSE; + chan->limitmode = FALSE; + chan->invite = FALSE; + chan->nomsg = FALSE; + chan->keymode = FALSE; + chan->bot_is_op = FALSE; + chan->sync = FALSE; + chan->wholist = FALSE; + chan->rejoin = FALSE; + + current->activechan = chan; +#ifdef CHANBAN + current->lastchanban = 0; +#endif /* CHANBAN */ + to_server("WHO %s\nMODE %s\nMODE %s b\n",rest,rest,rest); + purge_banlist(chan); + purge_chanusers(chan); + +#ifdef STATS + if (chan->setting[STR_STATS].str_var && chan->stats) + { + ChanStats *stats; + + stats = chan->stats; + stats->userseconds = 0; + stats->users = 0; + stats->lastuser = now; + stats->flags |= CSTAT_PARTIAL; + } +#endif /* STATS */ + } + if (chan->active) + { + chan->this10++; + chan->this60++; + /* + * instead of nickcpy(), do it here, so that src can be used for the make_chanuser() call. + * avoids spinning through the "from" buffer twice to find the '!'. + */ + src = from; + dst = CurrentNick; + while(*src) + { + if (*src == '!') + break; + *(dst++) = *(src++); + } + *dst = 0; + src++; + + /* + * find the User and Shit record for this user + */ + if (is_bot(from)) + { + CurrentUser = (User*)&LocalBot; + CurrentShit = NULL; + } + else + { + CurrentUser = get_user(from,rest); + CurrentShit = find_shit(from,rest); + } + + /* + * make a ChanUser for the channel->users list + */ + if (chan->wholist) + { +#ifdef STATS + if (chan->setting[STR_STATS].str_var) + stats_plusminususer(chan,1); + if (chan->stats) + chan->stats->joins++; +#endif /* STATS */ + make_chanuser(CurrentNick,src); + chan->users->user = CurrentUser; + chan->users->shit = CurrentShit; +#ifdef BOTNET + if (CurrentUser && CurrentUser->x.x.access == BOTLEVEL) + check_botjoin(chan,chan->users); +#endif /* BOTNET */ + } + on_join(chan,from); + } +} + +/* + * : MODE : + * : 221 + */ +void parse_mode(char *from, char *rest) +{ + char *to; + + if ((to = chop(&rest)) == NULL) + return; + + if (ischannel(to)) + { + CurrentUser = get_user(from,to); + CurrentShit = find_shit(from,to); + nickcpy(CurrentNick,from); + on_mode(from,to,rest); + } + else + if (!Strcasecmp(current->nick,to)) + { + char *dst; + char sign; + + sign = 0; + while(*rest) + { + if (*rest == '+' || *rest == '-') + sign = *rest; + else + { + for(dst=current->modes;*dst;dst++) + { + if (*dst == *rest) + break; + } + if (*dst && sign == '-') + { + while(dst[1]) + { + *dst = dst[1]; + dst++; + } + *dst = 0; + } + else + if (*dst == 0 && sign == '+') + { + *(dst++) = *rest; + *dst = 0; + } + } + rest++; + } + } +} + +void parse_notice(char *from, char *rest) +{ + char *ctcp,*to; + ulong pingtime; + + to = chop(&rest); + if (*rest == ':') + rest++; + + nickcpy(CurrentNick,from); + + if (*rest == 1 && *(rest+1)) + { + rest++; + ctcp = get_token(&rest," \001"); /* rest cannot be NULL */ + + if (!Strcasecmp(ctcp,"PING") && ((pingtime = get_number(rest)) != -1)) + { + send_spy(SPYSTR_STATUS,"[CTCP PING Reply From %s] %i second(s)", + CurrentNick,(int)(now - pingtime)); + } + else + { + to = get_token(&rest,"\001"); /* rest cannot be NULL */ + send_spy(SPYSTR_STATUS,"[CTCP %s Reply From %s] %s",ctcp,CurrentNick,to); + } + return; + } + + if (ischannel(to)) + { + send_spy(to,"-%s:%s- %s",CurrentNick,to,rest); +#ifdef STATS + { + Chan *chan; + if ((chan = find_channel_ny(to))) + { + if (chan->stats) + chan->stats->notice++; + } + } +#endif /* STATS */ + return; + } + send_spy(SPYSTR_MESSAGE,"-%s- %s",CurrentNick,rest); +} + +void parse_part(char *from, char *rest) +{ + Chan *chan; + char *channel; + char *nick; + + channel = chop(&rest); + if ((chan = find_channel_ny(channel)) == NULL) + return; + + nick = from; + if ((from = Strchr(from,'!'))) + { + *from = 0; + from++; + } + else + { + from = ""; + } + + if (current->spy & SPYF_CHANNEL) + send_spy(channel,"*** Parts: %s (%s)",nick,from); + + if (!nickcmp(current->nick,nick)) + { +#ifdef DEBUG + debug("(parse_part) Im parting %s\n",chan->name); +#endif /* DEBUG */ + chan->active = FALSE; + chan->bot_is_op = FALSE; +#ifdef STATS + if (chan->stats) + chan->stats->flags |= CSTAT_PARTIAL; +#endif /* STATS */ + } + +#ifdef STATS + if (chan->stats) + { + stats_plusminususer(chan,-1); + chan->stats->parts++; + } +#endif /* STATS */ + +#ifdef SEEN + make_seen(nick,from,channel,NULL,now,SEEN_PARTED); +#endif /* SEEN */ + + remove_chanuser(chan,nick); + + /* + * cycle the channel to gain ops once its empty + */ + if (chan->active && !chan->bot_is_op && chan->users && !chan->users->next) + cycle_channel(chan); +} + +void parse_ping(char *from, char *rest) +{ + to_server("PONG :%s\n",rest); +} + +void parse_privmsg(char *from, char *rest) +{ + ChanUser *cu; + char *to,*channel; + + to = chop(&rest); + if (*rest == ':') + rest++; + + channel = NULL; + CurrentChan = NULL; + + if (ischannel(to) && (CurrentChan = find_channel_ac(to))) + { + if ((cu = find_chanuser(CurrentChan,from))) + { + cu->idletime = now; + if (cu->shit) + return; + CurrentUser = cu->user; + } + else + { + channel = CurrentChan->name; + goto hard_lookup; + } + } + else + { + hard_lookup: + if (find_shit(from,channel)) + return; + CurrentUser = get_user(from,channel); + } + + CurrentShit = NULL; + nickcpy(CurrentNick,from); + + /* + * if its a CTCP, it goes to on_ctcp (dcc.c) + */ + if (*rest == 1) + { + on_ctcp(from,to,rest+1); + return; + } +#ifdef TRIVIA + if (CurrentChan) + trivia_check(CurrentChan,rest); +#endif /* TRIVIA */ +#ifdef STATS + if (CurrentChan && CurrentChan->stats) + CurrentChan->stats->privmsg++; +#endif /* STATS */ + on_msg(from,to,rest); +} + +void parse_quit(char *from, char *rest) +{ + Chan *chan; + + nickcpy(CurrentNick,from); + +#ifdef SEEN + make_seen(CurrentNick,from,rest,NULL,now,SEEN_QUIT); +#endif /* SEEN */ + +#ifdef FASTNICK + if (!nickcmp(CurrentNick,current->wantnick)) + to_server("NICK %s\n",current->wantnick); +#endif /* FASTNICK */ + + if (current->spy & SPYF_CHANNEL) + send_spy(MATCH_ALL,"*** Quits: %s (%s)",CurrentNick,rest); + + for(chan=current->chanlist;chan;chan=chan->next) + { +#ifdef STATS + if (chan->setting[STR_STATS].str_var) + { + if (find_chanuser(chan,CurrentNick)) + { + stats_plusminususer(chan,-1); + if (chan->stats) + chan->stats->quits++; + } + } +#endif /* STATS */ + remove_chanuser(chan,CurrentNick); + if (chan->active && !chan->bot_is_op && chan->users && !chan->users->next) + cycle_channel(chan); + } + + delete_auth(from); +} + +void parse_topic(char *from, char *rest) +{ + Chan *chan; + char *channel; + + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) == NULL) + return; + + if (*rest == ':') + rest++; + + send_spy(chan->name,"*** %s changes topic to \"%s\"",nickcpy(NULL,from),rest); + reverse_topic(chan,from,rest); +} + +void parse_wallops(char *from, char *rest) +{ + nickcpy(CurrentNick,from); + send_spy(SPYSTR_MESSAGE,"!%s! %s",CurrentNick,rest); +} + +/* + * + * Numerics + * + */ + +/* + * 213 RPL_STATSCLINE "C * " + */ +void parse_213(char *from, char *rest) +{ + send_pa(PA_STATS,NULL,"[\002%s\002] %s",from,rest); +} + +/* + * 219 RPL_ENDOFSTATS " :End of /STATS report" + */ +void parse_219(char *from, char *rest) +{ + send_pa(PA_STATS|PA_END,NULL,NULL); +} + +/* + * 251 RPL_LUSERCLIENT ":There are users and invisible on servers" + */ +void parse_251(char *from, char *rest) +{ + Server *sp; + char *nick; + int num[3] = {0,0,0}; + int i = 0,n = -1; + + if (current->reset) + { + nick = chop(&rest); + setbotnick(current,nick); + for(sp=serverlist;sp;sp=sp->next) + { + if (!Strcasecmp(sp->name,from) || !Strcasecmp(sp->realname,from)) + { + sp->lastconnect = now; + current->ontime = now; + current->server = sp->ident; + } + } +#ifdef DEBUG + debug("(parse_251) discovered nick = %s\n",nick); + debug("(parse_251) discovered server = %s\n",from); +#endif /* DEBUG */ + to_server("WHOIS %s\nMODE %s\n",nick,nick); + return; + } + + chop(&rest); /* discard bot nick */ + + while(*rest) + { + if (*rest >= '0' && *rest <= '9') + { + if (n == -1) + n = 0; + n = (n * 10) + (*rest - '0'); + } + else + if (n != -1) + { + num[i++] = n; + n = -1; + if (i == 3) + break; + } + rest++; + } + + send_pa(PA_LUSERS,NULL,"\037Lusers status\037"); + send_pa(PA_LUSERS,NULL,"Users: \002%i\002 (Invisible: \002%i\002)",num[0],num[1]); + send_pa(PA_LUSERS,NULL,"Servers: \002%i\002",num[2]); +} + +/* + * 252 RPL_LUSEROP " :operator(s) online" + */ +void parse_252(char *from, char *rest) +{ + int n; + + n = get_number(rest); + send_pa(PA_LUSERS,NULL,"IRC Operators: \002%i\002",n); +} + +/* + * 253 RPL_LUSERUNKNOWN " :unknown connection(s)" + */ +void parse_253(char *from, char *rest) +{ + int n; + + n = get_number(rest); + send_pa(PA_LUSERS,NULL,"Unknown Connections: \002%i\002",n); +} + +/* + * 254 RPL_LUSERCHANNELS " :channels formed" + */ +void parse_254(char *from, char *rest) +{ + int n; + + n = get_number(rest); + send_pa(PA_LUSERS,NULL,"Channels: \002%i\002",n); +} + +/* + * 255 RPL_LUSERME ":I have clients and servers" + */ +void parse_255(char *from, char *rest) +{ + int num[2] = {0,0}; + int i = 0,n = -1; + + while(*rest) + { + if (*rest >= '0' && *rest <= '9') + { + if (n == -1) + n = 0; + n = (n * 10) + (*rest - '0'); + } + else + if (n != -1) + { + num[i++] = n; + n = -1; + if (i == 2) + break; + } + rest++; + } + + send_pa(PA_LUSERS|PA_END,NULL,"Local Clients: \002%i\002 Local Servers: \002%i\002",num[0],num[1]); +} + +/* + * 301 RPL_AWAY " :" + */ +void parse_301(char *from, char *rest) +{ + char *nick; + + nick = chop(&rest); + if (*rest == ':') + rest++; + + send_pa(PA_WHOIS,nick,"Away - %s",rest); +} + +#ifdef NOTIFY + +/* + * 303 RPL_ISON ":[ {}]" + */ +void parse_303(char *from, char *rest) +{ + if (*rest == ':') + rest++; + catch_ison(rest); +} + +#endif /* NOTIFY */ + +/* + * 311 RPL_WHOISUSER " * :" + */ +void parse_311(char *from, char *rest) +{ + char *nick,*user,*host; + + nick = chop(&rest); + user = chop(&rest); + host = chop(&rest); + + chop(&rest); /* discard "*" */ + if (*rest == ':') + rest++; + + /* + * create a "user@host" string + */ + if (host) + host[-1] = '@'; + + if (!nickcmp(nick,current->nick)) + { + Free((char**)¤t->userhost); + set_mallocdoer(parse_311); + current->userhost = Strdup(user); +#ifdef BOTNET + botnet_refreshbotinfo(); +#endif /* BOTNET */ + } + + send_pa(PA_USERHOST|PA_END,nick,"Userhost: %s=%s",nick,user); + send_pa(PA_WHOIS,nick,"Whois: \037%s\037 (%s) : %s",nick,user,rest); +#ifdef NOTIFY + catch_whois(nick,user,rest); +#endif /* NOTIFY */ +} + +/* + * 312 RPL_WHOISSERVER " :" + */ +void parse_312(char *from, char *rest) +{ + char *nick,*server; + + nick = chop(&rest); + server = chop(&rest); + + if (*rest == ':') + rest++; + + send_pa(PA_WHOIS,nick,"Server: %s (%s)",server,rest); +} + +/* + * 313 RPL_WHOISOPERATOR " :is an IRC operator" + */ +void parse_313(char *from, char *rest) +{ + char *nick; + + nick = chop(&rest); + if (*rest == ':') + rest++; + + send_pa(PA_WHOIS,nick,"IRCop: %s %s",nick,rest); +} + +/* + * 315 RPL_ENDOFWHO " :End of /WHO list" + */ +void parse_315(char *from, char *rest) +{ + Chan *chan; + char *channel; + + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) != NULL) + { + chan->wholist = TRUE; + chan->sync = TRUE; +#ifdef STATS + if (chan->setting[STR_STATS].str_var) + stats_plusminususer(chan,0); +#endif /* STATS */ + } +} + +/* + * 317 RPL_WHOISIDLE " :seconds idle" + * 317 RPL_WHOISIDLE " :seconds idle, signon time" + */ +void parse_317(char *from, char *rest) +{ + char *nick; + time_t when = -1; + int min,sec = -1; + + nick = chop(&rest); + + while(*rest) + { + if (*rest >= '0' && *rest <= '9') + { + if (sec == -1) + sec = 0; + sec = (sec * 10) + (*rest - '0'); + } + else + if (sec != -1) + break; + rest++; + } + min = sec / 60; + sec = sec % 60; + + while(*rest) + { + if (*rest >= '0' && *rest <= '9') + { + if (when == -1) + when = 0; + when = (when * 10) + (*rest - '0'); + } + else + if (when != -1) + break; + rest++; + } + + if (when != -1) + send_pa(PA_WHOIS,nick,"Signed On: %s",time2away(when)); + + send_pa(PA_WHOIS,nick, + (sec) ? "Idle: %i minute%s, %i second%s" : "Idle: %i minute%s", + min,(min == 1) ? "" : "s",sec,(sec == 1) ? "" : "s"); +} + +/* + * 318 RPL_ENDOFWHOIS " :End of /WHOIS list" + */ +void parse_318(char *from, char *rest) +{ + char *nick; + + while((nick = get_token(&rest," ,"))) /* rest cannot be NULL */ + { + if (*nick == ':') + break; + + send_pa(PA_WHOIS|PA_END,nick,NULL); + } +} + +/* + * 319 RPL_WHOISCHANNELS " :{[@|+]}" + */ +void parse_319(char *from, char *rest) +{ + char nuh[NUHLEN]; + char *nick,*channel; +#ifdef CHANBAN + Shit *shit; +#endif /* CHANBAN */ + + nick = chop(&rest); + if (*rest == ':') + rest++; + if (*rest == 0) + return; + + send_pa(PA_WHOIS,nick,"Channels: %s",rest); + + // if nick is myself (the bot), check for reset recovery + if (!nickcmp(nick,current->nick)) + { + if (current->reset) + { +loop: + channel = chop(&rest); + if (!channel) + { + current->reset = FALSE; + return; + } + /* + * skip past '+', '-' and '@', etc. + */ + while(*channel && *channel != '#') // this is a recipe for disaster with other valid channels than '#' + channel++; + sprintf(nuh,"%s!%s",current->nick,current->userhost); +#ifdef DEBUG + debug("(parse_319) :%s JOIN :%s\n",nuh,channel); +#endif /* DEBUG */ + parse_join(nuh,channel); + goto loop; + } + } +#ifdef CHANBAN +/* +[StS] {2} WHOIS f0l3r +(in) {2} :Vancouver.BC.CA.Undernet.org 311 eenucks f0l3r ~folyourat folyourat.users.undernet.org * :Huh? +(in) {2} :Vancouver.BC.CA.Undernet.org 319 eenucks f0l3r :#emech @#folers @#margarita @#caracas +(in) {2} :Vancouver.BC.CA.Undernet.org 312 eenucks f0l3r *.undernet.org :The Undernet Underworld +(in) {2} :Vancouver.BC.CA.Undernet.org 330 eenucks f0l3r folyourat :is logged in as +(in) {2} :Vancouver.BC.CA.Undernet.org 318 eenucks f0l3r :End of /WHOIS list. +*/ + else + { + // check if all sendq is empty (text, kicks and modes) + if (current->sendq) + { +#ifdef DEBUG + debug("(parse_319) skipping chanban, sendq is not empty\n"); +#endif /* DEBUG */ + return; + } + // CurrentChan is unset and can be used + for(CurrentChan=current->chanlist;CurrentChan;CurrentChan=CurrentChan->next) + { + if (CurrentChan->kicklist || CurrentChan->modelist) + return; + } + + while((channel = chop(&rest))) + { +#ifdef DEBUG + debug("(parse_319) Cb: checking %s\n",channel); +#endif /* DEBUG */ + if (*channel == '@' || *channel == '+') // opped/voiced user, spin 1 + channel++; + // other user modes might also be present (crazy ircd's), this is insufficient testing + + // check if the channel is shitted + for(shit=current->shitlist;shit;shit=shit->next) + { + if (!matches(shit->mask,channel)) + { +#ifdef DEBUG + debug("(parse_319) Cb: executing chanban_action()\n",channel); +#endif /* DEBUG */ + chanban_action(nick,channel,shit); + } + } + } + } +#endif /* CHANBAN */ +} + +/* + * 324 RPL_CHANNELMODEIS " " + */ +void parse_324(char *from, char *rest) +{ + char *channel; + + if ((channel = chop(&rest))) + { + CurrentUser = NULL; + CurrentShit = NULL; + *CurrentNick = 0; + on_mode(CurrentNick,channel,rest); + } +} + +/* + * 352 RPL_WHOREPLY " [*][@|+] : " + */ +void parse_352(char *from, char *rest) +{ + Chan *chan; + char nuh[NUHLEN]; + char *channel,*nick,*userhost; + + channel = chop(&rest); + if ((CurrentChan = chan = find_channel_ac(channel)) == NULL) + return; + + if (chan->wholist == TRUE) + return; + + userhost = chop(&rest); + rest[-1] = '@'; /* glue: "user\0host" --> "user@host" */ + chop(&rest); + chop(&rest); + nick = chop(&rest); + + sprintf(nuh,"%s!%s",nick,userhost); + + make_chanuser(nick,userhost); + + if (is_bot(nick)) + { +#ifdef DEBUG + debug("(parse_352) setting as local bot: %s (%s)\n",nuh,channel); +#endif /* DEBUG */ + chan->users->user = (User*)&LocalBot; + chan->users->shit = NULL; + } + else + { + chan->users->user = get_user(nuh,channel); + chan->users->shit = find_shit(nuh,channel); + } + +#ifdef BOTNET + if (chan->users->user && chan->users->user->x.x.access == BOTLEVEL) + check_botjoin(chan,chan->users); +#endif /* BOTNET */ + + while(*rest != ' ') + { + if (*rest == '@') + { + chan->users->flags = CU_CHANOP; + if (!nickcmp(current->nick,nick)) + { +#ifdef DEBUG + debug("(parse_352) According to wholist I have ops\n"); +#endif /* DEBUG */ + chan->bot_is_op = TRUE; + } + /* + * chanop is the most relevant flag at this stage + */ + return; + } + else + if (*rest == '+') + { + chan->users->flags = CU_VOICE; + } + rest++; + } +} + +/* + * 367 RPL_BANLIST " " + * 367 RPL_BANLIST " " + */ +void parse_367(char *from, char *rest) +{ + Chan *chan; + char *channel,*banmask,*banfrom; + time_t bantime; + + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) == NULL) + return; + + banmask = chop(&rest); + + /* + * according to RFC1459, the next two args are optional + */ + if ((banfrom = chop(&rest)) == NULL) + banfrom = "?"; + + if ((bantime = get_number(rest)) == -1) + bantime = now; + + make_ban(&chan->banlist,banfrom,banmask,bantime); +} + +/* + * 376 RPL_ENDOFMOTD ":End of /MOTD command" + */ +void parse_376(char *from, char *rest) +{ + Server *sp; + char *mode; + + /* + * Save the name that the server uses for itself + */ + if ((sp = find_server(current->server))) + { + if (*sp->realname == 0) + Strncpy(sp->realname,from,NAMELEN); + sp->lastconnect = now; + } + if (current->connect != CN_ONLINE) + { + current->connect = CN_ONLINE; + current->ontime = now; + to_server("WHOIS %s\n",current->nick); + if ((mode = current->setting[STR_UMODES].str_var)) + to_server("MODE %s %s\n",current->nick,mode); + } +#ifdef IDWRAP + unlink_identfile(); +#endif /* IDWRAP */ +} + +/* + * 401 ERR_NOSUCHNICK :No such nick/channel + */ +void parse_401(char *from, char *rest) +{ + char *nick; + + nick = chop(&rest); + send_pa(PA_WHOIS,nick,"%s: No such nick",nick); +} + +/* + * How to determine the new nick: + * - If it's possible to add a '_' to the nick, do it (done) + * - else loop through the nick from begin to end and replace + * the first non-_ with a _. i.e. __derServ -> ___erServ + * - If the nick is 9 _'s, try the original nick with something random + */ +void parse_433(char *from, char *rest) +{ + char *s; + + /* + * try to get a better nick even if we are already online + */ + if ((s = current->setting[STR_ALTNICK].str_var)) + { + char scopy[strlen(s)+1]; + char *s2,*nick,*trynick; + + chop(&rest); + trynick = chop(&rest); + Strcpy(scopy,s); + s = scopy; + nick = NULL; + do + { + s2 = chop(&s); + if (current->connect == CN_ONLINE && !Strcasecmp(current->nick,s2)) + { + /* nicks listed first are more worth, dont try nicks after */ + break; + } + if (!nick) + nick = s2; + if (!Strcasecmp(trynick,s2)) + { + nick = NULL; + } +#ifdef DEBUG + debug("(parse_433) s = %s, s2 = %s, nick = %s\n", + nullbuf(s),nullstr(s2),nullstr(nick)); +#endif + } + while(*s); + + if (nick) + { +#ifdef DEBUG + debug("(parse_433) Nick: %s, Altnick: %s\n",current->nick,nick); +#endif /* DEBUG */ + to_server("NICK %s\n",nick); + if (current->connect != CN_ONLINE) + { + Free((char**)¤t->nick); + set_mallocdoer(parse_433); + current->nick = Strdup(nick); + } + return; + } + } +} + +void parse_451(char *from, char *rest) +{ + register_with_server(); +} + +/* + * 405 ERR_TOOMANYCHANNELS " :You have joined too many channels" + * 471 ERR_CHANNELISFULL " :Cannot join channel (+l)" + * 474 ERR_BANNEDFROMCHAN " :Cannot join channel (+b)" + * 475 ERR_BADCHANNELKEY " :Cannot join channel (+k)" + * 476 ERR_BADCHANMASK ??? + */ +void parse_471(char *from, char *rest) +{ + Chan *chan; + char *channel; + + channel = chop(&rest); + + if ((chan = find_channel_ny(channel)) != NULL) + { + chan->active = FALSE; + chan->sync = TRUE; + } +} + +/* + * 473 ERR_INVITEONLYCHAN " :Cannot join channel (+i)" + */ +void parse_473(char *from, char *rest) +{ + Chan *chan; + char *channel; + + channel = chop(&rest); + + if ((chan = find_channel_ny(channel)) != NULL) + { + chan->active = FALSE; + chan->sync = TRUE; + } +} + +#ifdef IRCD_EXTENSIONS + +/* +(in) {2} :ircnet.choopa.net 348 eenucks #emech *kong!*@* +(in) {2} :ircnet.choopa.net 349 eenucks #emech :End of Channel Exception List + +(in) {2} :ircnet.choopa.net 346 eenucks #emech *king*!*@* +(in) {2} :ircnet.choopa.net 347 eenucks #emech :End of Channel Invite List +*/ + +/* + * 346 xxx " " + * 346 xxx " " + */ +void parse_346(char *from, char *rest) +{ + Chan *chan; + char *channel,*banmask,*banfrom; + time_t bantime; + Ban *new; + + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) == NULL) + return; + + banmask = chop(&rest); + + /* + * according to RFC1459, the next two args are optional + */ + if ((banfrom = chop(&rest)) == NULL) + banfrom = "?"; + + if ((bantime = get_number(rest)) == -1) + bantime = now; + + new = make_ban(&chan->banlist,banfrom,banmask,bantime); + new->imode = TRUE; +} + +/* + * 348 xxx " " + * 348 xxx " " + */ +void parse_348(char *from, char *rest) +{ + Chan *chan; + char *channel,*banmask,*banfrom; + time_t bantime; + Ban *new; + + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) == NULL) + return; + + banmask = chop(&rest); + + /* + * according to RFC1459, the next two args are optional + */ + if ((banfrom = chop(&rest)) == NULL) + banfrom = "?"; + + if ((bantime = get_number(rest)) == -1) + bantime = now; + + new = make_ban(&chan->banlist,banfrom,banmask,bantime); + new->emode = TRUE; +} + +/* + * catch end of banlist reply and use it to send requests for I/e modes if they are supported. + */ +void parse_368(char *from, char *rest) +{ + Chan *chan; + char *channel; + + channel = chop(&rest); + if ((chan = find_channel_ac(channel)) == NULL) + return; + + if (current->ircx_flags & IRCX_IMODE) + { + to_server("MODE %s +I\n",chan->name); + } + if (current->ircx_flags & IRCX_EMODE) + { + to_server("MODE %s +e\n",chan->name); + } +} + +/* + * catch the capabilities line and extract some details from it. + */ +/* +:ircnet.choopa.net 005 botname +RFC2812 +PREFIX=(ov)@+ Legal channel user prefixes (ops and voice) +CHANTYPES=#&!+ Legal channel name prefixes +MODES=3 Permitted number of modes per MODE-line +CHANLIMIT=#&!+:21 +NICKLEN=15 +TOPICLEN=255 +KICKLEN=255 +MAXLIST=beIR:64 +CHANNELLEN=50 +IDCHAN=!:5 +CHANMODES=beIR,k,l,imnpstaqr +:are supported by this server +*/ +void parse_005(char *from, char *rest) +{ + char *opt,*s; + int n; + + while((opt = chop(&rest))) + { + if (*opt == ':') + break; + if (!matches("modes=?",opt)) + { + n = a2i(opt+6); + if (!errno && n >= 1 && n <= 20) + { + current->setting[INT_MODES].int_var = n; +#ifdef DEBUG + debug("(parse_005) autoconf: mode number: %i\n",n); +#endif /* DEBUG */ + } + } + else + if (!Strcasecmp("WALLCHOPS",opt)) + { + current->ircx_flags |= IRCX_WALLCHOPS; +#ifdef DEBUG + debug("(parse_005) autoconf: wallchops enabled\n"); +#endif /* DEBUG */ + } + else + if (!Strcasecmp("WALLVOICES",opt)) + { + current->ircx_flags |= IRCX_WALLVOICES; +#ifdef DEBUG + debug("(parse_005) autoconf: wallvoices enabled\n"); +#endif /* DEBUG */ + } + else + if (!matches("chanmodes=*",opt)) + { + s = opt+10; +#ifdef DEBUG + debug("(parse_005) autoconf: s = \"%s\"\n",s); +#endif /* DEBUG */ + while(*s) + { + //CHANMODES=beIR,k,l,imnpstaqr + if (*s == 'e') + { + current->ircx_flags |= IRCX_EMODE; +#ifdef DEBUG + debug("(parse_005) autoconf: +e modes (ban exceptions) enabled\n"); +#endif /* DEBUG */ + } + if (*s == 'I') + { + current->ircx_flags |= IRCX_IMODE; +#ifdef DEBUG + debug("(parse_005) autoconf: +I modes (invite exceptions) enabled\n"); +#endif /* DEBUG */ + } + s++; + } + } + } +} + +#endif /* IRCD_EXTENSIONS */ + +#define NEEDFROM 1 +#define DROPONE 2 + +LS const struct +{ + ulong hash; + short flags; + void (*func)(char *, char *); + +} pFuncs[] = +{ + { 0x50524956, NEEDFROM, parse_privmsg }, /* PRIVMSG */ + { 0x4A4F494E, NEEDFROM, parse_join }, /* JOIN */ + { 0x50415254, NEEDFROM, parse_part }, /* PART */ + { 0x4D4F4445, NEEDFROM, parse_mode }, /* MODE */ + { 0x4E49434B, NEEDFROM, on_nick }, /* NICK */ + { 0x4B49434B, NEEDFROM, on_kick }, /* KICK */ + { 0x50494E47, 0, parse_ping }, /* PING */ + { 0x544F5049, NEEDFROM, parse_topic }, /* TOPIC */ + { 0x4E4F5449, NEEDFROM, parse_notice }, /* NOTICE */ + { 0x51554954, NEEDFROM, parse_quit }, /* QUIT */ + { 0x494E5649, NEEDFROM|DROPONE, parse_invite }, /* INVITE */ + { 0x57414C4C, NEEDFROM, parse_wallops }, /* WALLOPS */ + { 0x4552524F, 0, parse_error }, /* ERROR */ + { 0x00333532, NEEDFROM|DROPONE, parse_352 }, /* 352 RPL_WHOREPLY */ + { 0x00333135, NEEDFROM|DROPONE, parse_315 }, /* 315 RPL_ENDOFWHO */ + { 0x00323231, NEEDFROM, parse_mode }, /* 221 RPL_UMODEIS */ + { 0x00333131, NEEDFROM|DROPONE, parse_311 }, /* 311 RPL_WHOISUSER */ + { 0x00333132, NEEDFROM|DROPONE, parse_312 }, /* 312 RPL_WHOISSERVER */ + { 0x00333138, NEEDFROM|DROPONE, parse_318 }, /* 318 RPL_ENDOFWHOIS */ + { 0x00343031, NEEDFROM|DROPONE, parse_401 }, /* 401 ERR_NOSUCHNICK */ + { 0x00343333, NEEDFROM, parse_433 }, /* 433 ERR_NICKNAMEINUSE */ + { 0x00323139, NEEDFROM, parse_219 }, /* 219 RPL_ENDOFSTATS */ + { 0x00323131, NEEDFROM|DROPONE, parse_213 }, /* 211 RPL_STATSLINKINFO */ + { 0x00323133, NEEDFROM|DROPONE, parse_213 }, /* 213 RPL_STATSCLINE */ + { 0x00323134, NEEDFROM|DROPONE, parse_213 }, /* 214 RPL_STATSNLINE */ + { 0x00323135, NEEDFROM|DROPONE, parse_213 }, /* 215 RPL_STATSILINE */ + { 0x00323136, NEEDFROM|DROPONE, parse_213 }, /* 216 RPL_STATSKLINE */ + { 0x00323137, NEEDFROM|DROPONE, parse_213 }, /* 217 RPL_STATSQLINE */ + { 0x00323138, NEEDFROM|DROPONE, parse_213 }, /* 218 RPL_STATSYLINE */ + { 0x00323431, NEEDFROM|DROPONE, parse_213 }, /* 241 RPL_STATSLLINE */ + { 0x00323432, NEEDFROM|DROPONE, parse_213 }, /* 242 RPL_STATSUPTIME */ + { 0x00323433, NEEDFROM|DROPONE, parse_213 }, /* 243 RPL_STATSOLINE */ + { 0x00323434, NEEDFROM|DROPONE, parse_213 }, /* 244 RPL_STATSHLINE */ + { 0x00323435, NEEDFROM|DROPONE, parse_213 }, /* 245 RPL_STATSSLINE */ + { 0x00323531, NEEDFROM, parse_251 }, /* 251 RPL_LUSERCLIENT */ + { 0x00323532, NEEDFROM|DROPONE, parse_252 }, /* 252 RPL_LUSEROP */ + { 0x00323533, NEEDFROM|DROPONE, parse_253 }, /* 253 RPL_LUSERUNKNOWN */ + { 0x00323534, NEEDFROM|DROPONE, parse_254 }, /* 254 RPL_LUSERCHANNELS */ + { 0x00323535, NEEDFROM|DROPONE, parse_255 }, /* 255 RPL_LUSERME */ + { 0x00333031, NEEDFROM|DROPONE, parse_301 }, /* 301 RPL_AWAY */ +#ifdef NOTIFY + { 0x00333033, DROPONE, parse_303 }, /* 303 RPL_ISON */ +#endif /* NOTIFY */ + { 0x00333133, NEEDFROM|DROPONE, parse_313 }, /* 313 RPL_WHOISOPERATOR */ + { 0x00333137, NEEDFROM|DROPONE, parse_317 }, /* 317 RPL_WHOISIDLE */ + { 0x00333139, NEEDFROM|DROPONE, parse_319 }, /* 319 RPL_WHOISCHANNELS */ + { 0x00333234, NEEDFROM|DROPONE, parse_324 }, /* 324 RPL_CHANNELMODEIS */ + { 0x00333637, NEEDFROM|DROPONE, parse_367 }, /* 367 RPL_BANLIST */ + { 0x00343531, 0, parse_451 }, /* 451 ERR_NOTREGISTERED */ + { 0x00343035, NEEDFROM|DROPONE, parse_471 }, /* 405 ERR_TOOMANYCHANNELS */ + { 0x00343731, NEEDFROM|DROPONE, parse_471 }, /* 471 ERR_CHANNELISFULL */ + { 0x00343733, NEEDFROM|DROPONE, parse_473 }, /* 473 ERR_INVITEONLYCHAN */ + { 0x00343734, NEEDFROM|DROPONE, parse_471 }, /* 474 ERR_BANNEDFROMCHAN */ + { 0x00343735, NEEDFROM|DROPONE, parse_471 }, /* 475 ERR_BADCHANNELKEY */ + { 0x00343736, NEEDFROM|DROPONE, parse_471 }, /* 476 ERR_BADCHANMASK */ + { 0x00333736, NEEDFROM, parse_376 }, /* 376 RPL_ENDOFMOTD */ + { 0x00343232, NEEDFROM, parse_376 }, /* 422 ERR_NOMOTD */ +#ifdef IRCD_EXTENSIONS + { 0x00303035, NEEDFROM|DROPONE, parse_005 }, /* 005 ircd extensions */ + { 0x00333638, NEEDFROM|DROPONE, parse_368 }, /* 368 RPL_ENDOFBANLIST */ + { 0x00333436, NEEDFROM|DROPONE, parse_346 }, /* 346 +I invitelist */ + { 0x00333438, NEEDFROM|DROPONE, parse_348 }, /* 348 +e ban exception */ +#endif /* IRCD_EXTENSIONS */ + { 0, 0, NULL } +}; + +ulong stringhash(char *s) +{ + ulong hash; + int i; + + hash = 0; + for(i=0;(i<4 && *s);i++) + hash = (hash << 8) | (uchar)*(s++); + return(hash); +} + +void parseline(char *rest) +{ +#ifdef SCRIPTING + Hook *hook; +#endif /* SCRIPTING */ + char *from,*command; + ulong cmdhash; + int i; + + if (current->spy & SPYF_RAWIRC) + send_spy(SPYSTR_RAWIRC,FMT_PLAIN,rest); + +//new undernet amusements +//(in) {5} NOTICE AUTH :*** You have identd disabled (or broken), to continue to connect you must type /QUOTE PASS 17071 + if (current->connect == CN_CONNECTED && *rest == 'N' && !matches("NOTICE AUTH * /QUOTE PASS *",rest)) + { + from = STREND(rest); + while(from > rest && *from != ' ') + from--; + if (*from == ' ' && *(from+1)) + { + to_server("PASS%s\n",from); + } + } + + if (*rest == ':') + { + rest++; + from = rest; + while(*rest != ' ') + rest++; + *(rest++) = 0; + } + else + { + from = NULL; + } + + command = chop(&rest); + if (*rest == ':') + rest++; + +#ifdef SCRIPTING + cmdhash = 1; + for(hook=hooklist;hook;hook=hook->next) + { + /* + * check if the hook applies to this particular bot + */ + if (hook->guid && hook->guid != current->guid) + continue; + /* + * does the hook match? + */ + if (hook->flags == HOOK_PARSE && !Strcasecmp(command,hook->type.command)) + { + if (hook->func(from,rest,hook)) + /* if the hook returns non-zero, the input should not be parsed internally */ + cmdhash = 0; + } + } + if (!cmdhash) + return; +#endif /* SCRIPTING */ + + cmdhash = stringhash(command); + + for(i=0;pFuncs[i].hash;i++) + { + if (cmdhash == pFuncs[i].hash) + { + if ((pFuncs[i].flags & NEEDFROM) && !from) + return; + if (pFuncs[i].flags & DROPONE) + chop(&rest); /* discard bot nick */ + pFuncs[i].func(from,rest); + return; + } + } +} diff --git a/src/perl.c b/src/perl.c new file mode 100644 index 0000000..9b1d59c --- /dev/null +++ b/src/perl.c @@ -0,0 +1,184 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2009 proton + Copyright (c) 2001 MadCamel + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define PERL_C +#include "config.h" + +#ifdef PERL + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + + #include "EXTERN.h" + #include "perl.h" + #include "XSUB.h" +#include "perl.h" + +PerlInterpreter *energymech_perl = NULL; + +/* + * parse_jump() translates from C to perl + */ +int perl_parse_jump(char *from, char *rest, Hook *hook) +{ + dSP; /* Declare a local stack pointer */ + char *args[3]; + +#ifdef DEBUG + debug("(perl_parse_jump) %s %s %s\n", + nullstr(hook->self),nullstr(from),nullstr(rest)); +#endif /* DEBUG */ + + args[0] = (*from && from) ? from : ""; /* Nulls are a no-no */ + args[1] = rest; + args[2] = NULL; + + /* Call_argv returns the # of args returned from perl */ + if (call_argv(Hook->self, G_EVAL|G_SCALAR, args) < 1) + return(0); + + SPAGAIN; /* Rehash stack, it's probably been clobbered */ + return(POPi); /* Pop an int */ + +} + +/* + * accept 2 arguments + * char *name = name of the IRC input to hook (PRIVMSG, NOTICE, PING, JOIN, PART, etc..) + * char *subroutine = name of the function that should be called in the script for each of the + * hooked input lines (coming from parseline()) + * + * this function should be made directly callable from the script as "parse_hook" or similar + */ + +/* Don't ask! You don't want to know! */ +XS(XS_perl_parse_hook) +{ + Hook *hook; + char *name, *sub; + int c; + dXSARGS; items = 0; + + /* + * translate *name and *sub from perl variables to C strings + * SvPV(ST(0)) returns a string(char) pointer to the first arg. + * but I don't know if it's safe to point directly in to perl + * space like that. + */ + if ((name = strdup(SvPV(ST(0), i)))) == NULL) + XSRETURN_EMPTY; + + if ((sub = strdup(SvPV(ST(0), i)))) == NULL) + { + free(name); + XSRETURN_EMPTY; + } + + /* + * make a Hook struct and link it into the parse hook list + */ + set_mallocdoer(perl_parse_hook); + hook = (Hook*)Calloc(sizeof(Hook) + Strlen2(name,sub)); + hook->func = perl_parse_jump; + hook->next = hooklist; + hooklist = hook; + + hook->command = Strcpy(hook->self,sub) + 1; + Strcpy(hook->command,name); + + free(name); + free(sub); + + /* + * return successful status to script + * I don't know how to return a number so I return the sub name. + */ + XST_mPV(0, sub); + XSRETURN(1); +} + +void init_perl(void) +{ + energymech_perl = perl_alloc(); + perl_construct(energymech_perl); + + /* + * make parse_hook() callable from scripts + */ + newXS("mech::parse_hook", XS_perl_parse_hook, "mech"); +} + +void do_perl(COMMAND_ARGS) +{ + /* + * call init_perl() if the perl interpreter isnt initialized yet + */ + + /* + * call the perl interpreter with the content of *rest + */ + + /* + * be verbose about success or fail to the user + */ +} + +void do_perlscript(COMMAND_ARGS) +{ + char *args[2]; + dSP; + + /* + * call init_perl() if the perl interpreter isnt initialized yet + */ + + /* + * chop(&rest) for name of script filename and load it into the perl interpreter + */ + args[0] = ""; + args[1] = chop(&rest); + + /* FIXME: Trap parse errors */ + perl_parse(energymech_perl, NULL, 1, argv, (char **)NULL); + + /* Call sub named Init, what should contain + * mech::perl_parse_hook("PRIVMSG", "yoink_privmsg"); + * Note to self: Wouldn't it be better to pass subs by + * Reference(perl ver of pointer) instead of name? + * How the fsck do i do that?! + */ + call_pv("Init", G_EVAL|G_NOARGS); + if(SvTRUE(ERRSV)) + { + STRLEN n_a; + to_user(from, "perl script %s failed to init: %s", + argv[1], SvPV(ERRSV, n_a)); + } + /* + * be verbose about success or fail to the user + */ +} + +#endif /* PERL */ diff --git a/src/prot.c b/src/prot.c new file mode 100644 index 0000000..80f5652 --- /dev/null +++ b/src/prot.c @@ -0,0 +1,610 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define PROTECTION_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * + * kicking and screaming + * + */ + +void send_kick(Chan *chan, const char *nick, const char *format, ...) +{ + qKick *new,**pp; + va_list vargs; + + /* + * gsockdata safe to use since we're a `tail' function + */ + va_start(vargs,format); + vsprintf(gsockdata,format,vargs); + va_end(vargs); + + pp = &chan->kicklist; + while(*pp) + pp = &(*pp)->next; + + set_mallocdoer(send_kick); + *pp = new = (qKick*)Calloc(sizeof(qKick) + Strlen2(nick,gsockdata)); + /* Calloc sets to zero new->next = NULL; */ + + new->reason = Strcpy(new->nick,nick) + 1; + Strcpy(new->reason,gsockdata); +} + +void push_kicks(Chan *chan) +{ + qKick *kick; + int n; + + n = (current->sendq_time - now); + while(n < 6) + { + if ((kick = chan->kicklist) == NULL) + return; + + chan->kicklist = kick->next; + to_server("KICK %s %s :%s\n",chan->name,kick->nick,kick->reason); + Free((char**)&kick); + n += 2; + } +} + +void unmode_chanuser(Chan *chan, ChanUser *cu) +{ + qMode *mode,**pp; + + pp = &chan->modelist; + while(*pp) + { + mode = *pp; + if ((mode->type == QM_CHANUSER) && (mode->data == (void*)cu)) + { + *pp = mode->next; + Free((char**)&mode); + /* + * there might be more modes associated with this chanuser + */ + continue; + } + pp = &mode->next; + } +} + +void send_mode(Chan *chan, int pri, int type, char plusminus, char modeflag, void *data) +{ + qMode *mode,**pp; + +#ifdef NO_DEBUG + debug("(send_mode) chan = %s; pri = %i; type = %i; plusminus = %c; modeflag = %c; data = "mx_pfmt"\n", + chan->name,pri,type,plusminus,modeflag,(mx_ptr)data); +#endif /* DEBUG */ + + /* + * make minusmodes always one priority lower than plusmodes + */ + if (plusminus == '-') + pri |= 1; + + pp = &chan->modelist; + while(*pp) + { + mode = *pp; + if (mode->pri == pri) + { + /* + * check for duplicate + */ + if ((mode->type == type) && (mode->plusminus == plusminus) && + (mode->modeflag == modeflag) && (mode->data == data)) + return; + } + if (mode->pri > pri) + break; + pp = &mode->next; + } + + set_mallocdoer(send_mode); + mode = (qMode*)Calloc(sizeof(qMode)); + mode->next = *pp; + *pp = mode; + mode->pri = pri; + mode->type = type; + mode->plusminus = plusminus; + mode->modeflag = modeflag; + + switch(type) + { + case QM_CHANUSER: + mode->data = data; + break; + default: + if (data) + { + set_mallocdoer(send_mode); + mode->data = (void*)Strdup((char*)data); + } + else + { + mode->data = NULL; + } + } +} + +int mode_effect(Chan *chan, qMode *mode) +{ + ChanUser *cu; + int f; + + if (mode->type == QM_CHANUSER) + { + cu = (ChanUser*)mode->data; + f = 0; + switch(mode->modeflag) + { + case 'o': + f = (cu->flags & CU_CHANOP) ? '+' : '-'; + break; + case 'v': + f = (cu->flags & CU_VOICE) ? '+' : '-'; + break; + } + if (f && f == mode->plusminus) + return(FALSE); + } + return(TRUE); +} + +void push_modes(Chan *chan, int lowpri) +{ + qMode *mode; + char flaglist[32],parmlist[MSGLEN]; + char *dstflag,*dstparm,*srcparm,lastmode; + int n,maxmodes; + + n = (current->sendq_time - now); + +loop: + maxmodes = current->setting[INT_MODES].int_var; + lastmode = 0; + dstflag = flaglist; + dstparm = parmlist; + while((mode = chan->modelist)) + { + /* + * if the line has already been partially filled, + * then its ok to fill up "empty slots" with non-priority modes. + */ + if (lowpri && !lastmode && (mode->pri >= QM_PRI_LOW)) + return; + chan->modelist = mode->next; + if (mode_effect(chan,mode)) + { + switch(mode->type) + { + case QM_CHANUSER: + srcparm = ((ChanUser*)mode->data)->nick; + break; + default: + srcparm = (char*)mode->data; + } + if (mode->plusminus != lastmode) + *(dstflag++) = lastmode = mode->plusminus; + *(dstflag++) = mode->modeflag; + if (srcparm) + { + *(dstparm++) = ' '; + dstparm = Strcpy(dstparm,srcparm); + } + maxmodes--; + } +#ifdef NO_DEBUG + else + { + debug("(push_modes) ineffectual mode: %c%c %s\n",mode->plusminus,mode->modeflag, + (mode->type == QM_CHANUSER) ? ((ChanUser*)mode->data)->nick : (char*)mode->data); + } +#endif /* DEBUG */ + if (mode->type != QM_CHANUSER) + Free((char**)&mode->data); + Free((char**)&mode); + if (!maxmodes) + break; + } + if (!lastmode) + return; + *dstflag = 0; + *dstparm = 0; + /* + * the missing space is not a bug. + */ + to_server("MODE %s %s%s\n",chan->name,flaglist,parmlist); + n += 2; + + if (lowpri && n < lowpri) + goto loop; +} + +void update_modes(Chan *chan) +{ + ChanUser *cu; + + for(cu=chan->users;cu;cu=cu->next) + { + if ( + (chan->setting[TOG_AOP].int_var && cu->user && cu->user->x.x.aop) || + ((cu->flags & (CU_CHANOP|CU_NEEDOP)) == CU_NEEDOP) + ) + { +#ifdef NO_DEBUG + debug("(update_modes) pushing: MODE %s +o %s!%s\n", + chan->name,cu->nick,cu->userhost); +#endif /* DEBUG */ + send_mode(chan,50,QM_CHANUSER,'+','o',(void*)cu); + } + } +} + +/* + * check_mass() takes no action, it only touches the abuse counters + * and timers, TRUE is returned if the limit is reached + */ +int check_mass(Chan *chan, ChanUser *doer, int type) +{ + time_t *when; + int *num,limit; + + /* + * must handle servers ... + */ + if (!doer) + return(FALSE); + + if (doer->user && doer->user->x.x.access >= ASSTLEVEL) + return(FALSE); + + if ((type == INT_CKL || type == INT_FL) && (doer->flags & CU_CHANOP)) + return(FALSE); + + limit = chan->setting[type].int_var; + switch(type) + { + /* + * two things we dont want channel users to do + */ + //case CHK_CAPS: + case INT_CKL: + num = &doer->capsnum; + break; + //case CHK_PUB: + case INT_FL: + num = &doer->floodnum; + break; + /* + * three things we dont want channel ops to do + */ + //case CHK_DEOP: + case INT_MDL: + num = &doer->deopnum; + break; + //case CHK_BAN: + case INT_MBL: + num = &doer->bannum; + break; + default: +/* case CHK_KICK: */ +/* case INT_MKL: */ + num = &doer->kicknum; + break; + } + + when = (time_t*)&num[1]; + if ((now - *when) > 10) + { + *when = now; + *num = 0; + } + ++(*num); + if (*num >= limit && limit) + return(TRUE); + return(FALSE); +} + +void mass_action(Chan *chan, ChanUser *doer) +{ + int mpl; + + if ((mpl = chan->setting[INT_MPL].int_var) == 0) + return; + + if (mpl >= 2) + { + if (0 == (doer->flags & CU_DEOPPED) || 0 == (doer->flags & CU_BANNED)) + { + deop_ban(chan,doer,NULL); + doer->flags |= CU_DEOPPED|CU_BANNED; + } + } + + if (0 == (doer->flags & CU_KICKED)) + { + send_kick(chan,CurrentNick,KICK_MASSMODES); + doer->flags |= CU_KICKED; + } +} + +void prot_action(Chan *chan, char *from, ChanUser *doer, char *target, ChanUser *victim) +{ + int maxprot,uprot; + + /* + * cant do anything to a user that isnt on the channel + * doer is normally supplied, but not always + */ + if (!doer) + { + if ((doer = find_chanuser(chan,from)) == NULL) + return; + } + + /* + * No protective measures for doers with high access + */ + if (doer->user && doer->user->x.x.access >= ASSTLEVEL) + return; + + maxprot = chan->setting[INT_PROT].int_var; + + if (victim) + uprot = (victim->user) ? victim->user->x.x.prot : 0; + else + { + uprot = get_protaction(chan,target); + } + + if ((uprot >= 4) && (!(doer->flags & CU_BANNED))) + { + doer->flags |= CU_BANNED|CU_DEOPPED; + deop_ban(chan,doer,NULL); + } + + if ((uprot >= 3) && (!(doer->flags & CU_KICKED))) + { + doer->flags |= CU_KICKED; + send_kick(chan,doer->nick,"\002%s is Protected\002",(target) ? target : get_nuh(victim)); + } + /* + * with (uprot == 2) our ONLY action is to deop the guilty party + */ + if ((uprot == 2) && (!(doer->flags & CU_DEOPPED))) + { + doer->flags |= CU_DEOPPED; + send_mode(chan,50,QM_CHANUSER,'-','o',(void*)doer); + } +} + +/* + * + * mode commands + * + */ + +void do_opdeopme(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + */ + Chan *chan; + ChanUser *cu; + + if ((chan = CurrentChan) && chan->bot_is_op) + { + if ((cu = find_chanuser(chan,from))) + { + send_mode(chan,80,QM_CHANUSER, + (CurrentCmd->name == C_DOWN) ? '-' : '+','o',cu); + } + } +} + +void do_opvoice(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + CARGS + */ + Chan *chan; + char *m; + + if ((chan = CurrentChan) && chan->bot_is_op) + { + /* rest can only be NULL on OP/VOICE commands, + DEOP/UNVOICE commands require arguments (CARGS) */ + if (!*rest) + rest = CurrentNick; + m = CurrentCmd->cmdarg; +#ifdef DEBUG + debug("(do_opvoice) Chan '%s', sign %c, mode %c, rest '%s'\n",chan->name,m[1],m[0],rest); +#endif /* DEBUG */ + channel_massmode(chan,rest,MODE_FORCE,m[1],m[0]); + } +} + +void do_kickban(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + CARGS + ACCHAN + */ + Chan *chan; + ChanUser *victim; + const char *m; + char *nick,*nuh; + uchar c; + int uaccess; + + nick = chop(&rest); + chan = CurrentChan; + + /* + * is the bot opped on that channel? + */ + if (!chan->bot_is_op) + return; + + if ((victim = find_chanuser(chan,nick)) == NULL) + return; + + if (*rest == 0) + { + if ((rest = randstring(RANDKICKSFILE)) == NULL) + rest = KICK_DEFAULT; + } + + m = CurrentCmd->cmdarg; + c = *(m++); + + uaccess = get_authaccess(from,to); + if (victim->user && victim->user->x.x.access > uaccess) + { + nuh = get_nuh(victim); + send_kick(chan,CurrentNick,"%s attempt of %s",m,nuh); + return; + } + + if (c & 0x4) + send_kick(chan,nick,FMT_PLAIN,rest); + + switch(c & 0x3) + { + case 0: + deop_ban(chan,victim,NULL); + break; + case 1: + deop_siteban(chan,victim); + break; + case 2: + deop_screwban(chan,victim); + break; + } + + m = STREND(m)+1; + to_user(from,"%s %sed on %s",nick,m,to); +} + +void do_unban(COMMAND_ARGS) +{ + /* + * on_msg checks: CAXS + */ + Ban *ban; + Chan *chan; + char *nick,*nuh; + + nick = (rest) ? chop(&rest) : NULL; + + if (((chan = find_channel_ac(to)) == NULL) || !chan->bot_is_op) + return; + + if (nick && STRCHR(nick,'*')) + { + channel_massunban(chan,nick,0); + return; + } + + if (!nick) + { + to_user(from,"You have been unbanned on %s",to); + nuh = from; + } + else + { + if ((nuh = nick2uh(from,nick)) == NULL) + return; + to_user(from,"%s unbanned on %s",nuh,to); + } + + for(ban=chan->banlist;ban;ban=ban->next) + { + if (!matches(ban->banstring,nuh)) + { + send_mode(chan,90,QM_RAWMODE,'-','b',(void*)ban->banstring); + } + } +} + +void do_banlist(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + */ + Ban *ban; + Chan *chan = CurrentChan; +#ifdef IRCD_EXTENSIONS + char *s; + + if (chan->banlist) + { + table_buffer(str_underline("channel") "\t" str_underline("type") "\t" str_underline("mask") "\t" str_underline("set by")); + for(ban=chan->banlist;ban;ban=ban->next) + { +#ifdef NEWBIE + if (ban->imode) + s = "invitelist"; + else + if (ban->emode) + s = "ban exception"; + else + s = "ban"; +#else /* NEWBIE */ + if (ban->imode) + s = "I"; + else + if (ban->emode) + s = "e"; + else + s = "b"; +#endif /* NEWBIE */ + + table_buffer("%s\t%s\t%s\t%s",to,s,ban->banstring,ban->bannedby); + } + table_send(from,2); + } + else + to_user(from,"There are no active bans/exceptions on %s",to); +#else /* IRCD_EXTENSIONS */ + if (chan->banlist) + { + table_buffer(str_underline("channel") "\t\037ban mask\037\t\037set by\037"); + for(ban=chan->banlist;ban;ban=ban->next) + table_buffer("%s\t%s\t%s",to,ban->banstring,ban->bannedby); + table_send(from,2); + } + else + to_user(from,"There are no active bans on %s",to); +#endif /* IRCD_EXTENSIONS */ +} diff --git a/src/python.c b/src/python.c new file mode 100644 index 0000000..39ae745 --- /dev/null +++ b/src/python.c @@ -0,0 +1,973 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2009 Nemesis128 + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/* Python scripting backend for Energymech + +by Nemesis128 + +*/ + +#define PYTHON_C +#include "config.h" + +#ifdef PYTHON + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#define PYVAR_guid 0 +#define PYVAR_currentnick 1 +#define PYVAR_botnick 2 +#define PYVAR_wantnick 3 +#define PYVAR_userhost 4 +#define PYVAR_server 5 +#define PYVAR_nextserver 6 +#define PYVAR_currentchan 7 + +#if PY_MAJOR_VERSION >= 3 + +char *python_unicode2char(PyUnicodeObject *obj) +{ /* try to get a valid char* from PyUnicode object. + returned str must be free'd afterwards. */ + int sz = PyUnicode_GET_SIZE(obj); + wchar_t *wcs = (wchar_t*) malloc(sizeof(wchar_t) * sz); + PyUnicode_AsWideChar(obj, wcs, sz); + char *cs = (char*) malloc(sizeof(char) * (sz + 1)); + wcstombs(cs, wcs, sz); + cs[sz] = '\0'; + free(wcs); + return cs; +} + +#endif /* Py3k */ + +static PyObject *python_error; /* emech exception object */ + +static PyObject *python_getvar(PyObject *self, PyObject *args) +{ /* get some global var */ +#ifdef DEBUG + if (!current) + { + PyErr_SetString(python_error, "(python_getvar) No current bot?!"); + return NULL; + } +#endif /* DEBUG */ + int vartype; + if (!PyArg_ParseTuple(args, "i", &vartype)) + return NULL; + switch (vartype) + { + case PYVAR_guid: + return Py_BuildValue("i", current ? current->guid : -1); + case PYVAR_currentnick: + return Py_BuildValue("s", CurrentNick); + case PYVAR_botnick: + return Py_BuildValue("s", current && current->nick ? current->nick : ""); + case PYVAR_wantnick: + return Py_BuildValue("s", current && current->wantnick ? current->wantnick : ""); + case PYVAR_userhost: + return Py_BuildValue("s", current && current->userhost ? current->userhost : ""); + case PYVAR_server: + return Py_BuildValue("i", current && current->server ? current->server : -1); + case PYVAR_nextserver: + return Py_BuildValue("i", current && current->nextserver ? current->nextserver : -1); + case PYVAR_currentchan: + return Py_BuildValue("s", current && current->activechan ? current->activechan->name : ""); + default: + PyErr_SetString(python_error, "(python_getvar) invalid var"); + return NULL; + } +} + +/* From channel.c */ + +static PyObject *python_is_chanuser(PyObject *self, PyObject *args, PyObject *keywds) +/* Return True if $nick is on $chan, else False */ +{ + char *chan, *nick; + static char *kwlist[] = {"chan", "nick", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss", kwlist, &chan, &nick)) + return NULL; + Chan *ch = (Chan*) find_channel(chan, CHAN_ANY); + if (!ch) Py_RETURN_FALSE; + ChanUser *cu = find_chanuser(ch, nick); + if (!cu) Py_RETURN_FALSE; + Py_RETURN_TRUE; +} + +#if 0 + +static PyObject *python_do_join(PyObject *self, PyObject *args, PyObject *keywds) +/* Join a channel */ +{ + char *from, *to="", *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss|i", kwlist, + &from, &rest, &cmdaccess)) + return NULL; + do_join(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_part(PyObject *self, PyObject *args, PyObject *keywds) +/* Part a channel */ +{ + char *from, *to, *rest=""; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss", kwlist, &from, &to)) + return NULL; + do_part(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_cycle(PyObject *self, PyObject *args, PyObject *keywds) +/* Cycle a channel */ +{ + char *from, *to, *rest=""; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss", kwlist, &from, &to)) + return NULL; + do_cycle(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_wall(PyObject *self, PyObject *args, PyObject *keywds) +/* Wall */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_wall(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_mode(PyObject *self, PyObject *args, PyObject *keywds) +/* Mode */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_mode(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_invite(PyObject *self, PyObject *args, PyObject *keywds) +/* Invite */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_invite(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_sayme(PyObject *self, PyObject *args, PyObject *keywds) +/* Say /me */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_sayme(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_topic(PyObject *self, PyObject *args, PyObject *keywds) +/* Set topic */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_topic(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_idle(PyObject *self, PyObject *args, PyObject *keywds) +/* Check idle time */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_idle(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +#endif /* 0 */ + +static PyObject *python_find_nuh(PyObject *self, PyObject *args, PyObject *keywds) +/* Find nuh */ +{ + char *nick, *nuh; + static char *kwlist[] = {"nick", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", kwlist, &nick)) + return NULL; + nuh = find_nuh(nick); + if (nuh == NULL) Py_RETURN_NONE; + else return Py_BuildValue("s", nuh); +} + +/* From core.c */ + +#if 0 + +static PyObject *python_do_die(PyObject *self, PyObject *args, PyObject *keywds) +{ + char *from="", *to="", *rest=""; + int cmdaccess=0; + static char *kwlist[] = {"from", "rest", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|s", kwlist, &from, &rest)) + return NULL; + do_die(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_shutdown(PyObject *self, PyObject *args, PyObject *keywds) +{ + char *from="", *to="", *rest=""; + int cmdaccess=0; + static char *kwlist[] = {"from", "rest", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|s", kwlist, &from, &rest)) + return NULL; + do_shutdown(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +static PyObject *python_do_server(PyObject *self, PyObject *args, PyObject *keywds) +/* Connect server */ +{ + char *from, *to, *rest; + int cmdaccess = 100; + static char *kwlist[] = {"from", "to", "rest", "cmdaccess", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss|i", kwlist, + &from, &to, &rest, &cmdaccess)) + return NULL; + do_server(from, to, rest, cmdaccess); + Py_RETURN_NONE; +} + +#endif /* 0 */ + +PyObject *python_hook(PyObject *self, PyObject *args, PyObject *keywds) +{ + Hook *hook; + HookTimer hooktimer; + PyObject *cb, *funcname; + char *type, *command, *cbname; + int guid = 0, mode, sz1, sz2; + + static char *kwlist[] = {"type", "command", "callback", "guid", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ssO|i", kwlist, + &type, &command, &cb, &guid)) + return NULL; + + /* check callback function */ + if (!PyFunction_Check(cb)) + { + PyErr_SetString(python_error, "(python_hook) callback is not a function"); + return NULL; + } + funcname = PyObject_GetAttrString(cb, "__name__"); /* new ref */ + if (!funcname) + { + PyErr_SetString(python_error, "(python_hook) cant get function name"); + return NULL; + } + +#if PY_MAJOR_VERSION >= 3 + cbname = python_unicode2char((PyUnicodeObject*) funcname); +#else + cbname = PyString_AsString(funcname); /* not a copy! */ +#endif /* Py3k */ + + mode = strlen(type); + sz1 = strlen(command); + sz2 = strlen(cbname); + if (!mode || !sz1 || !sz2) + { + Py_DECREF(funcname); +#if PY_MAJOR_VERSION >= 3 + free(cbname); +#endif /* Py3k */ + PyErr_SetString(python_error, "(python_hook) invalid params"); + return NULL; + } + + if (!Strcasecmp(type, "command")) + mode = HOOK_COMMAND; + else + if (!Strcasecmp(type, "dcc_complete")) + mode = HOOK_DCC_COMPLETE; + else + if (!Strcasecmp(type, "parse")) + mode = HOOK_PARSE; + else + if (!Strcasecmp(type, "timer")) + { + if (compile_timer(&hooktimer, command) < 0) + { + Py_DECREF(funcname); +#if PY_MAJOR_VERSION >= 3 + free(cbname); +#endif /* Py3k */ + PyErr_SetString(python_error, "(python_hook) cant compile timer"); + return NULL; + } + mode = HOOK_TIMER; + sz1 = sizeof(HookTimer); + } + else + { + Py_DECREF(funcname); +#if PY_MAJOR_VERSION >= 3 + free(cbname); +#endif /* Py3k */ + PyErr_SetString(python_error, "(python_hook) invalid hook type"); + return NULL; + } + + set_mallocdoer(python_hook); + hook = (Hook*) Calloc(sizeof(Hook) + sz1 + sz2); + hook->guid = guid; + hook->flags = mode; + hook->next = hooklist; + hooklist = hook; + + hook->type.any = (void*) (Strcpy(hook->self, cbname) + 1); + + switch(mode) + { + case HOOK_COMMAND: + case HOOK_PARSE: + Strcpy(hook->type.command, command); + hook->func = python_parse_jump; + break; + default: + /* case HOOK_TIMER: */ + memcpy(hook->type.timer, &hooktimer, sizeof(HookTimer)); + hook->func = python_timer_jump; + break; + } + +#ifdef DEBUG + debug("(python_hook) hooked %s `%s' --> %s\n", + nullstr(type), nullstr(command), nullstr(cbname)); +#endif /* DEBUG */ + + Py_DECREF(funcname); +#if PY_MAJOR_VERSION >= 3 + free(cbname); +#endif /* Py3k */ + Py_RETURN_NONE; +} + +PyObject *python_unhook(PyObject *self, PyObject *args, PyObject *keywds) +{ + return Py_BuildValue("i", 1); +} + +int python_parse_jump(char *from, char *rest, Hook *hook) +{ + int i; + PyObject *mainmod, *cb, *res; + +#ifdef DEBUG + debug("(python_parse_jump) %s %s %s\n", + nullstr(hook->self), nullstr(from), nullstr(rest)); +#endif /* DEBUG */ + + if (from) + nickcpy(CurrentNick, from); + else + *CurrentNick = 0; + + /* get callback object */ + mainmod = PyImport_AddModule("__main__"); + if (!mainmod) + { +#ifdef DEBUG + debug("(python_parse_jump) cant get main module\n"); +#endif /* DEBUG */ + return 0; /* python exception has been set */ + } + cb = PyDict_GetItemString(PyModule_GetDict(mainmod), hook->self); + if (!PyFunction_Check(cb)) + { +#ifdef DEBUG + debug("(python_parse_jump) invalid callback function\n"); +#endif /* DEBUG */ + return 0; + } + + /* call our function */ + res = PyObject_CallFunction(cb, "ss", from, rest); /* new ref */ +#if PY_MAJOR_VERSION >= 3 + i = PyLong_AsLong(res); +#else + i = PyInt_AS_LONG(res); +#endif /* Py3k */ + +#ifdef DEBUG + debug("(python_parse_jump) result = %d\n", i); +#endif + Py_DECREF(res); + return i; +} + +int python_timer_jump(Hook *hook) +{ + return 0; +} + +#ifdef DCC_FILE + +void python_dcc_complete(Client *client, int cps) +{ + PyObject *mainmod = NULL, *cb, *res; + Hook *hook; + + for (hook = hooklist; hook; hook = hook->next) + { + if (hook->flags == HOOK_DCC_COMPLETE && + hook->guid && current && hook->guid == current->guid) + { + /* get callback object */ + if (!mainmod) + mainmod = PyImport_AddModule("__main__"); + if (!mainmod) + { +#ifdef DEBUG + debug("(python_dcc_complete) cant get main module\n"); +#endif /* DEBUG */ + return; /* python exception has been set */ + } + cb = PyDict_GetItemString(PyModule_GetDict(mainmod), hook->self); + if (!PyFunction_Check(cb)) + { +#ifdef DEBUG + debug("(python_dcc_complete) invalid callback function\n"); +#endif /* DEBUG */ + return; + } + + /* call our function */ + res = PyObject_CallFunction(cb, "ssi", + client->whom, client->filename, cps); /* new ref */ +#ifdef DEBUG +#if PY_MAJOR_VERSION >= 3 + debug("(python_dcc_complete) result = %d\n", PyLong_AsLong(res)); +#else + debug("(python_dcc_complete) result = %d\n", PyInt_AS_LONG(res)); +#endif /* Py3k */ +#endif /* DEBUG */ + Py_DECREF(res); + } + } +} + +#endif /* DCC_FILE */ + +PyObject *python_userlevel(PyObject *self, PyObject *args, PyObject *keywds) +{ +#ifdef DEBUG + if (!current) + { + PyErr_SetString(python_error, "(python_userlevel) No current bot?!"); + return NULL; + } +#endif /* DEBUG */ + char *nuh, *chan; + int n; + static char *kwlist[] = {"nuh", "chan", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|s", kwlist, &nuh, &chan)) + return NULL; + n = get_useraccess(nuh, chan); + return Py_BuildValue("i", n); +} + +PyObject *python_to_server(PyObject *self, PyObject *args, PyObject *keywds) +{ +#ifdef DEBUG + if (!current) + { + PyErr_SetString(python_error, "(python_to_server) No current bot?!"); + return NULL; + } +#endif /* DEBUG */ + Strp *sp, **pp; + char *line; + int sendqmax = -1, n, sz; + static char *kwlist[] = {"line", "sendqmax", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|i", kwlist, &line, &sendqmax)) + return NULL; +#ifdef DEBUG + debug("(python_to_server) max = %i; line = %s\n", sendqmax, line); +#endif /* DEBUG */ + sz = strlen(line); + if (sendqmax >= 0) + { + n = 0; + pp = ¤t->sendq; + while (*pp) + { + n++; + pp = &(*pp)->next; + } + if (sendqmax && n >= sendqmax) n = -n; + else + if (sz) + { + *pp = sp = (Strp*) Calloc(sizeof(Strp) + sz); + /* Calloc sets to zero sp->next = NULL; */ + Strcpy(sp->p, line); + } + } + else + { + if ((n = write(current->sock, line, sz)) < 0) + { +#ifdef DEBUG + debug("(python_to_server) {%i} errno = %i\n", current->sock, errno); +#endif /* DEBUG */ + close(current->sock); + current->sock = -1; + current->connect = CN_NOSOCK; + PyErr_SetString(python_error, "Socket error"); + return NULL; + } + current->sendq_time += 2; + } + return Py_BuildValue("i", n); +} + +PyObject *python_to_file(PyObject *self, PyObject *args, PyObject *keywds) +{ + int fd, r; + char *txt; + static char *kwlist[] = {"fd", "text", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "is", kwlist, &fd, &txt)) + return NULL; + r = write(fd, txt, strlen(txt)); + return Py_BuildValue("i", r); +} + +#ifdef DCC_FILE + +static PyObject *python_dcc_sendfile(PyObject *self, PyObject *args, PyObject *keywds) +{ + char *filename, *target; + int sz; + static char *kwlist[] = {"file", "target", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss", kwlist, &filename, &target)) + return NULL; + if ((sz = dcc_sendfile(target, filename)) < 0) + { + PyErr_SetString(python_error, "(python_dcc_sendfile) error!"); + return NULL; + } + return Py_BuildValue("i", sz); +} + +#endif /* DCC_FILE */ + +#ifdef DEBUG + +PyObject *python_debug(PyObject *self, PyObject *args) +{ + PyObject *o; + if (!PyArg_ParseTuple(args, "O", &o)) + return NULL; + o = PyObject_Repr(o); + if (!o) + { + debug("(python_debug) object has no __repr__ method\n"); + Py_RETURN_NONE; + } +#if PY_MAJOR_VERSION >= 3 + char *cs; + cs = python_unicode2char((PyUnicodeObject*) o); + debug("(python_debug) %s\n", cs); + free(cs); +#else + debug("(python_debug) %s\n", PyString_AsString(o)); +#endif /* Py3k */ + Py_RETURN_NONE; +} + +#endif /* DEBUG */ + +#ifdef RAWDNS + +int python_dns_jump(char *host, char *resolved, Hook *hook) +{ + PyObject *mainmod, *cb, *res; + int i = 0; +#ifdef DEBUG + debug("(python_dns_jump) %s %s %s\n", + nullstr(hook->self), nullstr(host), nullstr(resolved)); +#endif /* DEBUG */ + + /* get callback object */ + mainmod = PyImport_AddModule("__main__"); + if (!mainmod) + { +#ifdef DEBUG + debug("(python_dns_jump) cant get main module\n"); +#endif /* DEBUG */ + return 0; /* python exception has been set */ + } + cb = PyDict_GetItemString(PyModule_GetDict(mainmod), hook->self); + if (!PyFunction_Check(cb)) + { +#ifdef DEBUG + debug("(python_dns_jump) invalid callback function\n"); +#endif /* DEBUG */ + return 0; + } + + /* call our function */ + res = PyObject_CallFunction(cb, "ss", host, resolved); /* new ref */ +#if PY_MAJOR_VERSION >= 3 + i = PyLong_AsLong(res); +#else + i = PyInt_AS_LONG(res); +#endif /* Py3k */ + +#ifdef DEBUG + debug("(python_dns_jump) result = %d\n", i); +#endif /* DEBUG */ + Py_DECREF(res); + return i; +} + +static PyObject *python_dns(PyObject *self, PyObject *args, PyObject *keywds) +{ + PyObject *cb, *funcname; + Hook *hook; + char *host, *cbname; + + static char *kwlist[] = {"host", "callback", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO", kwlist, host, cb)) + return NULL; + + /* check callback function */ + if (!PyFunction_Check(cb)) + { + PyErr_SetString(python_error, "(python_dns) callback is not a function"); + return NULL; + } + funcname = PyObject_GetAttrString(cb, "__name__"); /* new ref */ + if (!funcname) + { + PyErr_SetString(python_error, "(python_dns) cant get function name"); + return NULL; + } + +#if PY_MAJOR_VERSION >= 3 + cbname = python_unicode2char((PyUnicodeObject*) funcname); +#else + cbname = PyString_AsString(funcname); /* not a copy! */ +#endif /* Py3k */ + +#ifdef DEBUG + debug("(python_dns) resolving: %s (callback %s)\n", host, cbname); +#endif /* DEBUG */ + + set_mallocdoer(python_dns); + hook = (Hook*)Calloc(sizeof(Hook) + strlen(host)); + hook->guid = (current) ? current->guid : 0; + hook->flags = HOOK_DNS; + hook->next = hooklist; + hooklist = hook; + hook->type.host = Strcpy(hook->self, cbname) + 1; + Strcpy(hook->type.host, host); + hook->func = python_dns_jump; + + rawdns(host); +#if PY_MAJOR_VERSION >= 3 + free(cbname); +#endif /* Py3k */ + Py_DECREF(funcname); + Py_RETURN_NONE; +} + +#endif /* RAWDNS */ + +static PyObject *python_execute(PyObject *self, PyObject *args) +{ + PyObject *cmd, *from, *to, *rest; + char *c_cmd, *c_from, *c_to, *c_rest; + int cmdaccess, i; + void (*found)(char*,char*,char*,int) = NULL; + + if (!PyArg_ParseTuple(args, "OOOOi", &cmd, &from, &to, &rest, &cmdaccess)) + return NULL; + + /* check for 4 strings */ +#if PY_MAJOR_VERSION >= 3 + if (!PyUnicode_CheckExact(cmd) + || !(PyUnicode_CheckExact(from))/* || Py_None == from)*/ + || !PyUnicode_CheckExact(to) + || !PyUnicode_CheckExact(rest)) +#else + if (!PyString_CheckExact(cmd) + || !(PyString_CheckExact(from))/* || Py_None == from)*/ + || !PyString_CheckExact(to) + || !PyString_CheckExact(rest)) +#endif /* Py3k */ + { + PyErr_SetString(python_error, "(python_execute) invalid arguments"); + return NULL; + } + + /* extract vars */ +#if PY_MAJOR_VERSION >= 3 + c_cmd = python_unicode2char((PyUnicodeObject*) cmd); + if (from != Py_None) c_from = python_unicode2char((PyUnicodeObject*) from); + c_to = python_unicode2char((PyUnicodeObject*) to); + c_rest = python_unicode2char((PyUnicodeObject*) rest); +#else + c_cmd = PyString_AsString(cmd); /* not a copy */ + if (from != Py_None) c_from = PyString_AsString(from); + c_to = PyString_AsString(to); + c_rest = PyString_AsString(rest); +#endif /* Py3k */ + + /* + if (from == Py_None) + { + from = LocalBot..; + }*/ + + /* check command exists */ + for (i = 0; mcmd[i].name; ++i) + { + if (!Strcmp(mcmd[i].name, c_cmd)) + { + found = mcmd[i].func; + break; + } + } + if (!found) + { +#ifdef DEBUG + debug("(python_execute) squeeze me (%s) !?!\n", c_cmd); +#endif /* DEBUG */ +#if PY_MAJOR_VERSION >= 3 + free(c_cmd); if (from != Py_None) free(c_from); free(c_to); free(c_rest); +#endif /* Py3k */ + Py_RETURN_NONE; + } + +#ifdef DEBUG + debug("(python_execute) (%s) (%s) (%s) (%s) (%d)\n", c_cmd, c_from, c_to, c_rest, cmdaccess); +#endif /* DEBUG */ + + (*found)(c_from, c_to, c_rest, cmdaccess); + +#if PY_MAJOR_VERSION >= 3 + free(c_cmd); if (from != Py_None) free(c_from); free(c_to); free(c_rest); +#endif /* Py3k */ + + Py_RETURN_NONE; +} + +static struct PyMethodDef pythonMethods[] = +{ +{"getvar", (PyCFunction) python_getvar, METH_VARARGS, "Get variable"}, +{"userlevel", (PyCFunction) python_userlevel, METH_VARARGS|METH_KEYWORDS, "User level"}, +/* From channel.c */ +{"is_chanuser", (PyCFunction) python_is_chanuser, METH_VARARGS|METH_KEYWORDS, "Check nick is in channel"}, +#if 0 +{"join", (PyCFunction) python_do_join, METH_VARARGS|METH_KEYWORDS, "Join a channel"}, +{"part", (PyCFunction) python_do_part, METH_VARARGS|METH_KEYWORDS, "Part a channel"}, +{"cycle", (PyCFunction) python_do_cycle, METH_VARARGS|METH_KEYWORDS, "Cycle a channel"}, +{"wall", (PyCFunction) python_do_wall, METH_VARARGS|METH_KEYWORDS, "Do wall"}, +{"mode", (PyCFunction) python_do_mode, METH_VARARGS|METH_KEYWORDS, "Do mode"}, +{"invite", (PyCFunction) python_do_invite, METH_VARARGS|METH_KEYWORDS, "Do invite"}, +{"sayme", (PyCFunction) python_do_sayme, METH_VARARGS|METH_KEYWORDS, "Do sayme"}, +{"topic", (PyCFunction) python_do_topic, METH_VARARGS|METH_KEYWORDS, "Do topic"}, +{"idle", (PyCFunction) python_do_idle, METH_VARARGS|METH_KEYWORDS, "Do idle"}, +#endif /* 0 */ +{"find_nuh", (PyCFunction) python_find_nuh, METH_VARARGS|METH_KEYWORDS, "Find nuh"}, +/* From core.c */ +#if 0 +{"die", (PyCFunction) python_do_die, METH_VARARGS|METH_KEYWORDS, "Die"}, +{"shutdown", (PyCFunction) python_do_shutdown, METH_VARARGS|METH_KEYWORDS, "Shutdown"}, +{"server", (PyCFunction) python_do_server, METH_VARARGS|METH_KEYWORDS, "Connect server"}, +#endif /* 0 */ +/* Scripting stuff */ +{"hook", (PyCFunction) python_hook, METH_VARARGS|METH_KEYWORDS, "Create hook"}, +{"unhook", (PyCFunction) python_unhook, METH_VARARGS|METH_KEYWORDS, "Destroy hook"}, +#ifdef DCC_FILE +{"dcc_sendfile", (PyCFunction) python_dcc_sendfile, METH_VARARGS|METH_KEYWORDS, "DCC send file"}, +#endif /* DCC_FILE */ +{"to_server", (PyCFunction) python_to_server, METH_VARARGS|METH_KEYWORDS, "Write line to server"}, +{"to_file", (PyCFunction) python_to_file, METH_VARARGS|METH_KEYWORDS, "Write text to file"}, +#ifdef DEBUG +{"debug", (PyCFunction) python_debug, METH_VARARGS, "Emech debug"}, +#endif /* DEBUG */ +#ifdef RAWDNS +{"dns", (PyCFunction) python_dns, METH_VARARGS|METH_KEYWORDS, "DNS"}, +#endif /* RAWDNS */ +{"execute", (PyCFunction) python_execute, METH_VARARGS, "Execute internal command"}, +{NULL, NULL, 0, NULL} /* sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 + +struct PyModuleDef pythonModule = +{ + PyModuleDef_HEAD_INIT, + "emech", /* module name */ + "EnergyMech", /* module docstring */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + pythonMethods +}; + +#endif /* Py3k */ + +PyMODINIT_FUNC pythonInit(void) +{ + PyObject *m; + +#if PY_MAJOR_VERSION == 3 + if ((m = PyModule_Create(&pythonModule)) == NULL) + return NULL; +#else + m = Py_InitModule4("emech", pythonMethods, + "EnergyMech", (PyObject*) NULL, PYTHON_API_VERSION); +#endif /* Py3k */ + + python_error = PyErr_NewException("emech.Error", NULL, NULL); + Py_INCREF(python_error); + PyModule_AddObject(m, "Error", python_error); + +#ifdef DEBUG + PyModule_AddIntConstant(m, "define_debug", 1); +#else + PyModule_AddIntConstant(m, "define_debug", 0); +#endif /* DEBUG */ + + PyModule_AddStringConstant(m, "HOOK_COMMAND", "command"); + PyModule_AddStringConstant(m, "HOOK_PARSE", "parse"); + PyModule_AddStringConstant(m, "HOOK_TIMER", "timer"); + PyModule_AddStringConstant(m, "DCC_COMPLETE", "dcc_complete"); + + PyModule_AddIntConstant(m, "OK", 0); + PyModule_AddIntConstant(m, "ERROR", 1); + + PyModule_AddIntConstant(m, "GUID", PYVAR_guid); + PyModule_AddIntConstant(m, "CURRENTNICK", PYVAR_currentnick); + PyModule_AddIntConstant(m, "BOTNICK", PYVAR_botnick); + PyModule_AddIntConstant(m, "WANTNICK", PYVAR_wantnick); + PyModule_AddIntConstant(m, "USERHOST", PYVAR_userhost); + PyModule_AddIntConstant(m, "SERVER", PYVAR_server); + PyModule_AddIntConstant(m, "NEXTSERVER", PYVAR_nextserver); + PyModule_AddIntConstant(m, "CURRENTCHAN", PYVAR_currentchan); + + PyModule_AddStringConstant(m, "VERSION", (char *)VERSION); + PyModule_AddStringConstant(m, "SRCDATE", (char *)SRCDATE); + PyModule_AddStringConstant(m, "BOTCLASS", (char *)BOTCLASS); + + if (PyErr_Occurred()) + { + PyErr_Print(); + Py_FatalError("Cant initialize module emech!"); + } + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif /* Py3k */ +} + +void init_python(void) +{ + if (Py_IsInitialized()) return; + + PyImport_AppendInittab((char*) "emech", pythonInit); + +#if PY_MAJOR_VERSION >= 3 + Py_SetProgramName(L"energymech"); + wchar_t *argv2[] = {L""}; +#else + Py_SetProgramName("energymech"); + char *argv2[] = {""}; +#endif /* Py3k */ + + Py_Initialize(); + PySys_SetArgv(1, argv2); +} + +#ifdef PLEASE_HACK_MY_SHELL + +void do_python(COMMAND_ARGS) +{ + init_python(); + int res = PyRun_SimpleString(rest); + to_user(from, "python command %s", (res == 0) ? "executed ok" : "failed"); +} + +#endif /* PLEASE_HACK_MY_SHELL */ + +void do_pythonscript(COMMAND_ARGS) +{ + init_python(); + FILE *f = fopen(rest, "r"); + if (f == NULL) + { + to_user(from, "python script %s not existing or unreadable", rest); + return; + } + int res = PyRun_SimpleFileEx(f, rest, 1); + to_user(from,"python script %s", (res == 0) ? "loaded ok" : "failed to load"); +} + +void free_python(void) +{ + if (Py_IsInitialized()) Py_Finalize(); +} + +#endif /* PYTHON */ diff --git a/src/redirect.c b/src/redirect.c new file mode 100644 index 0000000..d89f64e --- /dev/null +++ b/src/redirect.c @@ -0,0 +1,167 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define REDIRECT_C +#include "config.h" + +#ifdef REDIRECT + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +int begin_redirect(char *from, char *args) +{ + char *pt,*nick; + + if (!args) + return(0); + pt = STRCHR(args,'>'); + if (pt) + { + *pt = 0; + nick = pt+1; + pt--; + while((pt > args) && (*pt == ' ')) + { + *pt = 0; + pt--; + } + while(*nick == ' ') + nick++; + if (*nick) + { +#ifdef DEBUG + debug("(begin_redirect) from %s --> %s\n",from,nick); +#endif /* DEBUG */ + if (ischannel(nick)) + { + if (find_channel_ac(nick)) + { + redirect.to = Strdup(nick); + redirect.method = R_PRIVMSG; + return(0); + } + else + { + to_user(from,ERR_CHAN,nick); + return(-1); + } + } + if (*nick == '>') + { + nick++; + while(*nick == ' ') + nick++; + if (!*nick) + { + to_user(from,"Missing name for redirect."); + return(-1); + } + if (!is_safepath(nick)) + { + to_user(from,"Bad filename."); + return(-1); + } + redirect.to = Strdup(nick); + redirect.method = R_FILE; + return(0); + } + if ((pt = find_nuh(nick))) + { + redirect.to = Strdup(nick); + redirect.method = R_NOTICE; + return(0); + } + else + { + to_user(from,TEXT_UNKNOWNUSER,nick); + return(-1); + } + } + else + { + to_user(from,"Bad redirect"); + return(-1); + } + } + return(0); +} + +void send_redirect(char *message) +{ + Strp *new,**pp; + char *fmt; + int fd; + + if (!redirect.to) + return; + + switch(redirect.method) + { + case R_FILE: + if ((fd = open(redirect.to,O_WRONLY|O_CREAT|O_APPEND,NEWFILEMODE)) < 0) + return; + fmt = Strcat(message,"\n"); + write(fd,message,(fmt-message)); + close(fd); + return; +#ifdef BOTNET + case R_BOTNET: + { + char tempdata[MAXLEN]; + Mech *backup; + + /* PM */ + sprintf(tempdata,"%i %s %s %s",redirect.guid,redirect.to,current->nick,message); + backup = current; + partyMessage(NULL,tempdata); + current = backup; + } + return; +#endif /* BOTNET */ + case R_NOTICE: + fmt = "NOTICE %s :%s"; + break; + /* case R_PRIVMSG: */ + default: + fmt = "PRIVMSG %s :%s"; + break; + } + + pp = ¤t->sendq; + while(*pp) + pp = &(*pp)->next; + + *pp = new = (Strp*)Calloc(sizeof(Strp) + Strlen(message,fmt,redirect.to,NULL)); + /* Calloc sets to zero new->next = NULL; */ + sprintf(new->p,fmt,redirect.to,message); +} + +void end_redirect(void) +{ + if (redirect.to) + Free((char**)&redirect.to); +} + +#endif /* REDIRECT */ diff --git a/src/reset.c b/src/reset.c new file mode 100644 index 0000000..1888bd3 --- /dev/null +++ b/src/reset.c @@ -0,0 +1,430 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2008 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define RESET_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" + +#ifdef TELNET +LS int client_type = DCC_ACTIVE; +#endif /* TELNET */ + +char *recover_client(char *env) +{ + struct sockaddr_in sai; + Client *client; + User *user; + char *p,*handle; + int guid,fd,sz; + + guid = fd = 0; + p = env; + + /* + * get the guid number + */ + while(*p >= '0' && *p <= '9') + { + guid = (guid * 10) + (*p - '0'); + p++; + } + if (*p != ':') + return(env); + p++; + + /* + * get the fd number + */ + while(*p >= '0' && *p <= '9') + { + fd = (fd * 10) + (*p - '0'); + p++; + } + if (*p != ':') + return(env); + p++; + + handle = p; + while(*p) + { + if (*p == ' ' || *p == 0) + break; + p++; + } + if (p == handle) + return(env); + + if (*p == ' ') + *(p++) = 0; + +#ifdef DEBUG + debug("(recover_client) guid = %i; fd = %i; handle = %s\n",guid,fd,handle); +#endif /* DEBUG */ + + /* + * check that it's an inet stream socket + */ + sz = sizeof(sai); + if (getsockname(fd,(struct sockaddr*)&sai,&sz) < 0) + { + close(fd); + return(p); + } + + for(current=botlist;current;current=current->next) + { + if (current->guid == guid) + { + for(user=current->userlist;user;user=user->next) + { + if (!Strcasecmp(user->name,handle)) + goto found_user; + } + break; + } + } + close(fd); + return(p); + +found_user: + if (to_file(fd,"[%s] [%s] %s[%i] has connected (reset recover)\n", + time2medium(now),current->wantnick,handle,user->x.x.access) < 0) + { + close(fd); + return(p); + } + + set_mallocdoer(recover_client); + client = (Client*)Calloc(sizeof(Client)); + client->user = user; + client->sock = fd; +#ifdef TELNET + client->flags = client_type; +#else + client->flags = DCC_ACTIVE; +#endif /* TELNET */ + client->lasttime = now; + + client->next = current->clientlist; + current->clientlist = client; + + if (user->x.x.access == OWNERLEVEL) + { + CurrentDCC = client; + Strcpy(client->sockdata,"status"); + do_spy(user->name,current->wantnick,client->sockdata,0); + *client->sockdata = 0; + CurrentDCC = NULL; + } + +#ifdef DEBUG + debug("(recover_client) client socket recovered\n"); +#endif /* DEBUG */ + + return(p); +} + +#ifdef DEBUG + +char *recover_debug(char *env) +{ + struct stat s; + char *p; + + debug_fd = 0; + p = env; + + /* + * get the fd number + */ + while(*p >= '0' && *p <= '9') + { + debug_fd = (debug_fd * 10) + (*p - '0'); + p++; + } + if (*p != ' ' && *p != 0) + return(env); + + if (fstat(debug_fd,&s) < 0) + { + dodebug = FALSE; + close(debug_fd); + debug_fd = -1; + } + else + { + dodebug = TRUE; + debug("(recover_debug) debug fd recovered\n"); + CoreClient.sock = debug_fd; + } + return(p); +} + +#endif /* DEBUG */ + +char *recover_server(char *env) +{ + struct sockaddr_in sai; + char *p; + int guid,fd,sz; +#ifdef IRCD_EXTENSIONS + int ircx = 0; +#endif /* IRCD_EXTENSIONS */ + + guid = fd = 0; + p = env; + + /* + * get the guid number + */ + while(*p >= '0' && *p <= '9') + { + guid = (guid * 10) + (*p - '0'); + p++; + } + if (*p != ':') + return(env); + p++; + + /* + * get the fd number + */ + while(*p >= '0' && *p <= '9') + { + fd = (fd * 10) + (*p - '0'); + p++; + } +#ifndef IRCD_EXTENSIONS + if (*p != ' ' && *p != 0) + return(env); +#endif /* ! IRCD_EXTENSIONS */ + +#ifdef IRCD_EXTENSIONS + if (*p == ':' && *(p+1) == 'X') + { + p += 2; + /* + * get the ircx flags number + */ + while(*p >= '0' && *p <= '9') + { + ircx = (ircx * 10) + (*p - '0'); + p++; + } + if (*p != ' ' && *p != 0) + return(env); + } + else + if (*p != ' ' && *p != 0) + return(env); +#ifdef DEBUG + debug("(recover_server) guid = %i; fd = %i, ircx = %i\n",guid,fd,ircx); +#endif /* DEBUG */ +#else /* IRCD_EXTENSIONS */ +#ifdef DEBUG + debug("(recover_server) guid = %i; fd = %i\n",guid,fd); +#endif /* DEBUG */ +#endif /* IRCD_EXTENSIONS */ + + sz = sizeof(sai); + if (getsockname(fd,(struct sockaddr*)&sai,&sz) < 0) + { + close(fd); + return(p); + } + for(current=botlist;current;current=current->next) + { + if (current->guid == guid) + { + current->reset = 1; + current->sock = fd; + current->connect = CN_ONLINE; + current->ontime = now; +#ifdef IRCD_EXTENSIONS + current->ircx_flags = ircx; +#endif /* IRCD_EXTENSIONS */ +#ifdef DEBUG + debug("(recover_server) {%i} server socket recovered\n",fd); +#endif /* DEBUG */ + to_file(fd,"LUSERS\n"); + break; + } + } + return(p); +} + +#ifdef IRCD_EXTENSIONS + +/* +#define IRCX_WALLCHOPS 1 +#define IRCX_WALLVOICES 2 +#define IRCX_IMODE 4 +#define IRCX_EMODE 8 +*/ + +#endif /* IRCD_EXTENSIONS */ + +void recover_reset(void) +{ + char *env = mechresetenv; + + mechresetenv = NULL; + + while(*env) + { + switch(*env) + { + case 'c': +#ifdef TELNET + client_type = DCC_ACTIVE; +#endif /* TELNET */ + env = recover_client(env+1); + break; +#ifdef DEBUG + case 'd': + env = recover_debug(env+1); + break; +#endif /* DEBUG */ +#ifdef TELNET + case 't': + client_type = DCC_TELNET; + env = recover_client(env+1); + break; +#endif /* TELNET */ + case 'f': + env = recover_server(env+1); + break; + default: + env++; + } + } +} + +/* + * + * commands + * + */ + +void do_reset(COMMAND_ARGS) +{ + Client *client; + Mech *backup; + char env[MSGLEN]; + char *p; + int n,sz; + + if (current->userlist && current->ul_save) + { + p = current->setting[STR_USERFILE].str_var; + write_userlist(p); + } + +#ifdef SESSION + write_session(); +#endif /* SESSION */ + +#ifdef SEEN + if (seenfile) + write_seenlist(); +#endif /* SEEN */ + +#ifdef TRIVIA + write_triviascore(); +#endif /* TRIVIA */ + +#ifdef NOTIFY + if (current->notifylist) + write_notifylog(); +#endif /* NOTIFY */ + + *env = 0; + p = Strcat(env,STR_MECHRESET); + n = 0; +#ifdef DEBUG + /* + * debug stuff + */ + if (dodebug && (debug_fd >= 0)) + { + sprintf(p,"d%i",debug_fd); + p = STREND(p); + n++; + } +#endif /* DEBUG */ + /* + * Save server connections + */ + for(backup=botlist;backup;backup=backup->next) + { + if ((backup->connect == CN_ONLINE) && ((MSGLEN - (p - env)) > 25)) + { + unset_closeonexec(backup->sock); + if (n) + *(p++) = ' '; +#ifdef IRCD_EXTENSIONS + sprintf(p,"f%i:%i:X%i",backup->guid,backup->sock,backup->ircx_flags); +#else /* IRCD_EXTENSIONS */ + sprintf(p,"f%i:%i",backup->guid,backup->sock); +#endif /* IRCD_EXTENSIONS */ + p = STREND(p); + n++; + } + for(client=backup->clientlist;client;client=client->next) + { +#ifdef TELNET + if ((client->flags & (DCC_ACTIVE|DCC_TELNET)) == 0) + continue; +#else + if (client->flags != DCC_ACTIVE) + continue; +#endif /* TELNET */ + sz = strlen(client->user->name) + 26; + if ((MSGLEN - (p - env)) > sz) + { + unset_closeonexec(client->sock); + if (n) + *(p++) = ' '; +#ifdef TELNET + sprintf(p,(client->flags & DCC_TELNET) ? "t%i:%i:%s" : "c%i:%i:%s", + backup->guid,client->sock,client->user->name); +#else + sprintf(p,"c%i:%i:%s",backup->guid,client->sock,client->user->name); +#endif /* TELNET */ + p = STREND(p); + n++; + } + } + } + +#ifdef DEBUG + debug("(do_reset) %s [%i]\n",env,(int)(p - env)); +#endif /* DEBUG */ + + mechresetenv = (n) ? env : NULL; + + do_exec = TRUE; + mech_exec(); + /* NOT REACHED */ +} diff --git a/src/seen.c b/src/seen.c new file mode 100644 index 0000000..40009ab --- /dev/null +++ b/src/seen.c @@ -0,0 +1,313 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define SEEN_C +#include "config.h" + +#ifdef SEEN + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +int write_seenlist(void) +{ + Seen *seen; + int f; +#ifdef DEBUG + int dodeb; +#endif /* DEBUG */ + + if (!seenfile) + return(FALSE); + if ((f = open(seenfile,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0) + return(FALSE); + +#ifdef DEBUG + dodeb = dodebug; + dodebug = FALSE; +#endif /* DEBUG */ + + for(seen=seenlist;seen;seen=seen->next) + { + if ((seen->when - now) > (86400 * SEEN_TIME)) + continue; + else + { + if (seen->t != 0) + { + to_file(f,"%s %s %lu %i %s %s\n", + seen->nick,seen->userhost, + seen->when,seen->t, + (seen->pa) ? seen->pa : "", + (seen->pb) ? seen->pb : ""); + } + } + } + close(f); + +#ifdef DEBUG + dodebug = dodeb; +#endif /* DEBUG */ + return(TRUE); +} + +int read_seenlist_callback(char *rest) +{ + char *nick,*uh,*pa,*pb; + time_t when; + int t; + + nick = chop(&rest); + uh = chop(&rest); + + when = a2i(chop(&rest)); /* seen time, a2i handles NULL */ + if (errno) + return(FALSE); + + t = a2i(chop(&rest)); /* seen type, a2i handles NULL */ + if (errno) + return(FALSE); + + pa = chop(&rest); + pb = rest; + + if ((now - when) < (SEEN_TIME * 86400)) + { + /* if (pa && !*pa) + pa = NULL; chop() doesnt return empty strings */ + if (!*pb) + pb = NULL; + make_seen(nick,uh,pa,pb,when,t); + } + return(FALSE); +} + +int read_seenlist(void) +{ + Seen *seen; + int in; +#ifdef DEBUG + int dodeb; +#endif /* DEBUG */ + + if (!seenfile || ((in = open(seenfile,O_RDONLY)) < 0)) + return(FALSE); + +#ifdef DEBUG + dodeb = dodebug; + dodebug = FALSE; +#endif /* DEBUG */ + + while((seen = seenlist)) + { + seenlist = seen->next; + Free((char**)&seen); + } + + readline(in,&read_seenlist_callback); /* readline closes in */ + +#ifdef DEBUG + dodebug = dodeb; +#endif /* DEBUG */ + return(TRUE); +} + +void make_seen(char *nick, char *userhost, char *pa, char *pb, time_t when, int t) +{ + Seen *seen,**pp; + char *pt; + uchar c1; + int i; + + for(pt=userhost;*pt;pt++) + { + if (*pt == '!') + { + userhost = pt + 1; + break; + } + } + + c1 = nickcmptab[(uchar)(*nick)]; + pt = nick + 1; + pp = &seenlist; + +step_one: + if (*pp) + { + if (c1 > nickcmptab[(uchar)(*(*pp)->nick)]) + { + pp = &(*pp)->next; + goto step_one; + } + } + +step_two: + if (*pp) + { + if (c1 == nickcmptab[(uchar)(*(*pp)->nick)]) + { + i = nickcmp(pt,(*pp)->nick+1); + if (i > 0) + { + pp = &(*pp)->next; + goto step_two; + } + if (!i) + { + seen = *pp; + *pp = seen->next; + Free((char**)&seen); + } + } + } + + /* + * dont fuck with this code unless you really know what you're doing + * pa might be NULL, but then pb is NULL also; pb might be NULL + * any NULL terminates the Strlen() check + */ + set_mallocdoer(make_seen); + seen = (Seen*)Calloc(sizeof(Seen) + Strlen(nick,userhost,pa,pb,NULL)); + + seen->next = *pp; + *pp = seen; + seen->when = when; + seen->t = t; + /* Calloc sets to zero seen->pa = seen->pb = NULL; */ + + seen->userhost = Strcpy(seen->nick,nick) + 1; + pt = Strcpy(seen->userhost,userhost) + 1; + if (pa) + { + seen->pa = pt; + pt = Strcpy(seen->pa,pa) + 1; + if (pb) + { + seen->pb = pt; + Strcpy(seen->pb,pb); + } + } + +} + +/* + * + * commands for seen features + * + */ + +void do_seen(COMMAND_ARGS) +{ + Seen *seen; + char ago[35]; /* enought for "36500 days, 23 hours and 59 minutes" (100 years) */ + char *chan,*fmt,*n,*u,*c1,*c2,*c3; + time_t when; + int d,h,m,mul; + + chan = get_channel(to,&rest); + mul = get_maxaccess(from); + + if (!*rest) + { + if (mul) to_user_q(from,"Who do you want me look for?"); + return; + } + + n = chop(&rest); + if (!is_nick(n)) + { + if (mul) to_user_q(from,ERR_NICK,n); + return; + } + + if (!nickcmp(n,current->nick)) + { + fmt = "%s is me you dweeb!"; + } + else + if (!nickcmp(n,from)) + { + fmt = "Trying to find yourself %s?"; + } + else + { + for(seen=seenlist;seen;seen=seen->next) + { + if (!Strcasecmp(n,seen->nick)) + break; + } + + if (!seen) + { + fmt = "I have no memory of %s"; + } + else + { + when = now - seen->when; + d = when / 86400; + h = (when -= d * 86400) / 3600; + m = (when -= h * 3600) / 60; + + *ago = 0; + c2 = ago; + + if (d) + { + sprintf(c2,"%i day%s, ",d,EXTRA_CHAR(d)); + } + if (h || d) + { + sprintf(ago,"%s%i hour%s and ",ago,h,EXTRA_CHAR(h)); + } + sprintf(ago,"%s%i minute%s",ago,m,EXTRA_CHAR(m)); + + n = seen->nick; + u = seen->userhost; + c1 = seen->pa; + c2 = ago; + + switch(seen->t) + { + case SEEN_PARTED: + fmt = "%s (%s) parted from %s, %s ago"; + break; + case SEEN_QUIT: + fmt = "%s (%s) signed off with message \"%s\", %s ago"; + break; + case SEEN_NEWNICK: + fmt = "%s (%s) changed nicks to %s, %s ago"; + break; + case SEEN_KICKED: + c2 = seen->pb; + c3 = ago; + fmt = "%s (%s) was kicked by %s with message \"%s\", %s ago"; + } + } + } + + to_user_q(from,fmt,n,u,c1,c2,c3); +} + +#endif /* SEEN */ diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000..0e7d80d --- /dev/null +++ b/src/settings.h @@ -0,0 +1,153 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef SETTINGS_H +#define SETTINGS_H 1 +#ifdef VARS_C + +#define DEFAULTCMDCHAR '-' + +#define ZERO 0 +#define INTCAST(x) (void*)((int)x) +#define CHARCAST (void*)((int)DEFAULTCMDCHAR) + +LS const Setting VarName[SIZE_VARS] = +{ +/* + * all channel settings in the beginning + */ +/* TYPE UACCES MIN DEFAULT NAME MAX */ +{ TOG_VAR, 40, 0, ZERO, "ABK", 1 }, +{ TOG_VAR, 40, 0, ZERO, "AOP", 1 }, /* autoop enable */ +{ INT_VAR, 40, 0, ZERO, "AUB", 86400 }, +{ INT_VAR, 40, 0, INTCAST(1), "AVOICE", 2 }, +#ifdef CHANBAN +{ TOG_VAR, 40, 0, ZERO, "CHANBAN", 1, }, /* chanban enable */ +#endif /* CHANBAN */ +{ INT_VAR, 40, 0, ZERO, "CKL", 20 }, +{ TOG_VAR, 40, 0, ZERO, "CTL", 1 }, +#ifdef DYNAMODE +{ STR_VAR, 40, 0, NULL, "DYNLIMIT", 1 }, /* settings for dynamode: `delay:window:minwin' */ +#endif /* DYNAMODE */ +{ TOG_VAR, 40, 0, ZERO, "ENFM", 1 }, +{ STR_VAR, 40, 0, NULL, "ENFMODES" }, /* modes to enforce, +ENFM to enable */ +{ INT_VAR, 40, 0, INTCAST(6), "FL", 20 }, /* number of lines that counts as a text flood */ +{ INT_VAR, 40, 0, ZERO, "FPL", 2 }, +{ INT_VAR, 40, 0, INTCAST(0), "IKT", 40320 }, /* idle-kick: minutes of idle-time (max 4 weeks) */ +{ TOG_VAR, 40, 0, ZERO, "KS", 1 }, /* kicksay enable */ +{ INT_VAR, 40, 0, INTCAST(90), "MAL", 200 }, +{ INT_VAR, 40, 2, INTCAST(7), "MBL", 20 }, +{ INT_VAR, 40, 2, INTCAST(7), "MDL", 20 }, +{ INT_VAR, 40, 2, INTCAST(7), "MKL", 20 }, +{ INT_VAR, 40, 0, INTCAST(1), "MPL", 2 }, /* mass action levels: 0=off, 1=kick, 2=kickban */ +{ INT_VAR, 40, 2, INTCAST(20), "NCL", 20 }, +{ INT_VAR, 40, 0, INTCAST(4), "PROT", 4 }, /* max enforced protection level */ +{ TOG_VAR, 40, 0, INTCAST(1), "PUB", 1 }, /* public commands */ +{ TOG_VAR, 40, 0, ZERO, "RK", 1 }, /* revenge kick enable */ +{ TOG_VAR, 40, 0, ZERO, "SD", 1 }, /* server-op deop enable */ +{ TOG_VAR, 40, 0, INTCAST(1), "SHIT", 1 }, /* shitlist enable */ +{ TOG_VAR, 40, 0, ZERO, "SO", 1 }, /* safe-op enable */ +#ifdef STATS +{ STR_VAR, 80, 0, NULL, "STATS" }, /* statistics log file */ +#endif /* STATS */ +{ TOG_VAR, 40, 0, ZERO, "TOP", 1 }, +/* + * all global variables + */ +/* TYPE UACCES MIN DEFAULT NAME MAX */ +{ INT_GLOBAL, 40, 0, ZERO, "AAWAY", 1440 }, /* set auto-away after ___ minutes */ +{ STR_GLOBAL, 90, 0, NULL, "ALTNICK" }, /* alternative nick */ +#ifdef BOTNET +{ TOG_PROC, 90, 0, (&autolink), "AUTOLINK", 1 }, /* establish links automagically */ +#endif /* BOTNET */ +#ifdef BOUNCE +{ INT_PROC, 100, 0, (&bounce_port), "BNCPORT", 65535, (&new_port_bounce) }, /* irc proxy port to listen on */ +#endif /* BOUNCE */ +{ TOG_GLOBAL, 90, 0, INTCAST(1), "CC", 1 }, /* require command char */ +{ CHR_GLOBAL, 90, 1, CHARCAST, "CMDCHAR", 255 }, /* command char */ +#ifdef CTCP +{ TOG_GLOBAL, 90, 0, INTCAST(1), "CTCP", 1 }, /* ctcp replies enable */ +#endif /* CTCP */ +{ INT_PROC, 100, 10, (&ctimeout), "CTIMEOUT", 3600 }, /* how long to wait between connect attempts */ +#ifdef DCC_FILE +{ INT_GLOBAL, 80, 0, ZERO, "DCCANON", 100 }, /* anonymous (non user) DCC slots */ +{ STR_GLOBAL, 80, 0, NULL, "DCCFILES" }, /* string with space separated masks for auto-accepted filenames */ +{ INT_GLOBAL, 80, 0, INTCAST(4), "DCCUSER", 100 }, /* user DCC slots */ +#endif /* DCC_FILE */ +{ TOG_GLOBAL, 80, 0, ZERO, "ENFPASS", 1 }, /* disallow users with no passwords */ +{ STR_GLOBAL, 90, 0, NULL, "IDENT" }, /* register with this in the `user' field */ +{ STR_GLOBAL, 90, 0, NULL, "IRCNAME" }, /* register with this in the `real name' field */ +#ifdef NOTIFY +{ INT_GLOBAL, 80, 10, INTCAST(30), "ISONDELAY", 600 }, /* seconds between each ISON */ +#endif /* NOTIFY */ +#ifdef BOTNET +{ STR_PROC, 90, 0, (&linkpass), "LINKPASS" }, /* local process linkpass */ +{ INT_PROC, 100, 0, (&linkport), "LINKPORT", 65535 }, /* listen on for botnet connections */ +#endif /* BOTNET */ +{ INT_GLOBAL, 80, 1, INTCAST(3), "MODES", 20 }, /* max number of channel modes to send */ +#ifdef BOTNET +{ TOG_GLOBAL, 90, 0, INTCAST(1), "NETUSERS", 1 }, /* this bot accepts shared users (on by default) */ +#endif /* BOTNET */ +{ TOG_GLOBAL, 80, 0, ZERO, "NOIDLE", 1 }, /* dont idle */ +#ifdef NOTIFY +{ STR_GLOBAL, 80, 0, NULL, "NOTIFYFILE" }, /* read notify settings from */ +#endif /* NOTIFY */ +{ TOG_GLOBAL, 90, 0, ZERO, "ONOTICE", 1 }, /* ircd has /notice @#channel */ +#ifdef TRIVIA +{ CHR_PROC, 80, 0, (&triv_qchar), "QCHAR" }, /* use as mask char when displaying answer */ +{ INT_PROC, 80, 1, (&triv_qdelay), "QDELAY", 3600 }, /* seconds between each question */ +{ STR_PROC, 80, 0, (&triv_qfile), "QFILE" }, /* load questions from */ +#endif /* TRIVIA */ +#ifdef CTCP +{ TOG_GLOBAL, 80, 0, ZERO, "RF", 1 }, /* random ctcp finger reply */ +{ TOG_GLOBAL, 80, 0, ZERO, "RV", 1 }, /* random ctcp version reply */ +#endif /* CTCP */ +#ifdef SEEN +{ STR_PROC, 90, 0, (&seenfile), "SEENFILE" }, /* load/save seen database from */ +#endif /* SEEN */ +{ STR_GLOBAL, 80, 0, NULL, "SERVERGROUP" }, /* connect bot to a certain group of servers */ +{ TOG_GLOBAL, 90, 0, ZERO, "SPY", 1 }, /* send info about executed commands to status channel */ +{ STR_GLOBAL, 90, 0, NULL, "UMODES" }, /* send these modes on connect */ +#ifdef UPTIME +{ STR_PROC, 100, 0, (&uptimehost), "UPHOST" }, /* send uptime packets to */ +{ STR_PROC, 100, 0, (&uptimenick), "UPNICK" }, /* send as identifier instead of bots nick */ +{ INT_PROC, 100, 0, (&uptimeport), "UPPORT", 65535 }, /* send packets to port */ +#endif /* UPTIME */ +{ STR_GLOBAL, 90, 0, NULL, "USERFILE" }, /* what file to load/save userlist from/to */ +{ STR_GLOBAL, 90, 0, NULL, "VIRTUAL", 0, (&var_resolve_host) }, /* visual host */ +#ifdef WEB +{ INT_PROC, 100, 0, (&webport), "WEBPORT", 65535 }, /* httpd should listen on... */ +#endif /* WEB */ +#ifdef WINGATE +{ STR_GLOBAL, 90, 0, NULL, "WINGATE", 0, (&var_resolve_host) }, /* wingate hostname */ +{ INT_GLOBAL, 90, 0, ZERO, "WINGPORT", 65535 }, /* wingate port */ +#endif /* WINGATE */ +{ 0, }}; + +#undef ZERO +#undef INTCAST +#undef CHARCAST + +#else /* VARS_C */ + +extern const Setting VarName[]; + +#endif /* VARS_C */ +#endif /* SETTINGS_H */ diff --git a/src/shit.c b/src/shit.c new file mode 100644 index 0000000..a5023b2 --- /dev/null +++ b/src/shit.c @@ -0,0 +1,401 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define SHITLIST_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * + * shitlist enforcing + * + */ + +void shit_action(Chan *chan, ChanUser *cu) +{ + Shit *shit; + char *nick,*fromnick; + char *userhost; + + if (!chan->setting[TOG_SHIT].int_var || !chan->bot_is_op || cu->user) + return; + + userhost = get_nuh(cu); + if ((cu->shit = find_shit(userhost,chan->name)) == NULL) + return; + + shit = cu->shit; + + if (shit->action == SHIT_KB || shit->action == SHIT_PERMABAN) + { + nick = cu->nick; + + send_mode(chan,85,QM_CHANUSER,'-','o',cu); + send_mode(chan,90,QM_RAWMODE,'+','b',shit->mask); + + fromnick = nickcpy(NULL,shit->from); + send_kick(chan,nick,"%s %s: %s",time2small(shit->time),fromnick, + (shit->reason) ? shit->reason : "GET THE HELL OUT!!!"); + return; + } + else + /* + * shitlevel 1: not allowed to be chanop + */ + if (shit->action == SHIT_CHANOP) + { + send_mode(chan,160,QM_CHANUSER,'-','o',(void*)cu); + } +} + +void check_shit(void) +{ + ChanUser *cu; + Chan *chan; + + for(chan=current->chanlist;chan;chan=chan->next) + { + for(cu=chan->users;cu;cu=cu->next) + { + shit_action(chan,cu); + } + } +} + +/* + * + * shitlist management. adding, deleting, clearing, searching, ... + * + */ + +void remove_shit(Shit *shit) +{ + Chan *chan; + ChanUser *cu; + Shit **pp; + +#ifdef DEBUG + debug("(remove_shit) removing shit %s on channel %s (Level 4)\n",shit->mask,shit->chan,shit->action); +#endif /* DEBUG */ + pp = ¤t->shitlist; + while(*pp) + { + if (*pp == shit) + { + /* + * remove links to this shit record from the chanuserlist + */ + for(chan=current->chanlist;chan;chan=chan->next) + { + for(cu=chan->users;cu;cu=cu->next) + { + if (cu->shit == shit) + cu->shit = NULL; + } + } + *pp = shit->next; + Free((char**)&shit); + current->ul_save++; + return; + } + pp = &(*pp)->next; + } +} + +void purge_shitlist(void) +{ + while(current->shitlist) + remove_shit(current->shitlist); +} + +Shit *add_shit(char *from, char *chan, char *mask, char *reason, int axs, int expire) +{ + Shit *shit; + + set_mallocdoer(add_shit); + shit = (Shit*)Calloc(sizeof(Shit) + Strlen(from,chan,mask,reason,NULL)); + + shit->action = axs; + shit->time = now; + shit->expire = expire; + + shit->next = current->shitlist; + current->shitlist = shit; + + shit->chan = Strcpy(shit->mask,mask) + 1; + shit->from = Strcpy(shit->chan,chan) + 1; + shit->reason = Strcpy(shit->from,from) + 1; + Strcpy(shit->reason,reason); + + current->ul_save++; + return(shit); +} + +Shit *find_shit(const char *userhost, const char *channel) +{ + Shit *shit,*save; + int num,best; + + if (!userhost) + return(NULL); + save = NULL; + best = 0; + for(shit=current->shitlist;shit;shit=shit->next) + { + if (!channel || !Strcasecmp(channel,shit->chan) || + (*shit->chan == '*') || (*channel == '*')) + { + num = num_matches(shit->mask,userhost); + if (num > best) + { + best = num; + save = shit; + } + } + } + if (save && save->expire < now) + { + remove_shit(save); + save = NULL; + } + return(save); +} + +Shit *get_shituser(char *userhost, char *channel) +{ + ChanUser *cu; + Chan *chan; + Shit *shit; + char *p; + +#ifdef DEBUG + debug("(get_shituser) userhost = '%s', channel = '%s'\n", + nullstr(userhost),nullstr(channel)); +#endif /* DEBUG */ + /* + * save us a few million function calls if the shitlist is empty + */ + if (!current->shitlist) + return(NULL); + if (!nickcmp(current->nick,userhost)) + return(NULL); + for(chan=current->chanlist;chan;chan=chan->next) + { + for(cu=chan->users;cu;cu=cu->next) + { + p = get_nuh(cu); + if (matches(userhost,p)) + continue; + if ((shit = find_shit(p,channel)) != NULL) + return(shit); + } + } + return(NULL); +} + +int get_shitaction(const char *userhost, const char *chan) +{ + Shit *shit; + + if ((shit = find_shit(userhost,chan))) + return(shit->action); + return(0); +} + +/* + * + * commands related to shitlist + * + */ + +/* + * SHIT [expire] + */ +void do_shit(COMMAND_ARGS) +{ + /* + * on_msg checks CARGS + */ + char *channel,*nick,*nuh; + int shitlevel,days,uaccess,shitaccess; + + if (CurrentCmd->name == C_QSHIT) + { + channel = MATCH_ALL; + } + else + { + channel = chop(&rest); + if (!ischannel(channel) && *channel != '*') + { + usage: + usage(from); /* usage for CurrentCmd->name */ + return; + } + } + + if ((uaccess = get_useraccess(from,channel)) < cmdaccess) + return; + + if ((nick = chop(&rest)) == NULL) + goto usage; + + if (CurrentCmd->name == C_QSHIT) + { + shitlevel = DEFAULTSHITLEVEL; + days = 86400 * DEFAULTSHITLENGTH; + if (*rest == 0) + rest = TEXT_DEFAULTSHIT; + } + else + { + shitlevel = a2i(chop(&rest)); + if (errno) + goto usage; + + if (shitlevel < 1 || shitlevel > MAXSHITLEVEL) + { + to_user(from,"Valid levels are from 1 thru " MAXSHITLEVELSTRING); + return; + } + + /* + * option: expire in XXX days + */ + days = 86400 * 30; + if (*rest >= '1' && *rest <= '9') + { + days = 86400 * a2i(chop(&rest)); + if (errno) + goto usage; + } + + if (*rest == 0) + goto usage; + } + + +#ifdef CHANBAN + if (shitlevel == SHIT_CHANBAN) + { + nuh = nick; + } + else +#endif /* CHANBAN */ + { + if ((nuh = nick2uh(from,nick)) == NULL) + return; + + if (find_shit(nuh,channel)) + { + to_user(from,TEXT_ALREADYSHITTED,nuh); + return; + } + + if (uaccess != OWNERLEVEL) + { + shitaccess = get_useraccess(nuh,channel); + if (shitaccess > uaccess) + { + to_user(from,TEXT_SHITLOWACCESS,nuh); + return; + } + } + + format_uh(nuh,FUH_USERHOST); + } + +#ifdef DEBUG + debug("(do_shit) adding %s to %s (Level %i)\n",nuh,channel,shitlevel); +#endif /* DEBUG */ + add_shit(from,channel,nuh,rest,shitlevel,now + days); + + to_user(from,TEXT_HASSHITTED,nuh,channel); + to_user(from,TEXT_SHITEXPIRES,time2str(now + days)); + + check_shit(); +} + +void do_rshit(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + Shit *shit; + char *chan,*nick,*nuh; + int uaccess; + + chan = chop(&rest); + if (!chan || !*chan || (!ischannel(chan) && *chan != '*')) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + + if ((uaccess = get_useraccess(from,chan)) < cmdaccess) + return; + + if ((nick = chop(&rest)) == NULL) + { + to_user(from,"No nick or userhost specified"); + return; + } + if ((nuh = nick2uh(from,nick)) == NULL) + return; + if ((shit = find_shit(nuh,chan)) == NULL) + { + to_user(from,"%s is not in my shit list on that channel",nuh); + return; + } + if ((get_useraccess(shit->from,chan)) > uaccess) + { + to_user(from,"The person who did this shitlist has a higher level than you"); + return; + } + remove_shit(shit); + to_user(from,"User %s is no longer being shitted on %s",nuh,chan); +} + +char *shit_actions[MAXSHITLEVEL+1] = { "nothing (0)", "no op/voice (1)", "kickban (2)", "permaban (3)", "chanban (4)" }; + +void do_shitlist(COMMAND_ARGS) +{ + Shit *shit; + + table_buffer("\037channel\037\t\037mask\037\t\037action\037\t\037set by\037\t\037reason\037\t\037expires\037"); + for(shit=current->shitlist;shit;shit=shit->next) + { + table_buffer("%s\t%s\t%s\t%s\t%s\t%s",shit->chan,shit->mask,shit_actions[shit->action], + nickcpy(NULL,shit->from),shit->reason,time2away(shit->expire)); + } + table_send(from,2); +} + +void do_clearshit(COMMAND_ARGS) +{ + purge_shitlist(); + to_user(from,TEXT_CLEAREDSHITLIST); +} diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..b3f24a1 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,674 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define SOCKET_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" + +#ifndef RAWDNS + +/* + * only include this hack if rawdns isnt compiled in + */ +ulong get_ip(const char *host) +{ + struct hostent *he; + ulong ip; + + if ((ip = inet_addr(host)) == INADDR_NONE) + { + if ((he = gethostbyname(host)) == NULL) + { +#ifdef DEBUG + debug("(get_ip) unable to resolve %s\n",host); +#endif /* DEBUG */ + return(-1); + } + ip = (ulong)((struct in_addr*)he->h_addr)->s_addr; + } +#ifdef DEBUG + debug("(get_ip) %s -> %s\n",host,inet_ntoa(*((struct in_addr*)&ip))); +#endif /* DEBUG */ + return(ip); +} + +#endif /* ! RAWDNS */ + +/* + * some default code for socket flags + */ +void SockFlags(int fd) +{ +#ifdef ASSUME_SOCKOPTS + fcntl(fd,F_SETFL,O_NONBLOCK|O_RDWR); + fcntl(fd,F_SETFD,FD_CLOEXEC); +#else /* not ASSUME_SOCKOPTS */ + fcntl(fd,F_SETFL,O_NONBLOCK | fcntl(fd,F_GETFL)); + fcntl(fd,F_SETFD,FD_CLOEXEC | fcntl(fd,F_GETFD)); +#endif /* ASSUME_SOCKOPTS */ +} + +int SockOpts(void) +{ + struct { int onoff; int linger; } parm; + int s; + + if ((s = socket(AF_INET,SOCK_STREAM,0)) < 0) + return(-1); + + parm.onoff = parm.linger = 0; + setsockopt(s,SOL_SOCKET,SO_LINGER,(char*)&parm,sizeof(parm)); + parm.onoff++; + setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char*)&parm.onoff,sizeof(int)); + + SockFlags(s); + + return(s); +} + +int SockListener(int port) +{ + struct sockaddr_in sai; + int s; + + if ((s = SockOpts()) < 0) + return(-1); + memset((char*)&sai,0,sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_addr.s_addr = INADDR_ANY; + sai.sin_port = htons(port); + if ((bind(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) || (listen(s,1) < 0)) + { + close(s); + return(-1); + } + return(s); +} + +#ifdef RAWDNS +#define get_ip rawdns_get_ip +#endif /* RAWDNS */ + +int SockConnect(char *host, int port, int use_vhost) +{ + struct sockaddr_in sai; + int s; +#ifdef IDWRAP + char *id,identfile[64]; + int t = FALSE; +#endif /* IDWRAP */ + +#ifdef DEBUG + debug("(SockConnect) %s %i%s\n",nullstr(host),port,(use_vhost) ? " [VHOST]" : ""); +#endif /* DEBUG */ + + if ((s = SockOpts()) < 0) + return(-1); + + memset((char*)&sai,0,sizeof(sai)); + sai.sin_family = AF_INET; + + /* + * special case, BOUNCE feature may call SockConnect() + * to create the IDWRAP symlink, using special use_vhost value == 2 + */ +#if defined(BOUNCE) && defined(IDWRAP) + if ((use_vhost == TRUE) +#else /* not ... */ + if (use_vhost +#endif /* ... */ + && ((current->vhost_type & VH_IPALIAS_FAIL) == 0) + && current->setting[STR_VIRTUAL].str_var) + { + current->vhost_type |= VH_IPALIAS_BOTH; + if ((sai.sin_addr.s_addr = get_ip(current->setting[STR_VIRTUAL].str_var)) != -1) + { + if (bind(s,(struct sockaddr *)&sai,sizeof(sai)) >= 0) + { + current->vhost_type &= ~(VH_IPALIAS_FAIL|VH_WINGATE); +#ifdef WINGATE + use_vhost++; +#endif /* WINGATE */ +#ifdef IDWRAP + t = TRUE; +#endif /* IDWRAP */ +#ifdef DEBUG + debug("(SockConnect) IP Alias virtual host bound OK\n"); +#endif /* DEBUG */ + } + } + } +#ifdef IDWRAP + /* + * do a blank bind to get a port number + */ + if (!t) + { + sai.sin_addr.s_addr = INADDR_ANY; + bind(s,(struct sockaddr *)&sai,sizeof(sai)); + } +#endif /* IDWRAP */ + + memset((char*)&sai,0,sizeof(sai)); + sai.sin_family = AF_INET; + +#ifdef WINGATE + /* + * bouncer connects (WinGate, ...) + */ + if ((use_vhost == TRUE) && ((current->vhost_type & VH_WINGATE_FAIL) == 0) + && current->setting[STR_WINGATE].str_var) + { +#ifdef DEBUG + debug("(SockConnect) Trying wingate @ %s:%i\n", + current->setting[STR_WINGATE].str_var, + current->setting[INT_WINGPORT].int_var); +#endif /* DEBUG */ + current->vhost_type |= VH_WINGATE_BOTH; + sai.sin_port = htons(current->setting[INT_WINGPORT].int_var); + if ((sai.sin_addr.s_addr = get_ip(current->setting[STR_WINGATE].str_var)) == -1) + { + close(s); + return(-1); + } + current->vhost_type &= ~(VH_WINGATE_FAIL|VH_IPALIAS); +#ifdef DEBUG + debug("(SockConnect) WINGATE host resolved OK\n"); +#endif /* DEBUG */ + } + else +#endif /* WINGATE */ + { + /* + * Normal connect, no bounces... + */ +#ifdef IDWRAP + if (use_vhost) + { + t = sizeof(sai); + if (getsockname(s,(struct sockaddr*)&sai,&t) == 0) + { + if (current->identfile) + Free((char**)¤t->identfile); + sprintf(identfile,IDWRAP_PATH "%i.%i",ntohs(sai.sin_port),port); + id = current->setting[STR_IDENT].str_var; + if (symlink((id) ? id : BOTLOGIN,identfile) == 0) + { + set_mallocdoer(SockConnect); + current->identfile = Strdup(identfile); +#ifdef DEBUG + debug("(SockConnect) symlink: %s -> %s\n",identfile,(id) ? id : BOTLOGIN); +#endif /* DEBUG */ + } + } + memset((char*)&sai,0,sizeof(sai)); + sai.sin_family = AF_INET; + } +#endif /* IDWRAP */ + sai.sin_port = htons(port); + if ((sai.sin_addr.s_addr = get_ip(host)) == -1) + { + close(s); + return(-1); + } + sai.sin_family = AF_INET; + } + + if ((connect(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) && (errno != EINPROGRESS)) + { +#ifdef DEBUG + debug("[CbN] unable to connect. errno = %i\n",errno); +#endif /* DEBUG */ + close(s); + return(-1); + } +#ifdef DEBUG + debug("(SockConnect) {%i} %s %i%s\n",s,nullstr(host),port,(use_vhost) ? " [VHOST]" : ""); +#endif /* DEBUG */ + return(s); +} + +int SockAccept(int sock) +{ + struct sockaddr_in sai; + int s,sz; + + sz = sizeof(sai); + s = accept(sock,(struct sockaddr*)&sai,&sz); + if (s >= 0) + { + SockFlags(s); + } + return(s); +} + +/* + * Format text and send to a socket or file descriptor + */ +int to_file(int sock, const char *format, ...) +{ + va_list msg; +#ifdef DEBUG + char *line,*rest; + int i; +#endif /* DEBUG */ + + if (sock == -1) + return(-1); + + va_start(msg,format); + vsprintf(gsockdata,format,msg); + va_end(msg); + +#ifdef DEBUG + i = write(sock,gsockdata,strlen(gsockdata)); + rest = gsockdata; + while((line = get_token(&rest,"\n"))) /* rest cannot be NULL */ + debug("(out) {%i} %s\n",sock,nullstr(line)); + if (i < 0) + debug("(out) {%i} errno = %i\n",sock,errno); + return(i); +#else /* DEBUG */ + return(write(sock,gsockdata,strlen(gsockdata))); +#endif /* DEBUG */ +} + +/* + * Format a message and send it to the current bots server + * to_server needs a newline (\n) it wont manufacture it itself. + */ +void to_server(char *format, ...) +{ + va_list msg; +#ifdef DEBUG + char *line,*rest; +#endif /* DEBUG */ + + if (current->sock == -1) + return; + + va_start(msg,format); + vsprintf(gsockdata,format,msg); + va_end(msg); + + /* + * each line we send to the server pushes the sendq time + * forward *two* seconds + */ + current->sendq_time += 2; + + if (write(current->sock,gsockdata,strlen(gsockdata)) < 0) + { +#ifdef DEBUG + debug("[StS] {%i} errno = %i\n",current->sock,errno); +#endif /* DEBUG */ + close(current->sock); + current->sock = -1; + current->connect = CN_NOSOCK; + return; + } +#ifdef DEBUG + rest = gsockdata; + while((line = get_token(&rest,"\n"))) /* rest cannot be NULL */ + debug("[StS] {%i} %s\n",current->sock,line); +#endif /* DEBUG */ +} + +void to_user_q(const char *target, const char *format, ...) +{ + va_list args; + Client *client; + Strp *new,**pp; + char message[MAXLEN]; + char nick[MAXHOSTLEN]; + char *fmt; + + va_start(args,format); + vsprintf(message,format,args); + va_end(args); + + Strcat(message,"\n"); + + fmt = "NOTICE %s :%s"; + if (CurrentChan) + { + target = CurrentChan->name; + fmt = "PRIVMSG %s :%s"; + } + + if (!ischannel(target)) + target = nickcpy(nick,target); + + if ((client = find_client(target))) + { + if (write(client->sock,message,strlen(message)) < 0) + client->flags = DCC_DELETE; +#ifdef DEBUG + debug("(to_user) {%i} [%s] %s",client->sock,target,message); +#endif /* DEBUG */ + return; + } + +#ifdef REDIRECT + if (redirect.to && target == redirect.to) + { + send_redirect(message); + return; + } +#endif /* REDIRECT */ + + pp = ¤t->sendq; + while(*pp) + pp = &(*pp)->next; + + set_mallocdoer(to_user_q); + *pp = new = (Strp*)Calloc(sizeof(Strp) + Strlen(fmt,target,message,NULL)); + /* Calloc sets to zero new->next = NULL; */ + sprintf(new->p,fmt,target,message); +} + +/* + * Format a message and send it either through DCC if the user is + * connected to the partyline, or send it as a NOTICE if he's not + */ +void to_user(const char *target, const char *format, ...) +{ + va_list args; + Client *client; + char message[MAXLEN]; + char *s; + +#ifdef DEBUG + if (!*target) + return; +#endif /* DEBUG */ + + s = message; + +#ifdef REDIRECT + + if (redirect.to) + client = NULL; + else + client = find_client(target); + + if (!redirect.to && !client) + +#else /* REDIRECT */ + + if ((client = find_client(target)) == NULL) + +#endif /* REDIRECT */ + + { + if (current->sock == -1) + { +#ifdef DEBUG + debug("(to_user) [%s] current->sock == -1\n",target); +#endif /* DEBUG */ + return; + } + s = Strcpy(message,"NOTICE "); + if (ischannel(target)) + Strcat(message,target); + else + nickcpy(s,target); + s = Strcat(message," :"); + } + + va_start(args,format); + vsprintf(s,format,args); + va_end(args); + +#ifdef REDIRECT + if (redirect.to) + { + send_redirect(message); + return; + } +#endif /* REDIRECT */ + + /* + * tag on a newline for DCC or server + */ + s = Strcat(message,"\n"); + +#ifdef DEBUG + *s = 0; +#endif /* DEBUG */ + + if (client) + { + if (write(client->sock,message,(s - message)) < 0) + client->flags = DCC_DELETE; +#ifdef DEBUG + debug("(to_user) {%i} [%s] %s",client->sock,target,message); +#endif /* DEBUG */ + return; + } + + current->sendq_time += 2; + + if (write(current->sock,message,(s - message)) < 0) + { +#ifdef DEBUG + debug("(to_user) {%i} [%s] errno = %i\n",current->sock,target,errno); +#endif /* DEBUG */ + close(current->sock); + current->sock = -1; + current->connect = CN_NOSOCK; + return; + } +#ifdef DEBUG + debug("(to_user) {%i} [%s] %s",current->sock,target,message); +#endif /* DEBUG */ +} + +/* + * Read any data waiting on a socket or file descriptor + * and return any complete lines to the caller + */ +char *sockread(int s, char *rest, char *line) +{ + char *src,*dst,*rdst; + int n; + + errno = EAGAIN; + + src = rest; + dst = line; + + while(*src) + { + if (*src == '\n' || *src == '\r') + { + gotline: + while(*src == '\n' || *src == '\r') + src++; + *dst = 0; + dst = rest; + while(*src) + *(dst++) = *(src++); + *dst = 0; +#ifdef DEBUG + debug("(in) {%i} %s\n",s,line); +#endif /* DEBUG */ + return((*line) ? line : NULL); + } + *(dst++) = *(src++); + } + rdst = src; + + n = read(s,gsockdata,MSGLEN-2); + switch(n) + { + case 0: + errno = EPIPE; + case -1: + return(NULL); + } + + gsockdata[n] = 0; + src = gsockdata; + + while(*src) + { + if (*src == '\r' || *src == '\n') + goto gotline; + if ((dst - line) >= (MSGLEN-2)) + { + /* + * line is longer than buffer, let the wheel spin + */ + src++; + continue; + } + *(rdst++) = *(dst++) = *(src++); + } + *rdst = 0; + return(NULL); +} + +void readline(int fd, int (*callback)(char *)) +{ + char linebuf[MSGLEN],readbuf[MSGLEN]; + char *ptr; + int oc; + + *readbuf = 0; + + do + { + ptr = sockread(fd,readbuf,linebuf); + oc = errno; + if (ptr && *ptr) + { + if (callback(ptr) == TRUE) + oc = EPIPE; + } + } + while(oc == EAGAIN); + + close(fd); + +#ifdef DEBUG + debug("(readline) done reading lines\n"); +#endif /* DEBUG */ +} + +void remove_ks(KillSock *ks) +{ + KillSock *ksp; + + close(ks->sock); + if (ks == killsocks) + { + killsocks = ks->next; + } + else + { + ksp = killsocks; + while(ksp && (ksp->next != ks)) + ksp = ksp->next; + if (ksp) + { + ksp->next = ks->next; + } + } +#ifdef DEBUG + debug("(killsock) {%i} removing killsocket\n",ks->sock); +#endif /* DEBUG */ + Free((char**)&ks); +} + +int killsock(int sock) +{ + KillSock *ks,*ksnext; + struct timeval t; + fd_set rd,wd; + char bitbucket[MSGLEN]; + int hi,n; + + if (sock >= 0) + { + set_mallocdoer(killsock); + ks = (KillSock*)Calloc(sizeof(KillSock)); + ks->time = now; + ks->sock = sock; + ks->next = killsocks; + killsocks = ks; +#ifdef DEBUG + debug("(killsock) {%i} added killsocket\n",ks->sock); +#endif /* DEBUG */ + return(TRUE); + } + + if (killsocks == NULL) + return(FALSE); + + if (sock == -1) + t.tv_sec = 0; + else + t.tv_sec = 1; + t.tv_usec = 0; + + FD_ZERO(&rd); + FD_ZERO(&wd); + + hi = -1; + for(ks=killsocks;ks;ks=ks->next) + { + if (ks->sock > hi) + hi = ks->sock; + FD_SET(ks->sock,&rd); + FD_SET(ks->sock,&wd); + } + + if (select(hi+1,&rd,&wd,NULL,&t) == -1) + { + switch(errno) + { + case EINTR: + /* have to redo the select */ + case ENOMEM: + /* should never happen, but still */ + return(TRUE); + } + } + + for(ks=killsocks;ks;) + { + ksnext = ks->next; + if (FD_ISSET(ks->sock,&rd)) + { + n = read(ks->sock,&bitbucket,MSGLEN); + if ((n == 0) || ((n == -1) && (errno != EAGAIN))) + remove_ks(ks); + } + if ((now - ks->time) > KILLSOCKTIMEOUT) + remove_ks(ks); + ks = ksnext; + } + return(TRUE); +} diff --git a/src/spy.c b/src/spy.c new file mode 100644 index 0000000..3daf563 --- /dev/null +++ b/src/spy.c @@ -0,0 +1,536 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define SPY_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#ifdef DEBUG + +LS const char SPY_DEFS[7][12] = +{ + "SPY_FILE", + "SPY_CHANNEL", + "SPY_DCC", + "SPY_STATUS", + "SPY_MESSAGE", + "SPY_RAWIRC", + "SPY_BOTNET" +}; + +#endif /* DEBUG */ + +void send_spy(const char *src, const char *format, ...) +{ + Chan *chan; + Mech *backup; + Spy *spy; + va_list msg; + const char *tempsrc; + char tempdata[MAXLEN]; + int fd; + int printed = FALSE; + + tempsrc = (src == SPYSTR_STATUS) ? time2medium(now) : src; + +#ifdef DEBUG + debug("(send_spy) src %s format = '%s'\n",src,format); +#endif /* DEBUG */ + + for(spy=current->spylist;spy;spy=spy->next) + { + if ((*src == '#' || *src == '*') && spy->t_src == SPY_CHANNEL) + { + if ((*src != '*') && Strcasecmp(spy->src,src)) + continue; + if ((chan = find_channel_ac(spy->src)) == NULL) + continue; + if (find_chanuser(chan,CurrentNick) == NULL) + continue; + tempsrc = spy->src; + } + else + /* + * by using string constants we can compare addresses + */ + if (spy->src != src) + continue; + + if (!printed) + { + printed = TRUE; + va_start(msg,format); + vsprintf(tempdata,format,msg); + va_end(msg); + } + + switch(spy->t_dest) + { + case SPY_DCC: + to_file(spy->dcc->sock,"[%s] %s\n",tempsrc,tempdata); + break; + case SPY_CHANNEL: + if (spy->destbot >= 0) + { + backup = current; + for(current=botlist;current;current=current->next) + { + if (current->guid == spy->destbot) + { + to_server("PRIVMSG %s :[%s] %s\n",spy->dest,tempsrc,tempdata); + break; + } + } + current = backup; + } + else + { + to_user(spy->dest,"[%s] %s",tempsrc,tempdata); + } + break; + case SPY_FILE: + if ((fd = open(spy->dest,O_WRONLY|O_CREAT|O_APPEND,NEWFILEMODE)) >= 0) + { + to_file(fd,"[%s] %s\n",logtime(now),tempdata); + close(fd); + } + } + } +} + +void send_global(const char *src, const char *format, ...) +{ + va_list msg; + Mech *backup; + char tempdata[MAXLEN]; + int printed = FALSE; + + backup = current; + for(current=botlist;current;current=current->next) + { + if (current->spy & SPYF_ANY) + { + if (!printed) + { + printed = TRUE; + va_start(msg,format); + vsprintf(tempdata,format,msg); + va_end(msg); + } + + send_spy(src,FMT_PLAIN,tempdata); + } + } + current = backup; +} + +void spy_typecount(Mech *bot) +{ + Spy *spy; + + bot->spy = 0; + for(spy=bot->spylist;spy;spy=spy->next) + { + bot->spy |= SPYF_ANY; + bot->spy |= (1 << spy->t_src); + } +} + +struct +{ + char *idstring; + int typenum; + +} spy_source_list[] = +{ +{ SPYSTR_STATUS, SPY_STATUS }, +{ SPYSTR_MESSAGE, SPY_MESSAGE }, +{ SPYSTR_RAWIRC, SPY_RAWIRC }, +{ SPYSTR_BOTNET, SPY_BOTNET }, +{ NULL, 0 }, +}; + +int spy_source(char *from, int *t_src, char **src) +{ + int i; + + for(i=0;spy_source_list[i].idstring;i++) + { + if (!Strcasecmp(*src,spy_source_list[i].idstring)) + { + *src = spy_source_list[i].idstring; + *t_src = spy_source_list[i].typenum; + return(200); + } + } + *t_src = SPY_CHANNEL; + if (!ischannel(*src)) + return(-1); + return(get_useraccess(from,*src)); +} + +/* + * + * commands related to spy-pipes + * + */ + +void do_spy(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + Spy *spy; + Mech *backup,*destbot; + char *src,*dest; + int t_src,t_dest; + int sz; + int guid; + + if (!*rest) + { + if (!current->spylist) + { + to_user(from,"No active spy channels"); + return; + } + + if (dcc_only_command(from)) + return; + table_buffer("\037source\037\t\037target\037"); + for(spy=current->spylist;spy;spy=spy->next) + { + switch(spy->t_src) + { + case SPY_MESSAGE: + src = "messages"; + break; + default: + src = spy->src; + } + dest = (spy->t_dest == SPY_FILE) ? " (file)" : ""; + table_buffer("%s\t%s%s",src,spy->dest,dest); + } + table_send(from,2); + return; + } + + src = chop(&rest); + dest = chop(&rest); + + if (!src) + { +spy_usage: + usage(from); /* usage for CurrentCmd->name */ + return; + } + + t_dest = SPY_DCC; + guid = -1; + destbot = NULL; + + if (*src >= '0' && *src <= '9') + { + guid = 0; + while(*src && *src != ':') + { + guid = *src - '0'; + src++; + } + if (*src != ':' && !ischannel(src+1)) + goto spy_usage; + src++; + t_src = SPY_CHANNEL; + /* + * TODO: check access + */ +#ifdef DEBUG + debug("(do_spy) spy source guid = %i, channel = %s\n",guid,src); +#endif /* DEBUG */ + + /* + * is it a bot? + * TODO: check botnet bots also + */ + for(backup=botlist;backup;backup=backup->next) + { + if (backup->guid == guid) + { + destbot = backup; + goto guid_ok; + } + } + to_user(from,"Unknown bot guid: %i",guid); + return; +guid_ok: + ; + } + else + { + sz = spy_source(from,&t_src,&src); + if (sz < 0) + goto spy_usage; + if (sz < cmdaccess) + return; + } + + if (dest) + { + /* + * log to a file + */ + if (*dest == '>') + { + dest++; + if (!*dest) + { + dest = chop(&rest); + if (!dest || !*dest) + goto spy_usage; + } + /* + * Dont just open anything. + */ + if (!is_safepath(dest)) + goto spy_usage; + t_dest = SPY_FILE; + goto spy_dest_ok; + } + + if (!ischannel(dest)) + goto spy_usage; + if (get_useraccess(from,dest) < cmdaccess) + { + to_user(from,"You don't have enough access on %s",dest); + return; + } + t_dest = SPY_CHANNEL; + } + +spy_dest_ok: +#ifdef DEBUG + debug("(do_spy) src = `%s'; t_src = %i (%s); dest = `%s'; t_dest = %i (%s)\n", + src,t_src,SPY_DEFS[t_src-1],nullstr(dest),t_dest,SPY_DEFS[t_dest-1]); + if (guid >= 0) + debug("(do_spy) spying from remote bot guid %i (%s) channel %s\n",guid,(destbot) ? destbot->nick : "unknown",src); +#endif /* DEBUG */ + + if (t_dest == SPY_DCC) + { + if (!CurrentDCC) + { + to_user(from,"Spying is only allowed in DCC chat"); + return; + } + dest = CurrentDCC->user->name; + } + + for(spy=current->spylist;spy;spy=spy->next) + { + if ((spy->t_src == t_src) && (spy->t_dest == t_dest) && + !Strcasecmp(spy->src,src) && !Strcasecmp(spy->dest,dest)) + { + to_user(from,"Requested spy channel is already active"); + return; + } + } + + set_mallocdoer(do_spy); + + sz = sizeof(Spy); + + if (t_dest != SPY_DCC) + sz += strlen(dest); + + if (t_src == SPY_CHANNEL) + sz += strlen(src); + + spy = Calloc(sz); + + if (t_dest != SPY_DCC) + { + spy->dest = spy->p; + spy->src = Strcat(spy->p,dest) + 1; + } + else + { + spy->dest = CurrentDCC->user->name; + spy->dcc = CurrentDCC; + spy->src = spy->p; + } + + if (t_src == SPY_CHANNEL) + { + Strcpy(spy->src,src); + } + else + { + spy->src = src; + } + + spy->t_src = t_src; + spy->t_dest = t_dest; + + /* + * finally link the spy record into the chain + * TODO: botnet bots + */ + if (guid >= 0) + { + if (destbot) + { + spy->destbot = current->guid; + spy->next = destbot->spylist; + destbot->spylist = spy; + spy_typecount(destbot); + } + } + else + { + spy->destbot = -1; + spy->next = current->spylist; + current->spylist = spy; + spy_typecount(current); + } + + switch(t_src) + { + case SPY_STATUS: + send_spy(SPYSTR_STATUS,"(%s) Added to mech core",nickcpy(NULL,from)); + break; + case SPY_MESSAGE: + src = "messages"; + default: + to_user(from,"Spy channel for %s has been activated",src); + } +} + +void do_rspy(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + Spy *spy,**pspy; + char *src,*dest,*tmp; + int t_src,t_dest; + int n; + + src = chop(&rest); + dest = chop(&rest); + + t_dest = SPY_DCC; + + if (!src) + { +rspy_usage: + usage(from); /* usage for CurrentCmd->name */ + return; + } + + n = spy_source(from,&t_src,&src); + if (n < 0) + goto rspy_usage; + if (n < cmdaccess) + return; + + if (dest) + { + if (*dest == '>') + { + dest++; + if (!*dest) + { + dest = chop(&rest); + if (!dest || !*dest) + goto rspy_usage; + } + /* + * Dont just open anything. + */ + if (!is_safepath(dest)) + goto rspy_usage; + t_dest = SPY_FILE; + goto rspy_dest_ok; + } + + if (ischannel(dest)) + t_dest = SPY_CHANNEL; + } + else + dest = from; + +rspy_dest_ok: +#ifdef DEBUG + debug("(do_rspy) src = `%s'; t_src = %i (%s); dest = `%s'; t_dest = %i (%s)\n", + src,t_src,SPY_DEFS[t_src-1],dest,t_dest,SPY_DEFS[t_dest-1]); +#endif /* DEBUG */ + + /* + * check if the spy channel exists + */ + for(spy=current->spylist;spy;spy=spy->next) + { + if ((spy->t_src == t_src) && (spy->t_dest == t_dest) && (!Strcasecmp(spy->src,src))) + { + if ((t_dest == SPY_DCC) && (!nickcmp(spy->dest,dest))) + break; + else + if (!Strcasecmp(spy->dest,dest)) + break; + } + } + if (!spy) + { + to_user(from,"No matching spy channel could be found"); + return; + } + + switch(t_src) + { + case SPY_STATUS: + tmp = (t_dest == SPY_DCC) ? nickcpy(NULL,spy->dest) : spy->dest; + send_spy(SPYSTR_STATUS,"(%s) Removed from mech core",tmp); + break; + case SPY_MESSAGE: + src = "messages"; + default: + to_user(from,"Spy channel for %s has been removed",src); + break; + } + + pspy = ¤t->spylist; + while(*pspy) + { + if (*pspy == spy) + { + *pspy = spy->next; + Free((char**)&spy); + return; + } + pspy = &(*pspy)->next; + } + spy_typecount(current); +} diff --git a/src/stats.c b/src/stats.c new file mode 100644 index 0000000..a08e000 --- /dev/null +++ b/src/stats.c @@ -0,0 +1,144 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define STATS_C +#include "config.h" + +#ifdef STATS +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#include + +void stats_loghour(Chan *chan, char *filename, int hour) +{ + ChanStats *stats; + time_t when; + int fd; + + if (!(stats = chan->stats)) + return; + + when = (now - (now % 3600)); + + if ((fd = open(filename,O_WRONLY|O_APPEND|O_CREAT,NEWFILEMODE)) >= 0) + { + stats->userseconds += stats->users * (when - stats->lastuser); + to_file(fd,"H %s %i %i %i %i\n",chan->name,hour, + (stats->flags & CSTAT_PARTIAL) ? -stats->userseconds : stats->userseconds, + stats->userpeak,stats->userlow); + close(fd); + } + stats->LHuserseconds = stats->userseconds; + stats->userseconds = 0; + stats->lastuser = when; + stats->flags = 0; +} + +void stats_plusminususer(Chan *chan, int plusminus) +{ + ChanStats *stats; + ChanUser *cu; + + if (!(stats = chan->stats)) + { + set_mallocdoer(stats_plusminususer); + chan->stats = stats = (ChanStats*)Calloc(sizeof(ChanStats)); /* Calloc sets memory to 0 */ + for(cu=chan->users;cu;cu=cu->next) + stats->users++; + stats->userpeak = stats->users; + stats->userlow = stats->users; + stats->lastuser = now; + stats->flags = CSTAT_PARTIAL; + } + + /* + * add (number of users until now * seconds since last user entered/left) + */ + stats->userseconds += stats->users * (now - stats->lastuser); + + stats->lastuser = now; + stats->users += plusminus; /* can be both negative (-1), zero (0) and positive (+1) */ + + if (stats->userpeak < stats->users) + stats->userpeak = stats->users; + if (stats->userlow > stats->users) + stats->userlow = stats->users; + +#ifdef DEBUG + debug("(stats_plusminususer) %s: %i users, %i userseconds, %i high, %i low; %s (%lu)\n", + chan->name,stats->users,stats->userseconds,stats->userpeak,stats->userlow, + atime(stats->lastuser),stats->lastuser); +#endif /* DEBUG */ +} + +void do_info(COMMAND_ARGS) +{ + ChanStats *stats; + Chan *chan; + char *p; + char text[MSGLEN]; + ulong avg; + + if (current->chanlist == NULL) + { + to_user(from,ERR_NOCHANNELS); + return; + } + to_user(from,"\037channel\037 " + "\037average\037 \037peak\037 \037low\037"); + for(chan=current->chanlist;chan;chan=chan->next) + { + *(p = text) = 0; + p = Strcat(p,chan->name); + if (chan == current->activechan) + p = Strcat(p," (current)"); + if ((stats = chan->stats)) + { + if (stats && stats->flags == CSTAT_PARTIAL) + p = Strcat(p," (partial)"); + while(p < text+35) + *(p++) = ' '; + if (stats->LHuserseconds > 0) + { + avg = stats->LHuserseconds / (60*60); + } + else + { + avg = (stats->userpeak + stats->userlow) / 2; + } + sprintf(p,"%-7lu %-4i %i",avg,stats->userpeak,stats->userlow); + to_user(from,FMT_PLAIN,text); + sprintf(text,"Messages: %i Notices: %i Joins: %i Parts: %i Kicks: %i Quits: %i", + stats->privmsg,stats->notice,stats->joins,stats->parts,stats->kicks,stats->quits); + } + else + { + Strcpy(p," (no current data)"); + } + to_user(from,FMT_PLAIN,text); + } +} + +#endif /* STATS */ diff --git a/src/structs.h b/src/structs.h new file mode 100644 index 0000000..2d140d2 --- /dev/null +++ b/src/structs.h @@ -0,0 +1,822 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef STRUCTS_H +#define STRUCTS_H 1 + +typedef union usercombo +{ + struct + { + ulong access:8, /* access level (0-200) [0-255] */ + prot:3, /* protlevel (0-4) [0-7] */ +#ifdef BOTNET + noshare:1, /* dont share this user over botnet */ + readonly:1, /* botnet cannot alter this user */ +#endif /* BOTNET */ +#ifdef GREET + greetfile:1, /* greeting is filename */ + randline:1, /* grab random line from filename */ +#endif /* GREET */ +#ifdef BOUNCE + bounce:1, /* user has access to bouncer */ +#endif /* BOUNCE */ + echo:1, /* partyline echo of own messages */ + aop:1, /* auto-opping */ + avoice:1; /* auto-voicing */ + } x; + ulong comboflags; + +} usercombo; + +#ifndef GENCMD_C + +typedef struct ircLink +{ + struct ircLink *next; + + int servsock; + int usersock; + + int status; + time_t active; + + char *userLine; + char *nickLine; + + char *nick; /* which nick to speak to */ + char *handle; + +#ifdef IDWRAP + char *idfile; +#endif /* IDWRAP */ + + char servmem[MSGLEN]; + char usermem[MSGLEN]; + +} ircLink; + +typedef struct +{ + int tenminute; + int hour; + +} SequenceTime; + +typedef struct DEFstruct +{ + int id; + char *idstr; + +} DEFstruct; + +typedef struct Alias +{ + struct Alias *next; + + char *format; + char alias[1]; + +} Alias; + +typedef struct nfLog +{ + struct nfLog *next; + + time_t signon; + time_t signoff; + + char *realname; + char userhost[2]; + +} nfLog; + +typedef struct Notify +{ + struct Notify *next; + + int status; + time_t checked; /* when nick was last checked */ + + nfLog *log; /* online + offline + userhost + realname */ + + char *info; + char *endofmask; + char *mask; + char nick[3]; + +} Notify; + +typedef struct IReq +{ + struct IReq *next; + + time_t when; + int t; + + char *nick; + char from[2]; + +} IReq; + +typedef struct Seen +{ + struct Seen *next; + + time_t when; + int t; + + /* + * dont mess with the order of these + */ + char *pa; + char *pb; + char *userhost; + char nick[4]; + +} Seen; + +typedef union UniVar +{ + union UniVar *proc_var; + + char *str_var; + char char_var; + int int_var; + +} UniVar; + +typedef struct Setting +{ + uchar type; + uchar uaccess; /* user access to touch/view this setting */ + short min; + void *setto; /* type-casted to whatever */ + char *name; + int max; + void (*func)(const struct Setting *); + +} Setting; + +typedef struct Strp +{ + struct Strp *next; + char p[1]; + +} Strp; + +typedef struct KickSay +{ + struct KickSay *next; + + int action; + + /* + * dont mess with the order of these + */ + char *chan; + char *reason; + char mask[5]; + +} KickSay; + +typedef struct Shit +{ + struct Shit *next; + + int action; + time_t time; + time_t expire; + + /* + * dont mess with the order of these + */ + char *chan; + char *from; + char *reason; + char mask[4]; + +} Shit; + + +/* + * this struct is put to use in global.h + */ +typedef struct User +{ + struct User *next; /* linked list */ + + Strp *mask; + Strp *chan; + + union usercombo x; + +#ifdef GREET + char *greet; +#endif /* GREET */ + +#ifdef NOTE + Strp *note; +#endif /* NOTE */ + +#ifdef BOTNET + int modcount; + int guid; + int tick; + int addsession; +#endif /* BOTNET */ + + /* + * dont mess with the order of these + */ + char *pass; + char name[2]; + +} User; + +typedef struct Client +{ + struct Client *next; + + User *user; + + int sock; + int flags; + int inputcount; /* used to enforce input limit */ + time_t lasttime; /* can be used for idletime */ + +#ifdef DCC_FILE + int fileno; + int fileend; + time_t start; + char *whom; + char filename[2]; +#endif /* DCC_FILE */ + + char sockdata[MSGLEN]; /* input buffer */ + +} Client; + +typedef struct ShortClient +{ + struct Client *next; + + User *user; + + int sock; + int flags; + int inputcount; /* used to enforce input limit */ + time_t lasttime; /* can be used for idletime */ + +} ShortClient; + +typedef struct Auth +{ + struct Auth *next; + + time_t active; + User *user; + char nuh[1]; + +} Auth; + +typedef struct ChanUser +{ + struct ChanUser *next; + + User *user; + Shit *shit; + + int flags; + + /* distance between &_num -> &_time is used in check_mass */ + int floodnum; + time_t floodtime; + int bannum; + time_t bantime; + int deopnum; + time_t deoptime; + int kicknum; + time_t kicktime; + int nicknum; + time_t nicktime; + int capsnum; + time_t capstime; + + time_t idletime; + +#ifdef CHANBAN + time_t lastwhois; /* time when the last whois was made to check channels */ +#endif /* CHANBAN */ + + char *nick; /* nick can change without the user reconnecting */ + char userhost[1]; /* ident & host is static */ + +} ChanUser; + +typedef struct qKick +{ + struct qKick *next; + + char *reason; + char nick[2]; + +} qKick; + +typedef struct qMode +{ + struct qMode *next; + + int pri; /* urgency factor */ + int type; + + void *data; /* nick (ChanUser), key, banmask, limit */ + char plusminus; /* +/- */ + char modeflag; /* ov, iklmnpst */ + +} qMode; + +typedef struct Ban +{ + struct Ban *next; + + time_t time; +#ifdef IRCD_EXTENSIONS + ulong imode:1, + emode:1; +#endif /* IRCD_EXTENSIONS */ + + char *bannedby; + char banstring[2]; + +} Ban; + +typedef struct ChanStats +{ + int userseconds; /* number of users on channel * number of seconds (one hour, 3600 seconds) */ + int LHuserseconds; + int users; + time_t lastuser; /* last time a user joined or left (part, quit, kick) */ + + int flags; + + int userpeak; + int userlow; + + int joins; + int parts; + int kicks; + int quits; + + int privmsg; + int notice; + +} ChanStats; + +typedef struct ShortChan +{ + struct ShortChan *next; + char *name; + +} ShortChan; + +typedef struct Chan +{ + struct Chan *next; + + char *name; /* name of the channel */ + char *key; /* channel key, if any */ + char *topic; /* channel topic, if any */ + char *kickedby; /* n!u@h of whomever kicks the bot */ + + Ban *banlist; /* banlist */ + qKick *kicklist; /* KICK sendq */ + qMode *modelist; /* MODE sendq */ + + ChanUser *users; /* users */ + ChanUser *cacheuser; /* cache for find_chanuser() */ + + int limit; /* channel limit */ + ulong + bot_is_op:1, /* set if the bot is opped */ + private:1, /* channel mode: +p */ + secret:1, /* channel mode: +s */ + moderated:1, /* channel mode: +m */ + topprot:1, /* channel mode: +t */ + limitmode:1, /* channel mode: +l */ + invite:1, /* channel mode: +i */ + nomsg:1, /* channel mode: +n */ + keymode:1, /* channel mode: +k */ + hiddenkey:1, /* Undernet screwup */ + sync:1, /* join sync status */ + wholist:1, /* first /WHO */ + active:1, /* active or inactive channel */ + rejoin:1; /* trying to rejoin it? */ + + int this10,this60; + int last10,last60; + +#ifdef DYNAMODE + time_t lastlimit; +#endif /* DYNAMODE */ + +#ifdef STATS + ChanStats *stats; +#endif /* STATS */ + + UniVar setting[CHANSET_SIZE]; /* channel vars */ + +} Chan; + +typedef struct Spy +{ + struct Spy *next; + + int t_src; + int t_dest; + + Client *dcc; + int destbot; + + char *src; + char *dest; + char p[2]; + +} Spy; + +typedef struct Server +{ + struct Server *next; + + int ident; + int usenum; + int servergroup; + int port; + int err; + time_t lastconnect; + time_t lastattempt; + time_t maxontime; + + char realname[NAMEBUF]; + char name[NAMEBUF]; + char pass[PASSLEN]; + +} Server; + +typedef struct ServerGroup +{ + struct ServerGroup *next; + + int servergroup; + char name[1]; + +} ServerGroup; + +typedef struct Mech +{ + struct Mech *next; + + int guid; /* globally uniqe ID */ + int connect; + int sock; + struct in_addr ip; /* for DCC */ + int server; /* ident of my current server */ + int nextserver; + + /* + * Line buffer for non-essential stuff + */ + Strp *sendq; + time_t sendq_time; + + /* + * Basic bot information + */ + char *nick; /* current nickname */ + char *wantnick; /* wanted nickname */ + char *userhost; + int vhost_type; + + ulong reset:1, + rejoin:1, + away:1; + + User *userlist; + time_t ul_save; /* increment for each change, save if needed */ + + Chan *chanlist; + Chan *activechan; + + Client *clientlist; + Auth *authlist; + Shit *shitlist; + KickSay *kicklist; + IReq *parselist; + +#ifdef NOTIFY + Notify *notifylist; +#endif /* NOTIFY */ + + Spy *spylist; + + int spy; + +#ifdef NOTIFY + time_t lastnotify; /* ... */ +#endif /* NOTIFY */ + time_t lastreset; /* last time bot was reset */ + time_t lastantiidle; /* avoid showing large idle times */ + time_t lastrejoin; /* last time channels were reset */ +#ifdef CHANBAN + time_t lastchanban; /* last time a chanban check was run */ +#endif /* CHANBAN */ + + time_t conntry; /* when connect try started */ + time_t activity; /* Away timer (AAWAY) */ + + time_t ontime; /* how long the bot has been connected */ + +#ifdef IRCD_EXTENSIONS + int ircx_flags; +#endif /* IRCD_EXTENSIONS */ + + /* + * Buffers for do_die() command. + */ + char *signoff; + char *from; + +#ifdef IDWRAP + char *identfile; +#endif /* IDWRAP */ + + /* big buffers at the end */ + UniVar setting[SIZE_VARS]; /* global vars + channel defaults */ + char modes[32]; + char *lastcmds[LASTCMDSIZE]; + + /* + * Server socket input buffer + */ + char sockdata[MSGLEN]; + +} Mech; + +typedef struct aME +{ + void *area; + void *doer; + int size; + time_t when; + char touch; + +} aME; + +typedef struct aMEA +{ + struct aMEA *next; + aME mme[MRSIZE]; + +} aMEA; + +typedef struct KillSock +{ + struct KillSock *next; + time_t time; + int sock; + +} KillSock; + +typedef struct Note +{ + struct Note *next; + + time_t start; + + char *user; + char *to; + char from[3]; + +} Note; + +typedef struct PartyUser +{ + struct PartyUser *next; + +} PartyUser; + +typedef struct BotInfo +{ + struct BotInfo *next; + + int guid; + int hops; + + char *version; + char *server; + char nuh[4]; + +} BotInfo; + +typedef struct BotNet +{ + struct BotNet *next; + + int sock; + int status; + int has_data; + + /* + * do not touch the above vars! + * they are copied partially in net.c + */ + + int guid; /* remote bot guid */ + int lsid; /* local session id */ + int rsid; /* remote session id */ + + struct + { + ulong pta:1; /* plain text auth */ + ulong md5:1; /* md5 */ + + } opt; + + Mech *controller; + + int tick; /* tick of the remote bot */ + int addsession; + time_t tick_last; + + time_t when; + + struct BotInfo *botinfo; + int list_complete; + + char sockdata[MSGLEN]; + +} BotNet; + +typedef struct NetCfg +{ + struct NetCfg *next; + + int guid; + ushort port; + + ushort linked:1; + + char *host; + char pass[2]; + +} NetCfg; + +typedef struct WebDoc +{ + struct WebDoc *next; + + char *url; + void (*proc)(); + +} WebDoc; + +typedef struct WebSock +{ + struct WebSock *next; + + int sock; + int status; + time_t when; + + WebDoc *docptr; + Mech *ebot; + Chan *echan; + char *url; + + char sockdata[MSGLEN]; + +} WebSock; + +typedef struct +{ + time_t last; + time_t next; + ulong second1; //:30; + ulong second2; //:30; + ulong minute1; //:30; + ulong minute2; //:30; + ulong hour; //:24; + ulong weekday; //:7; + +} HookTimer; + +typedef struct Hook +{ + struct Hook *next; + + int (*func)(); + int guid; /* guid filter */ + int flags; + union { + void *any; + char *command; + char *host; + HookTimer *timer; + } type; + char self[2]; + +} Hook; + +typedef struct TrivScore +{ + struct TrivScore *next; + + int score_wk; + int score_last_wk; + int week_nr; + + int score_mo; + int score_last_mo; + int month_nr; + + char nick[1]; + +} TrivScore; + +#endif /* GENCMD_C */ + +typedef struct OnMsg +{ + const char *name; + void (*func)(char *, char *, char *, int); + ulong defaultaccess:8, /* defaultaccess */ + dcc:1, + cc:1, + pass:1, + args:1, + nopub:1, + nocmd:1, + gaxs:1, + caxs:1, + redir:1, + lbuf:1, + cbang:1, + acchan:1; + char *cmdarg; + +} OnMsg; + +typedef unsigned char OnMsg_access; + +typedef struct dnsAuthority +{ + struct dnsAuthority *next; + struct in_addr ip; + unsigned char count; + char hostname[1]; + +} dnsAuthority; + +typedef struct dnsList +{ + struct dnsList *next; + time_t when; + struct in_addr ip; + ushort id; + int findauth; + dnsAuthority *auth; + dnsAuthority *auth2; + char *cname; + char host[1]; + +} dnsList; + +typedef struct dnsQuery +{ + ushort qid; /* query id */ + ushort flags; + ushort questions; + ushort answers; + ushort authorities; + ushort resources; + +} dnsQuery; + +#endif /* STRUCTS_H */ diff --git a/src/tcl.c b/src/tcl.c new file mode 100644 index 0000000..6b23816 --- /dev/null +++ b/src/tcl.c @@ -0,0 +1,654 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define TCL_C +#include "config.h" + +#ifdef TCL + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#include + +LS Tcl_Interp *energymech_tcl = NULL; + +#define tclv_READ TCL_TRACE_READS +#define tclv_WRITE TCL_TRACE_WRITES +#define tclv_RDWR (tclv_READ|tclv_WRITE) + +#define TVINFO_UNTYPED 0x0ffff +#define TVINFO_INT 0x10000 +#define TVINFO_CHAR 0x20000 + +enum { + TVINFO_pointer, + TVINFO_guid, + TVINFO_nick, + TVINFO_wantnick, + TVINFO_server, + TVINFO_nextserver, + TVINFO_currentchan, +}; + +typedef struct Tcl_TVInfo +{ + int type; + int flags; + char *name; + const char *data; + Tcl_Obj *n1; + +} Tcl_TVInfo; + +LS Tcl_TVInfo vinfolist[] = +{ +{ TVINFO_pointer | TVINFO_CHAR, tclv_READ, "mech_currentnick", CurrentNick }, +{ TVINFO_guid | TVINFO_INT, tclv_READ, "mech_guid" }, +{ TVINFO_nick | TVINFO_CHAR, tclv_READ, "mech_nick" }, +{ TVINFO_wantnick | TVINFO_CHAR, tclv_RDWR, "mech_wantnick" }, +{ TVINFO_server | TVINFO_INT, tclv_READ, "mech_server" }, +{ TVINFO_nextserver | TVINFO_INT, tclv_RDWR, "mech_nextserver" }, +{ TVINFO_currentchan | TVINFO_CHAR, tclv_READ, "mech_currentchan" }, +{ TVINFO_pointer | TVINFO_CHAR, tclv_READ, "mech_version", VERSION }, +{ TVINFO_pointer | TVINFO_CHAR, tclv_READ, "mech_srcdate", SRCDATE }, +{ TVINFO_pointer | TVINFO_CHAR, tclv_READ, "mech_class", BOTCLASS }, +{ 0, }}; + +/* + * + * + * + */ + +char *tcl_var_read(Tcl_TVInfo *vinfo, Tcl_Interp *I, char *n1, char *n2, int flags) +{ + Tcl_Obj *obj; + union { + int i; + const char *c; + } rdata; + + switch(vinfo->type & TVINFO_UNTYPED) + { + case TVINFO_pointer: + rdata.c = vinfo->data; + break; + case TVINFO_guid: + rdata.i = (current) ? current->guid : -1; + break; + case TVINFO_nick: + rdata.c = (current) ? current->nick : "(undefined variable)"; + break; + case TVINFO_wantnick: + rdata.c = (current) ? current->wantnick : "(undefined variable)"; + break; + case TVINFO_server: + rdata.i = (current) ? current->server : -1; + break; + case TVINFO_nextserver: + rdata.i = (current) ? current->nextserver : -1; + break; + case TVINFO_currentchan: + rdata.c = (current && current->activechan) ? current->activechan->name : "(undefined variable)"; + break; + default: + return("(undefined variable)"); + } + + if (vinfo->type & TVINFO_INT) + { + obj = Tcl_NewIntObj(rdata.i); + } + else + /* if (vinfo->type & TVINFO_CHAR) */ + { + obj = Tcl_NewStringObj((char*)rdata.c,strlen(rdata.c)); + } + + Tcl_ObjSetVar2(energymech_tcl,vinfo->n1,NULL,obj,TCL_GLOBAL_ONLY); + + return(NULL); +} + +char *tcl_var_write(Tcl_TVInfo *vinfo, Tcl_Interp *I, char *n1, char *n2, int flags) +{ + return("not yet implemented"); +} + +/* + * + * + * + */ + +int tcl_timer_jump(Hook *hook) +{ + return(0); +} + +int tcl_parse_jump(char *from, char *rest, Hook *hook) +{ + Tcl_Obj *tcl_result; + int i; + +#ifdef DEBUG + debug("(tcl_parse_jump) %s %s %s\n", + nullstr(hook->self),nullstr(from),nullstr(rest)); +#endif /* DEBUG */ + + if (from) + nickcpy(CurrentNick,from); + else + *CurrentNick = 0; + + Tcl_SetVar(energymech_tcl,"_from",from,0); + Tcl_SetVar(energymech_tcl,"_rest",rest,0); + + i = 0; + if (Tcl_VarEval(energymech_tcl,hook->self," $_from $_rest",NULL) == TCL_OK) + { + tcl_result = Tcl_GetObjResult(energymech_tcl); + Tcl_GetIntFromObj(energymech_tcl,tcl_result,&i); + } +#ifdef DEBUG + if (energymech_tcl->result && *energymech_tcl->result) + debug("(tcl_parse_jump) result = %s\n",nullstr(energymech_tcl->result)); +#endif /* DEBUG */ + return(i); +} + +#ifdef DCC_FILE + +void tcl_dcc_complete(Client *client, int cps) +{ + Tcl_Obj *obj; + Tcl_Obj *vname; + Hook *hook; + + vname = Tcl_NewStringObj("_cps",3); + for(hook=hooklist;hook;hook=hook->next) + { + if (hook->flags == HOOK_DCC_COMPLETE && + hook->guid && current && hook->guid == current->guid) + { + Tcl_SetVar(energymech_tcl,"_filetarget",client->whom,0); + Tcl_SetVar(energymech_tcl,"_filename",client->filename,0); + obj = Tcl_NewIntObj(cps); + Tcl_ObjSetVar2(energymech_tcl,vname,NULL,obj,TCL_GLOBAL_ONLY); + Tcl_VarEval(energymech_tcl,hook->self," $_filetarget $_filename $_cps",NULL); +#ifdef DEBUG + if (energymech_tcl->result && *energymech_tcl->result) + debug("(tcl_dcc_complete) result = %s\n",nullstr(energymech_tcl->result)); +#endif /* DEBUG */ + } + } +} + +#endif /* DCC_FILE */ + +int tcl_hook(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + Hook *hook; + HookTimer hooktimer; + char *type,*command,*self; + int guid,mode; + int sz1,sz2; + + guid = 0; + if (objc == 5) + { + if (Tcl_GetIntFromObj(energymech_tcl,objv[4],&guid) == TCL_ERROR) + return(TCL_ERROR); + } + else + if (objc != 4) + { + return(TCL_ERROR); + } + + type = Tcl_GetStringFromObj(objv[1],&mode); + command = Tcl_GetStringFromObj(objv[2],&sz1); + self = Tcl_GetStringFromObj(objv[3],&sz2); + + if (!mode || !sz1 || !sz2) + return(TCL_ERROR); + + if (!Strcasecmp(type,"command")) + mode = HOOK_COMMAND; + else + if (!Strcasecmp(type,"dcc_complete")) + mode = HOOK_DCC_COMPLETE; + else + if (!Strcasecmp(type,"parse")) + mode = HOOK_PARSE; + else + if (!Strcasecmp(type,"timer")) + { + if (compile_timer(&hooktimer,command) < 0) + return(TCL_ERROR); + mode = HOOK_TIMER; + sz1 = sizeof(HookTimer); + } + else + { + return(TCL_ERROR); + } + + set_mallocdoer(tcl_hook); + hook = (Hook*)Calloc(sizeof(Hook) + sz1 + sz2); + hook->guid = guid; + hook->flags = mode; + hook->next = hooklist; + hooklist = hook; + + hook->type.any = (void*)(Strcpy(hook->self,self) + 1); + + switch(mode) + { + case HOOK_COMMAND: + case HOOK_PARSE: + Strcpy(hook->type.command,command); + hook->func = tcl_parse_jump; + break; + default: + /* case HOOK_TIMER: */ + memcpy(hook->type.timer,&hooktimer,sizeof(HookTimer)); + hook->func = tcl_timer_jump; + break; + } + +#ifdef DEBUG + debug("(tcl_hook) hooked %s `%s' --> %s\n", + nullstr(type),nullstr(command),nullstr(self)); +#endif /* DEBUG */ + + return(TCL_OK); +} + +int tcl_unhook(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + return(TCL_ERROR); +} + +int tcl_userlevel(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *tcl_result; + char *nuh,*chan; + int n; + + if (!current) + return(TCL_ERROR); + + chan = NULL; + + if (objc == 3) + { + if ((chan = Tcl_GetStringFromObj(objv[2],NULL)) == NULL) + return(TCL_ERROR); + } + else + if (objc != 2) + return(TCL_ERROR); + + if ((nuh = Tcl_GetStringFromObj(objv[1],NULL)) == NULL) + return(TCL_ERROR); + + n = get_useraccess(nuh,chan); + + tcl_result = Tcl_GetObjResult(energymech_tcl); + Tcl_SetIntObj(tcl_result,n); + return(TCL_OK); +} + +#ifdef DEBUG + +int tcl_debug(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + char *text; + + if (objc != 2) + return(TCL_ERROR); + + if ((text = Tcl_GetStringFromObj(objv[1],NULL)) == NULL) + return(TCL_ERROR); + + debug("(tcl_debug) %s\n",text); + + return(TCL_OK); +} + +#endif /* DEBUG */ + +int tcl_to_server(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + Strp *sp,**pp; + Tcl_Obj *tcl_result; + char *line; + int n,sz,sendqmax; + + if (!current) + return(TCL_ERROR); + + sendqmax = -1; + if (objc == 3) + { + if (Tcl_GetIntFromObj(energymech_tcl,objv[2],&sendqmax) == TCL_ERROR) + return(TCL_ERROR); + } + else + if (objc != 2) + return(TCL_ERROR); + + if ((line = Tcl_GetStringFromObj(objv[1],&sz)) == NULL) + return(TCL_ERROR); + +#ifdef DEBUG + debug("(tcl_to_server) max = %i; line = %s",sendqmax,line); +#endif /* DEBUG */ + + if (sendqmax >= 0) + { + n = 0; + pp = ¤t->sendq; + while(*pp) + { + n++; + pp = &(*pp)->next; + } + if (sendqmax && n >= sendqmax) + { + n = -n; + } + else + if (sz) + { + *pp = sp = (Strp*)Calloc(sizeof(Strp) + sz); + /* Calloc sets to zero sp->next = NULL; */ + Strcpy(sp->p,line); + } + } + else + { + if ((n = write(current->sock,line,sz)) < 0) + { +#ifdef DEBUG + debug("(tcl_to_server) {%i} errno = %i\n",current->sock,errno); +#endif /* DEBUG */ + close(current->sock); + current->sock = -1; + current->connect = CN_NOSOCK; + return(TCL_ERROR); + } + current->sendq_time += 2; + } + tcl_result = Tcl_GetObjResult(energymech_tcl); + Tcl_SetIntObj(tcl_result,n); + return(TCL_OK); +} + +int tcl_to_file(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *tcl_result; + char *text; + int fd,sz; + int r; + + if (objc != 3) + return(TCL_ERROR); + + if ((Tcl_GetIntFromObj(energymech_tcl,objv[1],&fd) == TCL_ERROR) + || ((text = Tcl_GetStringFromObj(objv[2],&sz)) == NULL)) + return(TCL_ERROR); + + r = write(fd,text,sz); + + tcl_result = Tcl_GetObjResult(energymech_tcl); + Tcl_SetIntObj(tcl_result,r); + return(TCL_OK); +} + +#ifdef DCC_FILE + +int tcl_dcc_sendfile(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *tcl_result; + char *filename,*target; + int sz; + + if (objc != 3) + return(TCL_ERROR); + if ((target = Tcl_GetStringFromObj(objv[1],NULL)) == NULL) + return(TCL_ERROR); + if ((filename = Tcl_GetStringFromObj(objv[2],NULL)) == NULL) + return(TCL_ERROR); + + if ((sz = dcc_sendfile(target,filename)) < 0) + return(TCL_ERROR); + + tcl_result = Tcl_GetObjResult(energymech_tcl); + Tcl_SetIntObj(tcl_result,sz); + return(TCL_OK); +} + +#endif /* DCC_FILE */ + +#ifdef RAWDNS + +int tcl_dns_jump(char *host, char *resolved, Hook *hook) +{ + Tcl_Obj *tcl_result; + int i; + +#ifdef DEBUG + debug("(tcl_dns_jump) %s %s %s\n", + nullstr(hook->self),nullstr(host),nullstr(resolved)); +#endif /* DEBUG */ + + Tcl_SetVar(energymech_tcl,"_host",host,0); + Tcl_SetVar(energymech_tcl,"_resolved",resolved,0); + + i = 0; + if (Tcl_VarEval(energymech_tcl,hook->self," $_host $_resolved",NULL) == TCL_OK) + { + tcl_result = Tcl_GetObjResult(energymech_tcl); + Tcl_GetIntFromObj(energymech_tcl,tcl_result,&i); + } +#ifdef DEBUG + if (energymech_tcl->result && *energymech_tcl->result) + debug("(tcl_dns_jump) result = %s\n",nullstr(energymech_tcl->result)); +#endif /* DEBUG */ + return(i); +} + +int tcl_dns(void *foo, Tcl_Interp *I, int objc, Tcl_Obj *CONST objv[]) +{ + Tcl_Obj *tcl_result; + Hook *hook; + char *host,*callback; + + if (objc != 3) + return(TCL_ERROR); + if ((host = Tcl_GetStringFromObj(objv[1],NULL)) == NULL) + return(TCL_ERROR); + if ((callback = Tcl_GetStringFromObj(objv[2],NULL)) == NULL) + return(TCL_ERROR); + +#ifdef DEBUG + debug("(tcl_dns) resolving: %s (callback %s)\n",host,callback); +#endif /* DEBUG */ + + set_mallocdoer(tcl_dns); + hook = (Hook*)Calloc(sizeof(Hook) + strlen(host)); + hook->guid = (current) ? current->guid : 0; + hook->flags = HOOK_DNS; + hook->next = hooklist; + hooklist = hook; + hook->type.host = Strcpy(hook->self,callback) + 1; + Strcpy(hook->type.host,host); + hook->func = tcl_dns_jump; + + rawdns(host); + return(TCL_OK); +} + +#endif /* RAWDNS */ + +/* + * + * + * + */ + +LS struct +{ + char *cmdname; + void *func; + +} tcl2mech[] = +{ +{ "to_server", tcl_to_server }, +{ "to_file", tcl_to_file }, +{ "mech_userlevel", tcl_userlevel }, +{ "mech_hook", tcl_hook }, +{ "mech_unhook", tcl_unhook }, +#ifdef DEBUG +{ "mech_debug", tcl_debug }, +#endif /* DEBUG */ +#ifdef DCC_FILE +{ "mech_dcc_sendfile", tcl_dcc_sendfile }, +#endif /* DCC_FILE */ +#ifdef RAWDNS +{ "mech_dns", tcl_dns }, +#endif /* RAWDNS */ +{ NULL, }}; + +void init_tcl(void) +{ +#ifdef DEBUG + void *res; + int resi; +#endif /* DEBUG */ + int i; + + if ((energymech_tcl = Tcl_CreateInterp()) == NULL) + return; + + if (Tcl_Init(energymech_tcl) != TCL_OK) + { + Tcl_DeleteInterp(energymech_tcl); + energymech_tcl = NULL; + return; + } + +#ifdef DEBUG + Tcl_SetVar(energymech_tcl,"define_debug","1",0); +#endif /* DEBUG */ + + for(i=0;tcl2mech[i].cmdname;i++) + { +#ifdef DEBUG + res = +#endif /* DEBUG */ + Tcl_CreateObjCommand(energymech_tcl,tcl2mech[i].cmdname,tcl2mech[i].func,NULL,NULL); +#ifdef DEBUG + debug("(init_tcl) create tcl command: %s (%s)\n",tcl2mech[i].cmdname,(res) ? "SUCCESS" : "FAIL"); +#endif /* DEBUG */ + } + + /* + * trace list of variables + */ + for(i=0;vinfolist[i].name;i++) + { + /* + * make the variable name into a tcl object + */ + vinfolist[i].n1 = Tcl_NewStringObj(vinfolist[i].name,strlen(vinfolist[i].name)); + Tcl_IncrRefCount(vinfolist[i].n1); + /* + * trace read ops + */ +#ifdef DEBUG + resi = +#endif /* DEBUG */ + Tcl_TraceVar(energymech_tcl,vinfolist[i].name,TCL_TRACE_READS | TCL_GLOBAL_ONLY, + (Tcl_VarTraceProc*)tcl_var_read,&vinfolist[i]); +#ifdef DEBUG + debug("(init_tcl) trace tcl variable (read): %s (%s)\n", + vinfolist[i].name,(resi == TCL_OK) ? "SUCCESS" : "FAIL"); +#endif /* DEBUG */ + /* + * trace write ops + */ + if ((vinfolist[i].flags & tclv_WRITE) == 0) + continue; +#ifdef DEBUG + resi = +#endif /* DEBUG */ + Tcl_TraceVar(energymech_tcl,vinfolist[i].name,TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, + (Tcl_VarTraceProc*)tcl_var_write,(ClientData)&vinfolist[i]); +#ifdef DEBUG + debug("(init_tcl) trace tcl variable (write): %s (%s)\n", + vinfolist[i].name,(resi == TCL_OK) ? "SUCCESS" : "FAIL"); +#endif /* DEBUG */ + } +} + +/* + * + * + * + */ + + +void do_tcl(COMMAND_ARGS) +{ + int res; + + if (!energymech_tcl) + { + init_tcl(); + if (!energymech_tcl) + return; + } + +#ifdef PLEASE_HACK_MY_SHELL + if (CurrentCmd->name == C_TCL) + { + res = Tcl_Eval(energymech_tcl,rest); + to_user(from,"tcl command %s",(res == TCL_OK) ? "executed ok" : "failed"); + } + else +#endif /* PLEASE_HACK_MY_SHELL */ + { + res = Tcl_EvalFile(energymech_tcl,rest); + to_user(from,"tcl script %s",(res == TCL_OK) ? "loaded ok" : "failed to load"); +#ifdef DEBUG + debug("(do_tcl) tcl script \"%s\" %s\n",rest,(res == TCL_OK) ? "loaded ok" : "failed to load"); +#endif /* DEBUG */ + } +} + +#endif /* TCL */ diff --git a/src/telnet.c b/src/telnet.c new file mode 100644 index 0000000..d12db36 --- /dev/null +++ b/src/telnet.c @@ -0,0 +1,84 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define TELNET_C +#include "config.h" + +#ifdef TELNET + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" + +int check_telnet(int s, char *rest) +{ + Client *client; + User *user; + + user = NULL; + for(current=botlist;current;current=current->next) + { + for(user=current->userlist;user;user=user->next) + { + if (!Strcasecmp(user->name,rest)) + goto check_telnet_malloc; + } + } + if (!current) + current = botlist; +check_telnet_malloc: + to_file(s,TEXT_ENTERPASSWORD "\n"); + set_mallocdoer(check_telnet); + client = (Client*)Calloc(sizeof(Client)); + client->user = user; + client->sock = s; +#ifdef DCC_FILE + client->fileno = -1; +#endif /* DCC_FILE */ + client->flags = DCC_TELNETPASS; + client->lasttime = now; + client->next = current->clientlist; + current->clientlist = client; +#ifdef DEBUG + current = NULL; + debug("(check_telnet) catching telnet client\n"); +#endif /* DEBUG */ + return(TRUE); +} + +void check_telnet_pass(Client *client, char *rest) +{ + char *pass; + + pass = chop(&rest); + if (!pass || !client->user || !client->user->pass || !passmatch(pass,client->user->pass)) + { + client->flags = DCC_DELETE; + return; + } + + Strcpy(CurrentNick,client->user->name); + dcc_banner(client); + client->flags = DCC_TELNET|DCC_ACTIVE; +} + +#endif /* TELNET */ diff --git a/src/text.h b/src/text.h new file mode 100644 index 0000000..14a2795 --- /dev/null +++ b/src/text.h @@ -0,0 +1,202 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2000-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef TEXT_H +#define TEXT_H 1 + +/* + * These are more or less globally used.. + */ + +#define FMT_PLAIN "%s" +#define FMT_PLAINLINE "%s\n" +#define MATCH_ALL "*" + +#define TEXT_NOTINSERVLIST "(not in serverlist)" +#define TEXT_NONE "(none)" + +#define TEXT_LISTSAVED "Lists saved to file %s" +#define TEXT_LISTREAD "Lists read from file %s" +#define ERR_NOSAVE "Lists could not be saved to file %s" +#define ERR_NOREAD "Lists could not be read from file %s" +#define ERR_NOUSERFILENAME "No userfile has been set" + +#define TEXT_UNKNOWNUSER "Unknown user: %s" + +/* + * alias.c + */ +#define TEXT_NOALIASES "No aliases has been set" + +/* + * channel.c + */ +#define TEXT_SENTWALLOP "Sent wallop to %s" + +#define TEXT_TOPICCHANGED "Topic changed on %s" +#define TEXT_CHANINVALID "Invalid channel name" + +/* + * ctcp.c + */ +#define TEXT_DCC_ONLY "Multiline output from \"%s\" command requires DCC chat" +#define TEXT_DCC_GOODBYE "Hasta la vista!" + +#define TEXT_NOTCONNECTED "(not connected)" +#define TEXT_WHOMUSERLINE "%s\tu%i\t%s (idle %i min, %i sec)" +#define TEXT_WHOMSELFLINE "\037%s\037\t%s\t%s" +#define TEXT_WHOMBOTLINE "%s\t%s\t%s" +#define TEXT_WHOMBOTGUID "%s\t%s\t%s [%s] [%i]" + +/* + * core.c + */ +#define TEXT_EMPTYSERVLIST "No servers in serverlist!" +#define TEXT_NOSERVMATCHP "No matching entries was found for %s:%i" +#define TEXT_NOSERVMATCH "No matching entries was found for %s:*" +#define TEXT_SERVERDELETED "Server has been deleted: %s:%i" +#define TEXT_MANYSERVMATCH "Several entries for %s exists, please specify port also" +/* do_core() */ +#define TEXT_CURRNICKWANT "Current nick\t%s (Wanted: %s)" +#define TEXT_CURRNICKHAS "Current nick\t%s" +#define TEXT_CURRGUID "Guid\t%i" +#define TEXT_USERLISTSTATS "Users in userlist\t%i (%i Superuser%s, %i Bot%s)" +#define TEXT_ACTIVECHANS "Active channels\t%s" +#define TEXT_MOREACTIVECHANS "\t%s" + +#define TEXT_VIRTHOST "Virtual host\t%s (IP Alias%s)" +#define TEXT_VIRTHOSTWINGATE "Virtual host\t%s:%i (WinGate%s)" +#define TEXT_VHINACTIVE " - Inactive" + +#define TEXT_CURRSERVER "Current Server\t%s:%i" +#define TEXT_CURRSERVERNOT "Current Server\t" TEXT_NOTINSERVLIST +#define TEXT_SERVERONTIME "Server Ontime\t%s" +#define TEXT_BOTMODES "Mode\t+%s" + +#define TEXT_CURRENTTIME "Current Time\t%s" +#define TEXT_BOTSTARTED "Started\t%s" +#define TEXT_BOTUPTIME "Uptime\t%s" +#define TEXT_BOTVERSION "Version\t%s (%s)" +#define TEXT_BOTFEATURES "Features\t%s" + +#define TEXT_CSERV "Current Server: %s:%i" +#define TEXT_CSERVNOT "Current Server: " TEXT_NOTINSERVLIST + +#define TEXT_AGO " ago" +#define TEXT_CURRENT " (current)" +#define TEXT_NEVER "(never)" + +#define TEXT_SP_NOAUTH "(no authorization)" +#define TEXT_SP_KLINED "(K-lined)" +#define TEXT_SP_FULLCLASS "(connection class full)" +#define TEXT_SP_TIMEOUT "(connection timed out)" +#define TEXT_SP_ERRCONN "(unable to connect)" +#define TEXT_SP_DIFFPORT "(use a different port)" +#define TEXT_SP_NO_DNS "(DNS problem)" + +#define TEXT_NOLONGERAWAY "No longer set /away" +#define TEXT_NOWSETAWAY "Now set /away" + +#define TEXT_NAMETOOLONG "Hostname exceeds maximum length" + +#define TEXT_SHUTDOWNBY "Shutdown initiated by %s[100], flatlining ..." + +/* + * main.c + */ + +#define TEXT_SIGINT "Lurking interrupted by luser ... er, owner. (SIGINT)" +#define TEXT_SIGSEGV "Mary had a little signal segmentation fault (SIGSEGV)" +#define TEXT_SIGBUS "Another one drives the bus! (SIGBUS)" +#define TEXT_SIGTERM "What have I done to deserve this?? aaaaaarrghhh! (SIGTERM)" +#define TEXT_SIGUSR1 "QUIT :Switching servers... (SIGUSR1)\n" + +#define TEXT_USAGE "Usage: %s [switches [args]]\n" +#define TEXT_FSWITCH " -f read configuration from \n" +#define TEXT_CSWITCH " -c make core file instead of coredebug/reset\n" +#define TEXT_HSWITCH " -h show this help\n" +#define TEXT_VSWITCH " -v show EnergyMech version\n" + +#define TEXT_HDR_VERS "EnergyMech %s, %s\n" +#define TEXT_HDR_DATE "Compiled on " __DATE__ " " __TIME__ "\n" +#define TEXT_HDR_FEAT "Features: %s\n" + +#define ERR_MISSINGCONF "init: No configfile specified\n" +#define ERR_UNKNOWNOPT "init: Unknown option %s\n" +#define ERR_SAMEUSERLIST "init: Error: UserList for %s matches the userlist for %s\n" +#define ERR_SAMEUSERLIST2 " Bots can not share the same userlist, please specify a new one.\n" + +#define INFO_USINGCONF "init: Using config file: %s\n" +#define INFO_RUNNING "init: EnergyMech running...\n" + + +#define TEXT_ALREADYSHITTED "%s is in my shitlist already for this channel" +#define TEXT_SHITLOWACCESS "Unable to shit %s, insufficient access" +#define TEXT_DEFAULTSHIT "Leave Lamer!" +#define TEXT_HASSHITTED "The user has been shitted as %s on %s" +#define TEXT_SHITEXPIRES "The shit entry will expire: %s" + + +#define TEXT_SEENNOSAVE "SeenList could not be saved to file %s" +#define TEXT_SEENNOLOAD "SeenList could not be loaded from file %s" + +/* + * ons.c + */ +#define TEXT_LASTHDR str_underline("Last %i Commands") + +#define KICK_BANNED "banned" +#define KICK_REVENGE "revenge" +#define KICK_CAPS "excessive caps" +#define KICK_MASSMODES "massmodes" +#define KICK_NICKFLOOD "nickflood" +#define KICK_TEXTFLOOD "textflood" +#define KICK_BAD_IDENT "bad ident" +#define KICK_DEFAULT "Requested Kick" + +/* + * telnet + */ +#define TEXT_ENTERNICKNAME "Please enter your nickname." +#define TEXT_ENTERPASSWORD "Please enter your password." + +/* + * shit.c + */ +#define TEXT_CLEAREDSHITLIST "Shitlist has been cleared" + +/* + * user.c + */ +#define TEXT_PASS_SHORT "password too short" +#define TEXT_PASS_LONG "password too long" +#define TEXT_PASS_INCORRECT "password incorrect" +#define TEXT_PASS_NEWSET "new password has been set" + +#define TEXT_PARTYECHOON "Partyline echo is now On" +#define TEXT_PARTYECHOOFF "Partyline echo is now Off" + +#define TEXT_USERCHANGED "User %s has been modified" +#define TEXT_USERNOTCHANGED "User %s is unmodified" + +#define TEXT_NOACCESSON "Access denied (you have no access on %s)" +#define TEXT_USEROWNSYOU "Access denied (%s has higher access than you)" + +#endif /* TEXT_H */ diff --git a/src/toybox.c b/src/toybox.c new file mode 100644 index 0000000..3890e9a --- /dev/null +++ b/src/toybox.c @@ -0,0 +1,349 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2000-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define TOYBOX_C +#include "config.h" + +#ifdef TOYBOX +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#define BIGSAY_DEFAULTFONT "default" +#define FONT_EXTENSION ".bigchars" + +typedef struct BigC +{ + struct BigC *next; + + int width; + Strp *data; + char chars[1]; + +} BigC; + +LS char *fontname = NULL; +LS BigC *newchar; + +LS BigC *fontlist = NULL; +LS int charlines; +LS int charheight; +LS int spacewidth; +LS int kerning; + +LS BigC *orig_fontlist = NULL; +LS int orig_charlines; +LS int orig_charheight; +LS int orig_spacewidth; +LS int orig_kerning; + +int read_bigcharset_callback(char *rest) +{ + Strp *sp,**pp; + char *opt; + int *n,sz; + + if (charlines) + { + charlines--; + sz = strlen(rest); + if (sz > newchar->width) + newchar->width = sz; + pp = &newchar->data; + while(*pp) + pp = &(*pp)->next; + set_mallocdoer(read_bigcharset_callback); + *pp = sp = (Strp*)Calloc(sizeof(Strp) + sz); + /* Calloc sets to zero sp->next = NULL; */ + Strcpy(sp->p,rest); + return(FALSE); + } + + opt = chop(&rest); + n = NULL; + + if (!Strcasecmp(opt,"chars") && charheight) + { + charlines = charheight; + + opt = chop(&rest); + set_mallocdoer(read_bigcharset_callback); + newchar = (BigC*)Calloc(sizeof(BigC) + strlen(opt)); + /* Calloc sets to zero + newchar->width = 0; + newchar->data = NULL; + */ + newchar->next = fontlist; + fontlist = newchar; + Strcpy(newchar->chars,opt); + } + else + if (!Strcasecmp(opt,"spacewidth")) + { + n = &spacewidth; + } + else + if (!Strcasecmp(opt,"charheight")) + { + n = &charheight; + } + else + if (!Strcasecmp(opt,"kerning")) + { + n = &kerning; + } + else + if (!Strcasecmp(opt,"fontname")) + { + opt = chop(&rest); + if (fontname && !Strcasecmp(fontname,opt)) + { + fontlist = orig_fontlist; + charlines = orig_charlines; + charheight = orig_charheight; + spacewidth = orig_spacewidth; + kerning = orig_kerning; + orig_fontlist = NULL; + return(TRUE); + } + Free((char**)&fontname); + set_mallocdoer(read_bigcharset_callback); + fontname = Strdup(opt); + } + + if (n) + { + *n = a2i(rest); + if (errno) *n = 0; + } + + return(FALSE); +} + +int read_bigcharset(char *fname) +{ + BigC *bigc; + Strp *sp; + int fd; + + if ((fd = open(fname,O_RDONLY)) < 0) + { + Strcat(fname,FONT_EXTENSION); + if ((fd = open(fname,O_RDONLY)) < 0) + return(-1); + } + + orig_fontlist = fontlist; + orig_charlines = charlines; + orig_charheight = charheight; + orig_spacewidth = spacewidth; + orig_kerning = kerning; + + charlines = 0; + spacewidth = 0; + charheight = 0; + kerning = 0; + + readline(fd,&read_bigcharset_callback); /* readline closes fd */ + + /* + * free the old font if there is one + */ + while(orig_fontlist) + { + bigc = orig_fontlist; + orig_fontlist = bigc->next; + while(bigc->data) + { + sp = bigc->data; + bigc->data = sp->next; + Free((char**)&sp); + } + Free((char**)&bigc); + } + + return(0); +} + +/* + * + * + * + */ + +void do_bigsay(COMMAND_ARGS) +{ + /* + * on_msg checks CARGS + CAXS + */ + BigC *bigc; + Strp *sp; + char output[MSGLEN]; + char *pt,*tail,*temp; + int i,x,sz; + +#ifdef DEBUG + debug("(do_bigsay) rest = \"%s\"\n",rest); +#endif /* DEBUG */ + + Strcpy(output,BIGSAY_DEFAULTFONT); + + if (read_bigcharset(output) < 0) + { + to_user(from,ERR_FILEOPEN,output); + return; + } + + for(i=0;inext) + { + if (STRCHR(bigc->chars,*pt)) + { + sp = bigc->data; + for(x=0;xnext; + temp = Strcat(tail,sp->p); + while(temp < (tail + bigc->width)) + *(temp++) = ' '; + if (pt[1]) + { + x = kerning; + while(x--) + *(temp++) = ' '; + } + *temp = 0; + tail = temp; + break; + } + } + } + temp = NULL; + for(tail=output;*tail;tail++) + { + if (!temp && *tail == ' ') + temp = tail; + if (*tail != ' ') + temp = NULL; + } + if (temp) + { + if (temp == output) + temp++; + *temp = 0; + } + to_user_q(from,FMT_PLAIN,output); + } +} + +void do_random_msg(COMMAND_ARGS) +{ + char *filename,*message; + + filename = CurrentCmd->cmdarg; + + if (*rest) + { + to = chop(&rest); + if (ischannel(to) && (get_authaccess(from,to) < cmdaccess)) + return; + } + else + if (!ischannel(to)) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + + if ((message = randstring(filename)) == NULL) + { + to_user(from,ERR_FILEOPEN,filename); + return; + } + + /* + * send message to target nick/channel + */ + to_server("PRIVMSG %s :%s\n",to,message); + /* + * if its not a channel we send a copy to the user who did the command also + */ + if (!ischannel(to)) + { + to_user(from,"(%s) %s",to,message); + } +} + +void do_randtopic(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + */ + Chan *chan = CurrentChan; + char *topic; + + /* + * the bot can set a random topic if chanmode is -t + */ + if (chan->bot_is_op || chan->topprot == FALSE) + { + if ((topic = randstring(RANDTOPICSFILE)) == NULL) + { + to_user(from,ERR_FILEOPEN,RANDTOPICSFILE); + return; + } + to_server("TOPIC %s :%s\n",to,topic); + to_user(from,TEXT_TOPICCHANGED,to); + return; + } + to_user(from,ERR_NOTOPPED,to); +} + +void do_8ball(COMMAND_ARGS) +{ + char *message; + + if ((message = randstring(RAND8BALLFILE)) == NULL) + { + to_user_q(from,ERR_FILEOPEN,RAND8BALLFILE); + return; + } + + to_user_q(from,FMT_PLAIN,message); +} + +#endif /* TOYBOX */ diff --git a/src/trivia.c b/src/trivia.c new file mode 100644 index 0000000..aa28ac5 --- /dev/null +++ b/src/trivia.c @@ -0,0 +1,636 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2001-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define TRIVIA_C +#include "config.h" + +#ifdef TRIVIA +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +#define TRIV_WAIT_QUESTION 0 +#define TRIV_HINT_TWO 1 +#define TRIV_HINT_THREE 2 +#define TRIV_NO_ANSWER 3 + +#define TRIV_HINT_DELAY 15 + +#define TRIV_HINT_DELAY_STR1 "15" +#define TRIV_HINT_DELAY_STR2 "30" + +#define TRIV_METACHARS " .,-'%&/?!:;\"" + +#define DAY_IN_SECONDS (24*60*60) +#define WEEK_IN_SECONDS (7*24*60*60) + +LS TrivScore *lastwinner; +LS Chan *triv_chan = NULL; +LS Strp *triv_answers = NULL; +LS time_t triv_ask_time; +LS time_t triv_next_time = 0; +LS time_t triv_weektop10; +LS int triv_mode; +LS int triv_score; +LS int triv_streak; +LS int triv_halt_flag; + +/* + * + * scorings + * + */ + +void trivia_week_toppers(void) +{ + char triv_str[MSGLEN]; + TrivScore *chosen[10]; + TrivScore *su; + char *p; + int week; + int i,x; + + chosen[0] = NULL; + week = (now + (3 * DAY_IN_SECONDS)) / WEEK_IN_SECONDS; + + for(su=scorelist;su;su=su->next) + { + if (su->week_nr != week) + continue; + for(i=0;i<10;i++) + { + if (!chosen[i] || (chosen[i]->score_wk < su->score_wk)) + { + for(x=8;x>=i;x--) + chosen[x+1] = chosen[x]; + chosen[i] = su; + break; + } + } + } + + if (chosen[0]) + { + p = triv_str; + for(i=0;i<10;i++) + { + if (!chosen[i]) + break; + if (i) + { + *(p++) = ' '; + *(p++) = ' '; + } + sprintf(p,"#%i: %s (%ipts)",i+1,chosen[i]->nick,chosen[i]->score_wk); + p = STREND(p); + } + to_server("PRIVMSG %s :This Weeks Top 10: %s\n",triv_chan->name,triv_str); + } +} + +/* + * + * + * + */ + +void hint_one(void) +{ + char triv_str[MSGLEN]; + char *src,*dst; + + sprintf(triv_str,"PRIVMSG %s :1st hint: ",triv_chan->name); + dst = STREND(triv_str); + src = triv_answers->p; + while(*src) + { + if (STRCHR(TRIV_METACHARS,*src)) + *(dst++) = *src; + else + *(dst++) = triv_qchar; + src++; + } + *dst = 0; + to_server("%s Question score: %i points\n",triv_str,triv_score); +} + +void hint_two(void) +{ + char triv_str[MSGLEN]; + char *src,*dst; + int n; + + sprintf(triv_str,"PRIVMSG %s :2nd hint: ",triv_chan->name); + dst = STREND(triv_str); + src = triv_answers->p; + + n = a2i(src); + if (!errno) + { + if (n > 99 && *src) *(dst++) = *(src++); + if (n > 999 && *src) *(dst++) = *(src++); + if (n > 9999 && *src) *(dst++) = *(src++); + } + else + { + n = strlen(src); + if (n > 2 && *src) *(dst++) = *(src++); + if (n > 4 && *src) *(dst++) = *(src++); + if (n > 6 && *src) *(dst++) = *(src++); + } + + while(*src) + { + if (STRCHR(TRIV_METACHARS,*src)) + *(dst++) = *src; + else + *(dst++) = triv_qchar; + src++; + } + *dst = 0; + to_server("%s " TRIV_HINT_DELAY_STR2 " seconds remaining.\n",triv_str); +} + +void hint_three(void) +{ + char triv_str[MSGLEN]; + char *src,*dst; + int n; + + sprintf(triv_str,"PRIVMSG %s :3rd hint: ",triv_chan->name); + dst = STREND(triv_str); + src = triv_answers->p; + + n = a2i(src); + if (!errno) + { + if (n > 9 && *src) *(dst++) = *(src++); + if (n > 99 && *src) *(dst++) = *(src++); + if (n > 999 && *src) *(dst++) = *(src++); + } + else + { + n = strlen(src); + if (n > 1 && *src) *(dst++) = *(src++); + if (n > 3 && *src) *(dst++) = *(src++); + if (n > 4 && *src) *(dst++) = *(src++); + } + + while(*src) + { + if (STRCHR(TRIV_METACHARS "aeiouyAEIOUY",*src)) + *(dst++) = *src; + else + *(dst++) = triv_qchar; + src++; + } + *dst = 0; + to_server("%s " TRIV_HINT_DELAY_STR1 " seconds remaining.\n",triv_str); +} + +/* + * + * + * + */ + +void trivia_cleanup(void) +{ + Strp *ans; + + triv_mode = TRIV_WAIT_QUESTION; + triv_next_time = now + triv_qdelay; + while((ans = triv_answers)) + { + triv_answers = ans->next; + Free((char**)&ans); + } +} + +void trivia_check(Chan *chan, char *rest) +{ + TrivScore *su; + Strp *ans; + int week; + + if (chan != triv_chan) + return; + + for(ans=triv_answers;ans;ans=ans->next) + { + if (!Strcasecmp(ans->p,rest)) + goto have_answer; + } + return; + +have_answer: + week = (now + (3 * DAY_IN_SECONDS)) / WEEK_IN_SECONDS; + + for(su=scorelist;su;su=su->next) + { + if (!nickcmp(su->nick,CurrentNick)) + { + su->score_mo += triv_score; + + if (su->week_nr == week) + su->score_wk += triv_score; + else + { + if (su->week_nr == (week - 1)) + su->score_last_wk = su->score_wk; + else + su->score_last_wk = 0; + su->week_nr = week; + su->score_wk = triv_score; + } + break; + } + } + if (!su) + { + set_mallocdoer(trivia_check); + su = (TrivScore*)Calloc(sizeof(TrivScore) + strlen(CurrentNick)); + su->next = scorelist; + scorelist = su; + su->score_wk = su->score_mo = triv_score; + su->week_nr = week; + /* su->month_nr = 0; * fix this */ + Strcpy(su->nick,CurrentNick); + } + + to_server("PRIVMSG %s :Yes, %s! got the answer -> %s <- in %i seconds, and gets %i points!\n", + triv_chan->name,CurrentNick,triv_answers->p, + (int)(now - triv_ask_time),triv_score); + + if (su == lastwinner) + { + triv_streak++; + if (su->score_wk == su->score_mo) + { + to_server("PRIVMSG %s :%s has won %i in a row! Total score this Week: %i points\n", + triv_chan->name,CurrentNick,triv_streak,su->score_wk); + } + else + { + to_server("PRIVMSG %s :%s has won %i in a row! Total score this Week: %i points, and this Month: %i points\n", + triv_chan->name,CurrentNick,triv_streak,su->score_wk,su->score_mo); + } + } + else + { + lastwinner = su; + triv_streak = 1; + if (su->score_wk == su->score_mo) + { + to_server("PRIVMSG %s :%s has won the question! Total score this Week: %i points\n", + triv_chan->name,CurrentNick,su->score_wk); + } + else + { + to_server("PRIVMSG %s :%s has won the question! Total score this Week: %i points, and this Month: %i points\n", + triv_chan->name,CurrentNick,su->score_wk,su->score_mo); + } + } + + trivia_cleanup(); +} + +void trivia_no_answer(void) +{ + to_server("PRIVMSG %s :Time's up! The answer was -> %s <-\n", + triv_chan->name,triv_answers->p); + + trivia_cleanup(); +} + +char *random_question(char *triv_rand) +{ + char *p; + off_t sz; + int fd,ifd; + int n; + struct + { + int off; + int sz; + + } entry; + + if ((fd = open(triv_qfile,O_RDONLY)) < 0) + return(NULL); + + Strcpy(triv_rand,triv_qfile); + if ((p = STRCHR(triv_rand,'.')) == NULL) + p = STREND(triv_rand); + Strcpy(p,".index"); + + if ((ifd = open(triv_rand,O_RDONLY)) < 0) + return(NULL); + + sz = lseek(ifd,0,SEEK_END); + sz = sz / sizeof(entry); + n = RANDOM(1,sz); + n--; + + lseek(ifd,(n * sizeof(entry)),SEEK_SET); + read(ifd,&entry,sizeof(entry)); + + lseek(fd,entry.off,SEEK_SET); + read(fd,triv_rand,entry.sz); + triv_rand[entry.sz] = 0; + + close(fd); + close(ifd); + + return(triv_rand); +} + +void trivia_question(void) +{ + char buffer[MSGLEN]; + Strp *ans,**pp; + char *question,*answer,*rest; + + if (triv_halt_flag) + { + to_server("PRIVMSG %s :Trivia has been stopped!\n",triv_chan->name); + goto stop_trivia; + } + + if ((rest = random_question(buffer)) == NULL) + { +bad_question: + to_server("PRIVMSG %s :Bad Question File\n",triv_chan->name); +stop_trivia: + trivia_cleanup(); + triv_chan = NULL; + triv_next_time = 0; + triv_halt_flag = FALSE; + short_tv &= ~TV_TRIVIA; + return; + } + + question = get_token(&rest,MATCH_ALL); + + pp = &triv_answers; + while((answer = get_token(&rest,MATCH_ALL))) + { + set_mallocdoer(trivia_question); + *pp = ans = (Strp*)Calloc(sizeof(Strp) + strlen(answer)); + pp = &ans->next; + Strcpy(ans->p,answer); + } + *pp = NULL; + + if (triv_answers == NULL) + goto bad_question; + + triv_score = (RANDOM(2,9) + RANDOM(2,10) + RANDOM(2,10)) / 3; + triv_ask_time = now; + + if (now > (triv_weektop10 + 1200)) + { + trivia_week_toppers(); + triv_weektop10 = now; + } + + to_server("PRIVMSG %s :%s\n",triv_chan->name,question); + hint_one(); +} + +void trivia_tick(void) +{ + Chan *chan; + Mech *bot; + + if (triv_next_time && (now >= triv_next_time)) + { + for(bot=botlist;bot;bot=bot->next) + { + for(chan=bot->chanlist;chan;chan=chan->next) + { + if (triv_chan == chan) + { + current = bot; + triv_next_time = now + TRIV_HINT_DELAY; + switch(triv_mode) + { + case TRIV_WAIT_QUESTION: + trivia_question(); + break; + case TRIV_HINT_TWO: + hint_two(); + break; + case TRIV_HINT_THREE: + hint_three(); + break; + case TRIV_NO_ANSWER: + trivia_no_answer(); + return; /* dont increment with triv_mode */ + } + triv_mode++; +#ifdef DEBUG + current = NULL; +#endif /* DEBUG */ + return; + } + } + } + } +} + +/* + * + * File operations + * + */ + +void write_triviascore(void) +{ + TrivScore *su; + int fd; + + if (scorelist) + { + if ((fd = open(TRIVIASCOREFILE,O_WRONLY|O_TRUNC|O_CREAT,NEWFILEMODE)) < 0) + return; + to_file(fd,COMMENT_STRCHR " nick score_wk score_mo score_last_wk score_last_mo\n"); + for(su=scorelist;su;su=su->next) + { + to_file(fd,"%s %i %i %i %i %i %i\n",su->nick,su->score_wk,su->score_mo, + su->score_last_wk,su->score_last_mo,su->week_nr,su->month_nr); + } + close(fd); + } +} + +int trivia_score_callback(char *rest) +{ + TrivScore *su; + char *nick,*wk,*mo,*lwk,*lmo,*wnr,*mnr; + int score_wk,score_mo,score_last_wk,score_last_mo; + int week_nr,month_nr; + int err; + + if (*rest != COMMENT_CHAR) + { + nick = chop(&rest); + wk = chop(&rest); + mo = chop(&rest); + lwk = chop(&rest); + lmo = chop(&rest); + wnr = chop(&rest); + mnr = chop(&rest); + + if (mnr) + { + score_wk = a2i(wk); + err = errno; + score_mo = a2i(mo); + err += errno; + score_last_wk = a2i(lwk); + err += errno; + score_last_mo = a2i(lmo); + err += errno; + week_nr = a2i(wnr); + err += errno; + month_nr = a2i(mnr); + err += errno; + + if (!err) + { + set_mallocdoer(trivia_score_callback); + su = (TrivScore*)Calloc(sizeof(TrivScore) + strlen(nick)); + su->next = scorelist; + scorelist = su; + su->score_wk = score_wk; + su->score_mo = score_mo; + su->score_last_wk = score_last_wk; + su->score_last_mo = score_last_mo; + su->week_nr = week_nr; + su->month_nr = month_nr; + Strcpy(su->nick,nick); + } + } + } + return(FALSE); +} + +void read_triviascore(void) +{ + TrivScore *su; + int fd; + + if ((fd = open(TRIVIASCOREFILE,O_RDONLY)) < 0) + return; + + while(scorelist) + { + su = scorelist; + scorelist = su->next; + Free((char**)&su); + } + + readline(fd,&trivia_score_callback); /* readline closes fd */ +} + +/* + * + * + * + */ + +void do_trivia(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + CARGS + */ + Chan *chan; + int uaccess; + + uaccess = get_maxaccess(from); + + if ((chan = find_channel_ac(to)) == NULL) + { + if (uaccess) to_user(from,ERR_CHAN,to); + return; + } + + if (!Strcasecmp(rest,"start")) + { + if (triv_chan) + { + if (triv_chan == chan) + { + triv_halt_flag = FALSE; + return; + } + if (uaccess) to_user(from,"trivia is already activated on %s!", + (get_useraccess(from,triv_chan->name)) ? triv_chan->name : "another channel"); + return; + } + to_server("PRIVMSG %s :Trivia starting! Get ready...\n",chan->name); + triv_chan = chan; + triv_mode = TRIV_WAIT_QUESTION; + triv_next_time = now + triv_qdelay; + triv_weektop10 = now; + lastwinner = NULL; + short_tv |= TV_TRIVIA; + if (!scorelist) + { + read_triviascore(); + } + } + else + if (!Strcasecmp(rest,"stop")) + { + if (chan == triv_chan) + { + if (chan == triv_chan) + to_server("PRIVMSG %s :Trivia shutting down...\n",chan->name); + triv_halt_flag = TRUE; + } + } + else + if (!Strcasecmp(rest,"top10")) + { + int n; + + if (triv_chan) + { + uaccess = get_authaccess(from,triv_chan->name); + if (now > (triv_weektop10 + 300)) + n = 1; + else + n = uaccess; + + if (n) + { + trivia_week_toppers(); + if (!uaccess) triv_weektop10 = now; + } + } + } +} + +#endif /* TRIVIA */ diff --git a/src/uptime.c b/src/uptime.c new file mode 100644 index 0000000..42c1117 --- /dev/null +++ b/src/uptime.c @@ -0,0 +1,269 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2004 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define UPTIME_C +#include "config.h" + +#ifdef UPTIME +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" + +/* + * Uptime stuffies + */ + +typedef struct +{ + int regnr; + int pid; + int type; + ulong cookie; + ulong uptime; + ulong ontime; + ulong now; + ulong sysup; + +} PackStub; + +typedef struct +{ + int regnr; + int pid; + int type; + ulong cookie; + ulong uptime; + ulong ontime; + ulong now; + ulong sysup; + char string[512]; + +} PackUp; + +void init_uptime(void) +{ + struct sockaddr_in sai; + + uptimecookie = rand(); + + if (!uptimehost) + { + set_mallocdoer(init_uptime); + uptimehost = Strdup(defaultuptimehost); + } + + if ((uptimesock = socket(AF_INET,SOCK_DGRAM,0)) < 0) + { + uptimesock = -1; + return; + } + + memset(&sai,0,sizeof(sai)); + + sai.sin_family = AF_INET; + sai.sin_addr.s_addr = INADDR_ANY; + + if (bind(uptimesock,(struct sockaddr*)&sai,sizeof(sai)) < 0) + { + close(uptimesock); + uptimesock = -1; + return; + } + SockFlags(uptimesock); +} + +void send_uptime(int type) +{ + PackUp upPack; + struct sockaddr_in sai; + struct stat st; + Server *sp; + const char *server,*nick; + int sz; + + if (uptimeport == 0) + return; + +#ifdef RAWDNS + if (uptimeip == -1 && uptimehost) + { + char *host; + + uptimelast = now + 10; + if ((host = poll_rawdns(uptimehost))) + { + if ((uptimeip = inet_addr(host)) != -1) + { + Free((char**)&uptimehost); + set_mallocdoer(send_uptime); + uptimehost = Strdup(host); + } + } + else + { + if (type < UPTIME_GENERICDEATH) + rawdns(uptimehost); + return; + } + } +#endif /* RAWDNS */ + + /* + * update the time when we last sent packet + */ + sz = (uptimelast + 1) & 7; + uptimelast = (now & ~7) + 21600 + sz; /* 21600 seconds = 6 hours */ + + uptimecookie = (uptimecookie + 1) * 18457; + upPack.cookie = htonl(uptimecookie); + + upPack.now = htonl(now); + upPack.regnr = uptimeregnr; + upPack.type = htonl(type); + upPack.uptime = htonl(uptime); + upPack.ontime = 0; /* set a few lines down */ + + /* + * callouts to other functions should be done last (think compiler optimizations) + */ + upPack.pid = htonl(getpid()); + + /* + * this trick for most systems gets the system uptime + */ + if (stat("/proc",&st) < 0) + { + upPack.sysup = 0; + } + else + { + upPack.sysup = htonl(st.st_ctime); + } + + server = "unknown"; + nick = BOTLOGIN; + + /* + * set bot related stuff from the first bot in the list + */ + if (botlist) + { + nick = botlist->nick; + upPack.ontime = htonl(botlist->conntry); + if ((sp = find_server(botlist->server))) + { + server = (*sp->realname) ? sp->realname : sp->name; + } + } + + if (uptimenick) + { + nick = uptimenick; + } + +#ifndef RAWDNS + if ((uptimeip == -1) || ((uptimelast & 7) == 0)) + { + uptimeip = get_ip(uptimehost); + if (uptimeip == -1) + return; + } +#endif /* ! RAWDNS */ + + sz = sizeof(PackStub) + 3 + Strlen(nick,server,VERSION,NULL); + if (sz > sizeof(PackUp)) + return; + + sprintf(upPack.string,"%s %s %s",nick,server,VERSION); + + /* + * udp sending... + */ + memset((char*)&sai,0,sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_addr.s_addr = uptimeip; + sai.sin_port = htons(uptimeport); + + sendto(uptimesock,(void*)&upPack,sz,0,(struct sockaddr*)&sai,sizeof(sai)); +} + +void uptime_death(int type) +{ +#ifdef DEBUG + debug("(uptime_death) sending death message\n"); +#endif /* DEBUG */ + time(&now); + uptimelast = 0; /* avoid resolving the hostname */ + send_uptime(type); + uptimeport = 0; /* avoid sending more packets */ +} + +void process_uptime(void) +{ + struct sockaddr_in sai; + int res,sz; + struct + { + int regnr; + ulong cookie; + + } regPack; + + if (uptimesock == -1) + return; + + if (FD_ISSET(uptimesock,&read_fds)) + { + sz = sizeof(sai); + res = recvfrom(uptimesock,(void*)®Pack,sizeof(regPack),0,(struct sockaddr*)&sai,&sz); + if (res == sizeof(regPack)) + { + if (uptimecookie == ntohl(regPack.cookie)) + { + if (uptimeregnr == 0) + uptimeregnr = ntohl(regPack.regnr); + } + } + } + + if (uptimelast < now) + { + send_uptime(UPTIME_BOTTYPE); + } +} + +/* + * + * commands related to uptimes + * + */ + +void do_upsend(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + */ + send_uptime(UPTIME_BOTTYPE); +} + +#endif /* UPTIME */ diff --git a/src/usage.h b/src/usage.h new file mode 100644 index 0000000..95c1b14 --- /dev/null +++ b/src/usage.h @@ -0,0 +1,183 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 2000-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +typedef struct +{ + const char *command; + char *usage; + +} UsageList; + +LS const UsageList ulist[] = +{ +#ifdef TOYBOX +{ C_8BALL, "[text]" }, +#endif /* TOYBOX */ +{ C_ACCESS, "[channel] [nick|userhost]" }, +#ifdef ALIAS +{ C_ALIAS, "[alias [replacement format]]" }, +#endif /* ALIAS */ +{ C_AWAY, "[message]" }, +{ C_BAN, "[channel] " }, +{ C_BANLIST, "[channel]" }, +#ifdef TOYBOX +{ C_BIGSAY, "[channel] " }, +#endif /* TOYBOX */ +{ C_BYE, NULL }, +{ C_CCHAN, "[channel]" }, +#ifdef DYNCMD +{ C_CHACCESS, " [DISABLE|level]" }, +#endif /* DYNCMD */ +{ C_CHANNELS, NULL }, +{ C_CHAT, NULL }, +{ C_CLEARSHIT, NULL }, +#if defined(BOTNET) && defined(REDIRECT) +{ C_CMD, "[guid] " }, +#endif /* BOTNET && REDIRECT */ +{ C_CORE, NULL }, +{ C_CSERV, NULL }, +#ifdef CTCP +{ C_CTCP, " " }, +#endif /* CTCP */ +{ C_CYCLE, "[channel]" }, +#ifdef DEBUG +{ C_DEBUG, NULL }, +#endif /* DEBUG */ +{ C_DEOP, "[channel] " }, +{ C_DIE, "[reason]" }, +#ifdef RAWDNS +{ C_DNS, "" }, +{ C_DNSSERVER, "[+|-serverip]" }, +#endif /* RAWDNS */ +{ C_DO, "" }, +{ C_DOWN, "[channel]" }, +{ C_ECHO, "" }, +{ C_ESAY, "[channel] " }, +{ C_FORGET, "" }, +#ifdef GREET +{ C_GREET, " [greeting|\"@\"greetfile|\"%\"greetfile]" }, +#endif /* GREET */ +{ C_HELP, "[topic|command|level|pattern]" }, +{ C_IDLE, "" }, +#ifdef TOYBOX +{ C_INSULT, "[channel|nick]" }, +#endif /* TOYBOX */ +{ C_INVITE, "[channel] [nick]" }, +{ C_JOIN, " [key]" }, +{ C_KB, "[channel] [reason]" }, +{ C_KICK, "[channel] [reason]" }, +{ C_KS, " [action] <\"pattern\"> " }, +{ C_LAST, "[number of commands]" }, +#ifdef BOTNET +{ C_LINK, "[[+|-]guid [pass [host port]]" }, +#endif /* BOTNET */ +{ C_LOAD, NULL }, +{ C_LUSERS, NULL }, +{ C_ME, "[channel] " }, +{ C_MODE, "[channel|botnick] " }, +{ C_MSG, " " }, +{ C_NAMES, "[channel]" }, +{ C_NEXTSERVER, NULL }, +{ C_NICK, "[guid] " }, +#ifdef NOTE +{ C_NOTE, "" }, +#endif /* NOTE */ +#ifdef NOTIFY +{ C_NOTIFY, "[options] [nick]" }, /* */ +#endif /* NOTIFY */ +{ C_ONTIME, NULL }, +{ C_OP, "[channel] [nick|mask]" }, +{ C_PART, "" }, +{ C_PASSWD, "[oldpassword] " }, +#ifdef TOYBOX +{ C_PICKUP, "[channel|nick]" }, +#endif /* TOYBOX */ +#ifdef CTCP +{ C_PING, "" }, +#endif /* CTCP */ +{ C_QSHIT, " [reason]" }, +#ifdef NOTE +{ C_READ, "[-]" }, +#endif /* NOTE */ +{ C_RESET, NULL }, +{ C_RKS, " " }, +#ifdef TOYBOX +{ C_RSAY, "[channel|nick]" }, /* */ +#endif /* TOYBOX */ +{ C_RSHIT, " " }, +{ C_RSPY, " [channel|\">\" filename]" }, +#ifdef TOYBOX +{ C_RT, "" }, +#endif /* TOYBOX */ +{ C_SAVE, NULL }, +{ C_SAY, " " }, +{ C_SCREW, "[channel] [reason]" }, +#ifdef SEEN +{ C_SEEN, "" }, +#endif /* SEEN */ +#ifdef DCC_FILE +{ C_SEND, "[to] " }, +#endif /* DCC_FILE */ +{ C_SERVER, "[[+|-]servername] [port] [pass]" }, +{ C_SERVERGROUP,"[identifier]" }, +{ C_SET, "[channel|*] [setting [value]]" }, +{ C_SETPASS, " " }, +{ C_SHIT, " [expire] " }, +{ C_SHITLIST, NULL }, +{ C_SHOWIDLE, "[channel] [seconds]" }, +{ C_SHUTDOWN, NULL }, +{ C_SITEBAN, "[channel] " }, +{ C_SITEKB, "[channel] [reason]" }, +{ C_SPY, "[STATUS|MESSAGE|RAWIRC|[guid\":\"|botnick\":\"] [channel|\">\" filename]" }, +{ C_STATS, " [servername]" }, +#ifdef TCL +#ifdef PLEASE_HACK_MY_SHELL +{ C_TCL, "" }, +#endif /* PLEASE_HACK_MY_SHELL */ +{ C_TCLSCRIPT, "" }, +#endif /* TCL */ +{ C_TIME, NULL }, +{ C_TOPIC, "[channel] " }, +#ifdef TRIVIA +{ C_TRIVIA, "[START|STOP|TOP10]" }, +#endif /* TRIVIA */ +#ifdef ALIAS +{ C_UNALIAS, "" }, +#endif /* ALIAS */ +{ C_UNBAN, "[channel] [nick|userhost]" }, +{ C_UNVOICE, "[channel] " }, +{ C_UP, "[channel]" }, +#ifdef UPTIME +{ C_UPSEND, NULL }, +#endif /* UPTIME */ +{ C_UPTIME, NULL }, +{ C_USAGE, "" }, +{ C_USER, "[+|-] [modifiers [...]]" }, +{ C_USERHOST, "" }, +{ C_USERLIST, "[+minlevel] [-maxlevel] [channel] [mask] [-B] [-C]" }, +{ C_VER, NULL }, +{ C_VERIFY, "" }, +{ C_VOICE, "[channel] [nick|pattern [...]]" }, +{ C_WALL, "[channel] " }, +{ C_WHO, " [-ops|-nonops] [pattern]" }, +{ C_WHOIS, "" }, +{ C_WHOM, NULL }, +{ NULL, }}; diff --git a/src/user.c b/src/user.c new file mode 100644 index 0000000..5c87485 --- /dev/null +++ b/src/user.c @@ -0,0 +1,1697 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define USERLIST_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" +#include "usercombo.h" + +/* + * + * reading and writing userlists + * + */ + +/* + * functions that handle userlist stuff (userlist_cmds[]) + */ + +void cfg_user(char *rest) +{ + set_mallocdoer(cfg_user); + cfgUser = Calloc(sizeof(User) + strlen(rest)); + Strcpy(cfgUser->name,rest); +} + +#ifdef BOTNET + +void cfg_modcount(char *rest) +{ + int i; + + i = a2i(rest); + if (errno || i < 1) + return; + cfgUser->modcount = i; +} + +#endif /* BOTNET */ + +void cfg_pass(char *rest) +{ + Free((char**)&cfgUser->pass); + set_mallocdoer(cfg_pass); + cfgUser->pass = Strdup(rest); +} + +void cfg_mask(char *rest) +{ + addtouser(&cfgUser->mask,rest); +} + +void cfg_chan(char *rest) +{ + addtouser(&cfgUser->chan,rest); +} + +LS struct +{ + char modechar; + int modeflag; + +} cfg_opt_flags[] = +{ +{ 'a', COMBO_AOP }, +#ifdef BOUNCE +{ 'b', COMBO_BOUNCE }, +#endif /* BOUNCE */ +{ 'e', COMBO_ECHO }, +#ifdef BOTNET +{ 'L', COMBO_NOSHARE }, +{ 'R', COMBO_READONLY }, +#endif /* BOTNET */ +#ifdef GREET +{ 'g', COMBO_GREETFILE }, +{ 'r', COMBO_RANDLINE }, +#endif /* GREET */ +{ 'v', COMBO_AVOICE }, +{ 0, 0, }}; + +void cfg_opt(char *rest) +{ + int i; + + /* + * `OPT abeLRgrp0vu100' + */ + while(*rest) + { + for(i=0;cfg_opt_flags[i].modechar;i++) + { + if (cfg_opt_flags[i].modechar == *rest) + cfgUser->x.comboflags |= cfg_opt_flags[i].modeflag; + } + if (*rest == 'p' && rest[1] >= '0' && rest[1] <= '4') + cfgUser->x.x.prot = rest[1] - '0'; + if (*rest == 'u') + { + cfgUser->x.x.access = a2i(rest+1); + return; + } + rest++; + } +} + +void cfg_shit(char *rest) +{ + char *channel,*mask,*from; + time_t backup_now,expire,when; + int shitlevel; + + channel = chop(&rest); + mask = chop(&rest); + from = chop(&rest); + + /* + * quick way of getting the shitlevel + * also, if channel, mask or from is NULL, this will fail (cuz *rest == 0) + */ + if (*rest < '0' || *rest > MAXSHITLEVELCHAR) + return; + shitlevel = *rest - '0'; + chop(&rest); /* discard shitlevel argument */ + + /* + * convert the expiry time + */ + expire = a2i(chop(&rest)); /* a2i() can handle NULLs */ + if (errno || expire < now) + return; + + /* + * convert time when the shit was added + */ + when = a2i(chop(&rest)); + if (errno || *rest == 0) /* if *rest == 0, the reason is missing */ + return; + + /* + * finally, add the sucker + */ + backup_now = now; + now = when; + add_shit(from,channel,mask,rest,shitlevel,expire); + now = backup_now; +} + +void cfg_kicksay(char *rest) +{ + Client *backup; + + backup = CurrentDCC; + CurrentDCC = (Client*)&CoreClient; + do_kicksay((char*)CoreUser.name,NULL,rest,0); + CurrentDCC = backup; +} + +#ifdef GREET + +void cfg_greet(char *rest) +{ + Free((char**)&cfgUser->greet); + set_mallocdoer(cfg_greet); + cfgUser->greet = Strdup(rest); +} + +#endif /* GREET */ + +#ifdef NOTE + +void cfg_note(char *rest) +{ + Strp *sp,**np; + + np = &cfgUser->note; + while(*np) + np = &(*np)->next; + *np = sp = Calloc(sizeof(Strp) + strlen(rest)); + /* Calloc sets to zero sp->next = NULL; */ + Strcpy(sp->p,rest); +} + +#endif /* NOTE */ + +void user_sync(void) +{ + User *user; + + user = add_user(cfgUser->name,cfgUser->pass,0); + /* memcpy copies all access and userflags */ + memcpy(&user->mask,&cfgUser->mask,((char*)&user->pass - (char*)&user->mask)); + +#ifdef BOTNET + user->tick = global_tick++; +#endif /* BOTNET */ + + Free((char**)&cfgUser->pass); + Free((char**)&cfgUser); +} + +#define FL_ONEARG 1 +#define FL_NEEDUSER 2 /* userfile */ +#define FL_NEWUSER 4 /* userfile */ + +typedef struct CommandStruct +{ + char *name; + void (*function)(char *); + int flags; + +} ConfCommand; + +LS const ConfCommand userlist_cmds[] = +{ +/* + * users + */ +{ "USER", cfg_user, FL_NEWUSER | FL_ONEARG }, +{ "PASS", cfg_pass, FL_NEEDUSER | FL_ONEARG }, +{ "MASK", cfg_mask, FL_NEEDUSER | FL_ONEARG }, +{ "CHAN", cfg_chan, FL_NEEDUSER | FL_ONEARG }, +{ "OPT", cfg_opt, FL_NEEDUSER }, +{ "SHIT", cfg_shit, 0 }, +{ "KICKSAY", cfg_kicksay, 0 }, +#ifdef GREET +{ "GREET", cfg_greet, FL_NEEDUSER }, +#endif /* GREET */ +#ifdef NOTE +{ "NOTE", cfg_note, FL_NEEDUSER }, +#endif /* NOTE */ +#ifdef BOTNET +{ "MODCOUNT", cfg_modcount, FL_NEEDUSER }, +#endif /* BOTNET */ +{ NULL, }}; + +int read_userlist_callback(char *line) +{ + char *command; + int i; + + fix_config_line(line); + command = chop(&line); + for(i=0;userlist_cmds[i].name;i++) + { + if (!Strcasecmp(command,userlist_cmds[i].name)) + break; + } + if (userlist_cmds[i].name) + { + if (!cfgUser && (userlist_cmds[i].flags & FL_NEEDUSER)) + { +#ifdef DEBUG + debug("[RUC] cfgUser is NULL for command that requires it to be set (%s)\n",command); +#endif /* DEBUG */ + return(FALSE); + } + if ((userlist_cmds[i].flags & FL_NEWUSER) && cfgUser) + user_sync(); + if (userlist_cmds[i].flags & FL_ONEARG) + { + if ((line = chop(&line)) == NULL) + return(FALSE); + } + userlist_cmds[i].function(line); + } + return(FALSE); +} + +int read_userlist(char *filename) +{ + User *user,*u2; + User *olduserlist; + User *newuserlist; + int in; + + if (!filename) + return(FALSE); + if ((in = open(filename,O_RDONLY)) < 0) + return(FALSE); + + olduserlist = current->userlist; + cfgUser = current->userlist = NULL; + + readline(in,&read_userlist_callback); /* readline closes in */ + + /* + * save the last user + */ + if (cfgUser) + user_sync(); + + newuserlist = current->userlist; + current->userlist = olduserlist; + + for(user=newuserlist;user;user=user->next) + { + u2 = find_handle(user->name); /* find user in old userlist, may be NULL */ + reset_userlink(u2,user); + } + + /* + * remove the old userlist + */ + while(current->userlist) + remove_user(current->userlist); + + /* + * re-apply shitlist that we just loaded + */ + check_shit(); + + current->userlist = newuserlist; + current->ul_save = 0; + return(TRUE); +} + +int write_userlist(char *filename) +{ + KickSay *ks; + Shit *shit; + Strp *ump; + User *user; + char *p,flags[7]; + int i,f; +#ifdef DEBUG + int dodeb; +#endif /* DEBUG */ + + if (!filename) + return(FALSE); + + if (!current->ul_save) + return(TRUE); + + if ((f = open(filename,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0) + return(FALSE); + + /* + * reset the change-counter + */ + current->ul_save = 0; + +#ifdef DEBUG + dodeb = dodebug; + dodebug = FALSE; +#endif /* DEBUG */ + + for(user=current->userlist;user;user=user->next) + { + to_file(f,"\nuser\t\t%s\n",user->name); + for(ump=user->mask;ump;ump=ump->next) + to_file(f,"mask\t\t%s\n",ump->p); + for(ump=user->chan;ump;ump=ump->next) + to_file(f,"chan\t\t%s\n",ump->p); + /* + * `OPT aegrp0vu100' + */ + p = flags; + for(i=0;cfg_opt_flags[i].modeflag;i++) + { + if (user->x.comboflags & cfg_opt_flags[i].modeflag) + *(p++) = cfg_opt_flags[i].modechar; + } + *p = 0; + to_file(f,"opt\t\t%sp%iu%i\n",flags,user->x.x.prot,user->x.x.access); + /* + * `PASS ' + */ + if (user->pass) + to_file(f,"pass\t\t%s\n",user->pass); +#ifdef GREET + if (user->greet) + to_file(f,"greet\t\t%s\n",user->greet); +#endif /* GREET */ +#ifdef NOTE + for(ump=user->note;ump;ump=ump->next) + to_file(f,"note\t\t%s\n",ump->p); +#endif /* NOTE */ +#ifdef BOTNET + to_file(f,"modcount\t%i\n",user->modcount); +#endif /* BOTNET */ + } + + to_file(f,"\n"); + + for(shit=current->shitlist;shit;shit=shit->next) + { + to_file(f,"shit\t\t%s %s %s %i %lu %lu %s\n", + shit->chan,shit->mask,shit->from,shit->action, + shit->expire,shit->time,shit->reason); + } + + to_file(f,"\n"); + + for(ks=current->kicklist;ks;ks=ks->next) + { + to_file(f,"kicksay\t\t%s %i \"%s\" %s\n",ks->chan,ks->action,ks->mask,ks->reason); + } + + close(f); + +#ifdef DEBUG + dodebug = dodeb; +#endif /* DEBUG */ + return(TRUE); +} + +/* + * adding and removing masks from user records + * rehash_chanusers() executed if mask is added or removed + * addtouser() add channel or mask to a user + * remfromuser() remove a channel or mask from a user + */ + +__page(CORE_SEG) +void rehash_chanusers(void) +{ + Chan *chan; + ChanUser *cu; + + for(chan=current->chanlist;chan;chan=chan->next) + { + for(cu=chan->users;cu;cu=cu->next) + cu->user = get_user(get_nuh(cu),chan->name); + } +} + +__attr(CORE_SEG, __regparm (2)) +void addtouser(Strp **pp, const char *string) +{ + Strp *um; + + while(*pp) + { + um = *pp; + if (!Strcasecmp(um->p,string)) + return; + pp = &um->next; + } + + set_mallocdoer(addtouser); + *pp = um = (Strp*)Calloc(sizeof(Strp) + strlen(string)); + Strcpy(um->p,string); + rehash_chanusers(); +} + +__attr(CORE_SEG, __regparm (2)) +int remfromuser(Strp **pp, const char *string) +{ + Strp *um; + + while(*pp) + { + um = *pp; + if (!Strcasecmp(um->p,string)) + { + *pp = um->next; + Free((char**)&um); + rehash_chanusers(); + return(TRUE); + } + pp = &um->next; + } + return(FALSE); +} + +/* + * duplicate a list of Strp + */ +void dupe_strp(Strp *sp, Strp **pp) +{ + while(sp) + { + set_mallocdoer(dupe_strp); + *pp = (Strp*)Calloc(sizeof(Strp) + strlen(sp->p)); + Strcpy((*pp)->p,sp->p); + pp = &((*pp)->next); + sp = sp->next; + } +} + +/* + * make duplicates of a user on other local bots + */ +void mirror_user(User *user) +{ + Mech *backup,*anybot; + User *newuser,*olduser; + Strp *notes; + +#ifdef BOTNET + /* dont mirror noshare users */ + if (user->x.x.noshare) + return; +#endif /* BOTNET */ + backup = current; + for(anybot=botlist;anybot;anybot=anybot->next) + { + if (anybot == backup) // dont try to copy to myself, bad things will happen + continue; + for(olduser=anybot->userlist;olduser;olduser=olduser->next) + { + if (!Strcasecmp(user->name,olduser->name)) + { +#ifdef BOTNET + /* dont overwrite "better" users */ + if (olduser->modcount >= user->modcount) + return; + /* dont overwrite read only users */ + if (olduser->x.x.readonly) + return; +#endif /* BOTNET */ + break; + } + } +#ifdef DEBUG + debug("(mirror_user) mirroring user %s[%i] to local bot %s(%i)\n", + user->name,user->x.x.access,nullstr(anybot->nick),anybot->guid); +#endif /* DEBUG */ + + current = anybot; + if (olduser) + { +#ifdef NOTE + /* hang on to notes */ + notes = olduser->note; + olduser->note = NULL; +#endif /* NOTE */ + remove_user(olduser); // uses current->userlist + // authlist/chanuserlist/dcclist is now a minefield + } + newuser = add_user(user->name,user->pass,user->x.x.access); // uses current->userlist + if (olduser) + { +#ifdef NOTE + newuser->note = notes; +#endif /* NOTE */ +#ifdef DEBUG + debug("(1)\n"); +#endif /* DEBUG */ + reset_userlink(olduser,newuser); // uses current->userlist + // authlist/chanuserlist/dcclist should now be safe again. + } +#ifdef DEBUG + debug("(2)\n"); +#endif /* DEBUG */ + dupe_strp(user->mask,&newuser->mask); // copy masks + dupe_strp(user->chan,&newuser->chan); // copy channels + // do not copy notes (creates spam) +#ifdef DEBUG + debug("(3)\n"); +#endif /* DEBUG */ + newuser->x.comboflags = user->x.comboflags; +#ifdef BOTNET + newuser->x.x.readonly = 0; // dont copy the RO flag + newuser->modcount = user->modcount; + newuser->tick = user->tick; // is this proper??? +#endif /* BOTNET */ + } + current = backup; // assume my old identity +#ifdef DEBUG + debug("(x)\n"); +#endif /* DEBUG */ +} + +void mirror_userlist(void) +{ + User *user; + +#ifdef DEBUG + debug("(mirror_userlist) mirroring userlist of %s(%i)\n",nullstr(current->nick),current->guid); +#endif /* DEBUG */ + + for(user=current->userlist;user;user=user->next) + { +#ifdef BOTNET + if (!user->x.x.noshare) +#endif /* BOTNET */ + mirror_user(user); + } +} +/* + * adding, removing, matching and searching for user records + */ + +void reset_userlink(User *old, User *new) +{ + Auth *auth,*nx_auth; + Chan *chan; + ChanUser *cu; + Client *client,*nx_client; + Spy *spy; + + /* + * auth list + */ + for(auth=current->authlist;auth;) + { + nx_auth = auth->next; + if (auth->user == old) + { + if (new) + auth->user = new; + else + remove_auth(auth); + } + auth = nx_auth; + } + + /* + * client list + */ + for(client=current->clientlist;client;) + { + nx_client = client->next; + if (client->user == old) + { + if (new) + client->user = new; + else + delete_client(client); + } + client = nx_client; + } + + /* + * spy list + */ + if (new) + { + for(spy=current->spylist;spy;spy=spy->next) + { + if (spy->dest == old->name) + spy->dest = new->name; + } + } + + /* + * channel userlists + */ + for(chan=current->chanlist;chan;chan=chan->next) + { + for(cu=chan->users;cu;cu=cu->next) + { + if (cu->user == old) + cu->user = new; + } + } +} + +void remove_user(User *user) +{ + User **pp; + Strp *ump,*nxt; + + pp = ¤t->userlist; + for(;(*pp);pp=&(*pp)->next) + { + if (*pp == user) + { + *pp = user->next; +#ifdef GREET + Free((char**)&user->greet); +#endif /* GREET */ + for(ump=user->mask;ump;) + { + nxt = ump->next; + Free((char**)&ump); + ump = nxt; + } +#ifdef NOTE + for(ump=user->note;ump;) + { + nxt = ump->next; + Free((char**)&ump); + ump = nxt; + } +#endif /* NOTE */ + Free((char**)&user); + current->ul_save++; + return; + } + } +} + +User *add_user(char *handle, char *pass, int axs) +{ + User *user; + char *p; + +#ifdef DEBUG + debug("(add_user) handle = %s; pass = %s; axs = %i\n", + nullstr(handle),nullstr(pass),axs); +#endif /* DEBUG */ + + set_mallocdoer(add_user); + user = (User*)Calloc(sizeof(User) + Strlen2(handle,pass)); + user->x.x.access = axs; + user->next = current->userlist; + current->userlist = user; + + /* + * "name\0pass\0" + */ + p = Strcpy(user->name,handle) + 1; + if (pass) + { + user->pass = p; + Strcpy(user->pass,pass); + } + current->ul_save++; + return(user); +} + +/* + * find the user record for a named handle + */ +User *find_handle(char *handle) +{ + User *user; + + for(user=current->userlist;user;user=user->next) + { + if (!Strcasecmp(handle,user->name)) + return(user); + } + return(NULL); +} + +int userhaschannel(const User *user, const char *channel) +{ + Strp *ump; + + if (!channel) + return(TRUE); + for(ump=user->chan;ump;ump=ump->next) + { + if (*ump->p == '*' || !Strcasecmp(ump->p,channel)) + return(TRUE); + } + return(FALSE); +} + +/* + * Find the user that best matches the userhost + */ +User *get_user(const char *userhost, const char *channel) +{ + Strp *ump; + User *user,*save; + int num,best; + + if (CurrentDCC && CurrentDCC->user->name == userhost) + { + if (userhaschannel(CurrentDCC->user,channel)) + return(CurrentDCC->user); + return(NULL); + } + + save = NULL; + best = 0; + for(user=current->userlist;user;user=user->next) + { + if (userhaschannel(user,channel)) + { + for(ump=user->mask;ump;ump=ump->next) + { + num = num_matches(ump->p,userhost); + if (num > best) + { + best = num; + save = user; + } + } + } + } + return(save); +} + +/* + * highest channel on a given channel + */ +int get_useraccess(char *userhost, char *channel) +{ + User *user; + + if (is_bot(userhost)) + return(BOTLEVEL); + + if ((user = get_user(userhost,channel)) == NULL) + return(0); + return(user->x.x.access); +} + +/* + * highest access regardless of channel + */ +int get_maxaccess(const char *userhost) +{ + Strp *ump; + User *user; + int uaccess; + + if (CurrentDCC && CurrentDCC->user->name == userhost) + return(CurrentDCC->user->x.x.access); + + if (is_bot(userhost)) + return(BOTLEVEL); + + uaccess = 0; + for(user=current->userlist;user;user=user->next) + { + for(ump=user->mask;ump;ump=ump->next) + { + if (!matches(ump->p,userhost)) + uaccess = (user->x.x.access > uaccess) ? user->x.x.access : uaccess; + } + } + return(uaccess); +} + +int is_bot(const char *userhost) +{ + Mech *bot; + + for(bot=botlist;bot;bot=bot->next) + { + if (!nickcmp(userhost,bot->nick)) + return(TRUE); + } + return(FALSE); +} + +/* + * FIXME: does this apply to local bots? + */ +int get_protaction(Chan *chan, char *userhost) +{ + Strp *ump; + User *user; + int prot; + + prot = 0; + for(user=current->userlist;user;user=user->next) + { + if (userhaschannel(user,chan->name)) + { + for(ump=user->mask;ump;ump=ump->next) + { + if (!matches(userhost,ump->p)) + { + if (user->x.x.prot > prot) + prot = user->x.x.prot; + break; + } + } + } + } + return(prot); +} + +__attr(CORE_SEG, __regparm (2)) +int usercanmodify(const char *from, const User *user) +{ + User *u; + Strp *fmp,*ump; + int ua; + + ua = (user->x.x.access >= 100) ? 100 : user->x.x.access; + + for(u=current->userlist;u;u=u->next) + { + if (!CurrentDCC || CurrentDCC->user != u) + { + for(fmp=u->mask;fmp;fmp=fmp->next) + { + if (!matches(fmp->p,from)) + break; + } + if (!fmp) + continue; + } + for(fmp=u->chan;fmp;fmp=fmp->next) + { + if (*fmp->p == '*' && u->x.x.access >= ua) + return(TRUE); + for(ump=user->chan;ump;ump=ump->next) + { + if (!Strcasecmp(ump->p,fmp->p)) + { + if (u->x.x.access >= ua) + return(TRUE); + } + } + } + } + return(FALSE); +} + +/* + * + * user commands related to userlist + * + */ + +void do_access(COMMAND_ARGS) +{ + char *chan,*nuh; + int level; + + chan = get_channel(to,&rest); + if (rest && ((nuh = chop(&rest)))) + { + if (*nuh == current->setting[CHR_CMDCHAR].char_var) + { + nuh++; + level = access_needed(nuh); + if (level < 0) + return; + else + if (level > 200) + to_user(from,"The command \"%s\" has been disabled",nuh); + else + to_user(from,"The access level needed for \"%s\" is: %i",nuh,level); + return; + } + if ((nuh = nick2uh(from,nuh)) == NULL) + return; + } + else + { + nuh = from; + } + to_user(from,"Immortality Level for %s",nuh); + to_user(from,"Channel: %s Access: %i",chan,get_useraccess(nuh,chan)); +} + +#ifdef BOTNET +#define BOTNET_FMT "%s/%s/" +#else +#define BOTNET_FMT /* nothing */ +#endif /* BOTNET */ + +#ifdef BOUNCE +#define BOUNCE_FMT "%s/" +#else +#define BOUNCE_FMT /* nothing */ +#endif /* BOUNCE */ + +char *userlist_outputfmt[4] = +{ + "Chan%s\t: %s", + "%s\t %s", + "Mask%s\t: %s", + "%s\t %s" +}; + +void do_userlist(COMMAND_ARGS) +{ + Strp *ump; + User *user; + char *channel,*mask,*extra; + int minlevel = 0; + int maxlevel = BOTLEVEL; + int chanonly = FALSE; + int count; + int n; +#ifdef NOTE + int sz; +#endif /* NOTE */ + + mask = channel = NULL; + count = 0; + + if (*rest) + { + if (*rest == '+' && rest[1] >= '0' && rest[1] <= '9') + { + minlevel = a2i(rest+1); + } + else + if (*rest == '-' && rest[1] >= '0' && rest[1] <= '9') + { + maxlevel = a2i(rest+1); + } + else + if (*rest == '-' && (*rest|32) == 'b') + { + minlevel = BOTLEVEL; + } + else + if (*rest == '-' && (*rest|32) == 'c') + { + chanonly = TRUE; + } + else + if (ischannel(rest)) + { + channel = rest; + } + else + if (STRCHR(rest,'*') != NULL) + { + mask = rest; + } + else + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + } + +#ifdef DEBUG + debug("(do_userlist) mask=%s minlevel=%i maxlevel=%i chanonly=%s\n", + (mask) ? mask : "NOMASK",minlevel,maxlevel,(chanonly) ? "Yes" : "No"); +#endif /* DEBUG */ + + for(user=current->userlist;user;user=user->next) + { + if (user->x.x.access < minlevel) + continue; + if (user->x.x.access > maxlevel) + continue; + if (channel && (userhaschannel(user,channel) == FALSE)) + continue; + if (chanonly && userhaschannel(user,MATCH_ALL) == TRUE) + continue; + ump = NULL; + if (mask) + { + for(ump=user->mask;ump;ump=ump->next) + { + if (matches(mask,ump->p)) + break; + } + } + if (ump) + continue; + + table_buffer("User\t: %-11s [%3i/%s/%s/" BOTNET_FMT BOUNCE_FMT "%s/P%i]", + user->name,user->x.x.access, + (user->x.x.aop) ? "AO" : "--", + (user->x.x.avoice) ? "AV" : "--", +#ifdef BOTNET + (user->x.x.readonly) ? "RO" : "--", + (user->x.x.noshare) ? "NS" : "--", +#endif /* BOTNET */ +#ifdef BOUNCE + (user->x.x.bounce) ? "BNC" : "---", +#endif /* BOUNCE */ + (user->pass) ? "PW" : "--", + user->x.x.prot); + + n = 0; + ump = user->chan; + extra = NULL; + while(ump) + { + if (!extra) + extra = (ump->next) ? "s" : ""; + table_buffer(userlist_outputfmt[n],extra,ump->p); + extra = ""; + n |= 1; + ump = ump->next; + if (!ump && n < 2) + { + n = 2; + extra = NULL; + ump = user->mask; + } + } +#ifdef GREET + if (user->greet) + { + table_buffer("Greet\t: %s%s",user->greet, + (user->x.x.greetfile) ? " (greetfile)" : + ((user->x.x.randline) ? " (random line from file)" : "")); + } +#endif /* GREET */ +#ifdef NOTE + if ((ump = user->note)) + { + sz = n = 0; + for(;ump;ump=ump->next) + { + if (*ump->p == 1) + n++; + else + sz += strlen(ump->p); + } + table_buffer(from,"Message\t: %i message%s (%i bytes)",n,(n == 1) ? "" : "s",sz); + } +#endif /* NOTE */ + table_buffer(" "); + count++; + } + table_send(from,0); + to_user(from,"Total of %i users",count); +} + +#define _ADD '+' +#define _SUB '-' + +void do_user(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + Mech *anybot; + User *user; + Strp *ump; + char *handle,*pt,*mask,*nick,*chan,*anum,*pass,*encpass; + char mode; + int change; + int newaccess,uaccess; + union usercombo combo; + int ch,ho; + + mode = 0; + mask = NULL; + + /* + * chop wont return NULL (on_msg checks CARGS) + */ + handle = chop(&rest); + if (*handle == '-' || *handle == '+') + { + if (handle[1] == 0) + { + mode = *handle; + handle = chop(&rest); + } + else + { + mode = *handle; + handle++; + } + } + + if (!handle) + goto usage; + user = find_handle(handle); + + if (mode == '+') + { + /* + * Usage: USER + [password] + */ + /* + * dont create duplicate handles + */ + if (user) + { + to_user(from,"User \"%s\" already exists",handle); + return; + } + chan = chop(&rest); + nick = chop(&rest); + anum = chop(&rest); + pass = chop(&rest); + + newaccess = a2i(anum); + if (errno) + goto usage; + + if (*chan == '*') + chan = MATCH_ALL; + else + if (!ischannel(chan)) + goto usage; + + if ((uaccess = get_useraccess(from,chan)) < cmdaccess) + return; + /* + * check access level + */ + if ((newaccess != BOTLEVEL) && ((newaccess < 0) || (newaccess > OWNERLEVEL))) + { + to_user(from,"access must be in the range 0 - %i",OWNERLEVEL); + return; + } + if ((uaccess != OWNERLEVEL) && (newaccess > uaccess)) + { + to_user(from,"access denied on %s",chan); + return; + } + /* + * convert and check nick/mask + */ + if ((mask = nick2uh(from,nick)) == NULL) + return; +#ifdef NEWBIE + if (!matches(mask,"!@")) + { + to_user(from,"Problem adding %s (global mask)",mask); + return; + } + if (matches("*@?*.?*",mask)) + { + to_user(from,"Problem adding %s (invalid mask)",mask); + return; + } +#endif /* NEWBIE */ + format_uh(mask,FUH_USERHOST); + /* + * dont duplicate users + */ + if (get_useraccess(mask,chan)) + { + to_user(from,"%s (%s) on %s is already a user",nick,mask,chan); + return; + } + /* + * encrypt password + */ + encpass = (pass) ? makepass(pass) : NULL; + /* + * passwords for bots are never used + */ + if (newaccess == BOTLEVEL) + encpass = NULL; + + /* + * add_user() touches current->ul_save for us + */ + user = add_user(handle,encpass,newaccess); + addtouser(&user->mask,mask); + addtouser(&user->chan,chan); + to_user(from,"%s has been added as %s on %s",handle,mask,chan); + to_user(from,"Access level: %i%s%s",newaccess,(pass) ? " Password: " : "",(pass) ? pass : ""); +#ifdef NEWUSER_SPAM + if ((newaccess != BOTLEVEL) && find_nuh(nick)) + { + /* + * yay! its a nick! we can spam them! + */ + char cmdchar = current->setting[CHR_CMDCHAR].char_var; + + to_server("NOTICE %s :%s has blessed you with %i levels of immortality\n", + nick,CurrentNick,newaccess); + to_server("NOTICE %s :My command character is %c\n",nick,cmdchar); + to_server("NOTICE %s :Use \026%c%s\026 for command help\n",nick,cmdchar,C_HELP); + if (encpass) + { + to_server("NOTICE %s :Password necessary for doing commands: %s\n",nick,pass); + to_server("NOTICE %s :If you do not like your password, use \026%c%s\026 to change it\n", + nick,cmdchar,C_PASSWD); + } + } +#endif /* NEWUSER_SPAM */ + return; + } + + if (!user) + { + to_user(from,TEXT_UNKNOWNUSER,handle); + return; + } + if (!usercanmodify(from,user)) + { + to_user(from,TEXT_USEROWNSYOU,user->name); + return; + } + + /* + * ".user - handle" passed access tests, remove it. + */ + if (mode == '-') + { + to_user(from,"User %s has been purged",handle); +#ifdef BOTNET + botnet_relay(NULL,"UD%i %s\n",user->modcount,user->name); +#endif /* BOTNET */ + /* + * delete all references to the user record + */ + reset_userlink(user,NULL); + /* + * remove_user() touches current->ul_save for us + */ + remove_user(user); + return; + } + + combo.comboflags = user->x.comboflags; + change = 0; + ch = ho = 0; + + while((pt = chop(&rest))) + { + switch(*pt++) + { + case '+': + mode = TRUE; + break; + case '-': + mode = FALSE; + break; + default: + goto usage; + } + +#ifdef BOTNET + if (!Strcasecmp(pt,"NS")) + combo.x.noshare = mode; + else + if (!Strcasecmp(pt,"RO")) + combo.x.readonly = mode; + else +#endif /* BOTNET */ +#ifdef BOUNCE + if (!Strcasecmp(pt,"BNC")) + combo.x.bounce = mode; + else +#endif /* BOUNCE */ + if (!Strcasecmp(pt,"AV")) + combo.x.avoice = mode; + else + if (!Strcasecmp(pt,"AO")) + combo.x.aop = mode; + else + if (!Strcasecmp(pt,"ECHO")) + combo.x.echo = mode; + else + if (!Strcasecmp(pt,"CHAN")) + { + ch = (mode) ? _ADD : _SUB; + mask = chop(&rest); + if (!mask || *rest) /* mask needs to be the last option */ + goto usage; + } + else + if (!Strcasecmp(pt,"HOST")) + { + ho = (mode) ? _ADD : _SUB; + mask = chop(&rest); + if (!mask || *rest) /* mask needs to be the last option */ + goto usage; + } + else + if (*pt == 'p' || *pt == 'P') + { + pt++; + if ((*pt >= '0') && (*pt <= '4') && (pt[1] == 0)) + { + combo.x.prot = *pt - '0'; + } + else + if ((*pt == 0) && (mode == _SUB)) + { + combo.x.prot = 0; + } + else + goto usage; + } + else + if ((uaccess = a2i(pt)) >= 0 && uaccess <= BOTLEVEL && errno == 0) + { + combo.x.access = uaccess; + } + else + goto usage; + + change++; + } + if (!change) + { +usage: + usage(from); /* usage for CurrentCmd->name */ + return; + } + change = 0; + + /* + * make the actual changes + */ + if (ch == _SUB && remfromuser(&user->chan,mask)) + { + change++; + } + else + if (ch == _ADD) + { + for(ump=user->chan;ump;ump=ump->next) + { + if (!Strcasecmp(ump->p,mask)) + { + to_user(from,"Channel %s already exists for %s",mask,user->name); + return; + } + } +#ifdef NEWBIE + if (!ischannel(mask) && *mask != '*') + { + to_user(from,TEXT_CHANINVALID); + return; + } +#endif /* NEWBIE */ + addtouser(&user->chan,mask); + change++; + } + else + if (ho == _SUB && remfromuser(&user->mask,mask)) + { + change++; + } + else + if (ho == _ADD) + { + for(ump=user->mask;ump;ump=ump->next) + { + if (!Strcasecmp(ump->p,mask)) + { + to_user(from,"Mask %s already exists for %s",mask,user->name); + return; + } + } +#ifdef NEWBIE + /* + * newbies dont know what they're doing + */ + if (!matches(mask,"!@")) + { + to_user(from,"Problem adding %s (global mask)",mask); + return; + } + if (matches("*@?*.?*",mask)) + { + to_user(from,"Problem adding %s (invalid mask)",mask); + return; + } +#endif /* NEWBIE */ + addtouser(&user->mask,mask); + change++; + } + + if (combo.comboflags != user->x.comboflags) + { + user->x.comboflags = combo.comboflags; + change++; + } + if (change) + { + to_user(from,TEXT_USERCHANGED,user->name); + current->ul_save++; +#ifdef BOTNET + user->modcount++; + user->tick = global_tick++; +#endif /* BOTNET */ + mirror_user(user); + } + else + { + to_user(from,TEXT_USERNOTCHANGED,user->name); + } +} + +void do_echo(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + char *tmp; + + if ((tmp = chop(&rest))) + { + if (!Strcasecmp(tmp,"on")) + { + if (CurrentUser->x.x.echo == FALSE) + { + current->ul_save++; + CurrentUser->x.x.echo = TRUE; + mirror_user(CurrentUser); + } + to_user(from,TEXT_PARTYECHOON); + return; + } + if (!Strcasecmp(tmp,"off")) + { + if (CurrentUser->x.x.echo == TRUE) + { + current->ul_save++; + CurrentUser->x.x.echo = FALSE; + mirror_user(CurrentUser); + } + to_user(from,TEXT_PARTYECHOOFF); + return; + } + } + usage(from); /* usage for CurrentCmd->name */ +} + + +void change_pass(User *user, char *pass) +{ + User *new,**uptr; + + /* + * password is stuck in a solid malloc in a linked list + * add_user() touches current->ul_save for us + */ + new = add_user(user->name,makepass(pass),0); + memcpy(&new->mask,&user->mask,((char*)&user->pass - (char*)&user->mask)); +#ifdef BOTNET + new->modcount++; + new->tick = global_tick++; +#endif /* BOTNET */ + + /* + * unlink the old user record + */ + uptr = &new->next; + while(*uptr) + { + if (*uptr == user) + { + *uptr = user->next; + break; + } + uptr = &(*uptr)->next; + } + reset_userlink(user,new); + Free((char**)&user); + mirror_user(new); +} + +void do_passwd(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + User *user; + char *pass,*savedpass; + + if ((user = get_user(from,NULL)) == NULL) + return; + + savedpass = pass = chop(&rest); + if (user->pass) + { + pass = chop(&rest); + if (!pass || !*pass) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + } + if (strlen(pass) < MINPASSCHARS) + { + to_user(from,TEXT_PASS_SHORT); + return; + } + if (strlen(pass) > MAXPASSCHARS) + { + to_user(from,TEXT_PASS_LONG); + return; + } + if (user->pass && !passmatch(savedpass,user->pass)) + { + to_user(from,TEXT_PASS_INCORRECT); + return; + } + to_user(from,TEXT_PASS_NEWSET); + /* + * all is well + * change_pass() -> add_user() and current->ul_save is touched + */ + change_pass(user,pass); +} + +void do_setpass(COMMAND_ARGS) +{ + /* + * on_msg checks: CARGS + */ + User *user; + char *nick,*pass; + + nick = chop(&rest); + pass = chop(&rest); + + if (!nick || !pass) + { + usage(from); /* usage for CurrentCmd->name */ + return; + } + if (strlen(pass) < MINPASSCHARS) + { + to_user(from,"password must be at least %i characters long",MINPASSCHARS); + return; + } + if (strlen(pass) >= MAXPASSCHARS) + pass[MAXPASSCHARS] = 0; + if ((user = find_handle(nick)) == NULL) + { + to_user(from,TEXT_UNKNOWNUSER,nick); + return; + } + if (!usercanmodify(from,user)) + { + to_user(from,TEXT_USEROWNSYOU,user->name); + return; + } + if (!Strcasecmp(pass,"none")) + { + user->pass = NULL; + to_user(from,"password for %s has been removed",user->name); + return; + } + to_user(from,"new password for %s has been set",user->name); + /* + * all is well + * change_pass() -> add_user() and current->ul_save is groped + */ + change_pass(user,pass); +} + +void do_save(COMMAND_ARGS) +{ + char *fname; + + fname = current->setting[STR_USERFILE].str_var; + current->ul_save = 1; + if (!write_userlist(fname)) + { + to_user(from,(fname) ? ERR_NOSAVE : ERR_NOUSERFILENAME,fname); + } + else + { + to_user(from,TEXT_LISTSAVED,fname); + } +#ifdef SEEN + if (seenfile && !write_seenlist()) + { + to_user(from,TEXT_SEENNOSAVE,seenfile); + } +#endif /* SEEN */ +#ifdef NOTIFY + if (current->notifylist) + write_notifylog(); +#endif /* NOTIFY */ +#ifdef TRIVIA + write_triviascore(); +#endif /* TRIVIA */ +} + +void do_load(COMMAND_ARGS) +{ + /* + * on_msg checks: GAXS + */ + char *fname; + + fname = current->setting[STR_USERFILE].str_var; + if (!read_userlist(fname)) + { + purge_shitlist(); + purge_kicklist(); + to_user(from,(fname) ? ERR_NOREAD : ERR_NOUSERFILENAME,fname); + } + else + { + to_user(from,TEXT_LISTREAD,fname); + } +#ifdef SEEN + if (seenfile && !read_seenlist()) + { + to_user(TEXT_SEENNOLOAD,seenfile); + } +#endif /* SEEN */ +#ifdef TRIVIA + read_triviascore(); +#endif /* TRIVIA */ + mirror_userlist(); +} diff --git a/src/vars.c b/src/vars.c new file mode 100644 index 0000000..2152241 --- /dev/null +++ b/src/vars.c @@ -0,0 +1,558 @@ +/* + + EnergyMech, IRC bot software + Parts Copyright (c) 1997-2009 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define VARS_C +#include "config.h" + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" +#include "mcmd.h" +#include "settings.h" + +void set_str_varc(Chan *channel, int which, char *value) +{ + char *temp,**dst; + + if (value && *value) + { + set_mallocdoer(set_str_varc); + temp = Strdup(value); + } + else + temp = NULL; + dst = (channel) ? (char**)&channel->setting[which] : (char**)¤t->setting[which]; + if (*dst) + Free(dst); + *dst = temp; +} + +/* + * The rest + */ +int find_setting(char *name) +{ + int i; + + for(i=0;VarName[i].name;i++) + { + if (!Strcasecmp(name,VarName[i].name)) + return(i); + } + return(-1); +} + +void copy_vars(UniVar *dst, UniVar *src) +{ + int i; + + for(i=0;iactivechan) ? current->activechan->name : TEXT_NONE); +} + +void ec_channels(char *from, char *to) +{ + Chan *chan; + int n; + + if ((chan = current->chanlist) == NULL) + { + nobo_strcpy(ERR_NOCHANNELS); + return; + } + for(n=0;chan;chan=chan->next) + { + if (chan->active) + { + if (n && ec_dest < ec_end) + *(ec_dest++) = ' '; + if (chan->bot_is_op && ec_dest < ec_end) + *(ec_dest++) = '@'; + nobo_strcpy(chan->name); + } + } +} + +void ec_time(char *from, char *to) +{ + nobo_strcpy(time2away(now)); +} + +void ec_set(char *from, char *to) +{ + Chan *chan; + UniVar *varval; + char num[20]; + char *src; + int which,uaccess; + + src = ec_src; + while(*src && *src != ')') + src++; + if (*src != ')') + return; + *(src++) = 0; + + if ((which = find_setting(ec_src)) >= 0) + { + if (which >= CHANSET_SIZE) + { + uaccess = get_useraccess(from,ANY_CHANNEL); + varval = ¤t->setting[which]; + } + else + if ((chan = find_channel_ny(to))) + { + uaccess = get_useraccess(from,to); + varval = &chan->setting[which]; + } + else + { + nobo_strcpy("(unknown channel)"); + return; + } + if (uaccess < VarName[which].uaccess) + return; + + if (IsProc(which)) + varval = varval->proc_var; + + if (IsChar(which)) + { + num[0] = varval->char_var; + num[1] = 0; + nobo_strcpy(num); + } + else + if (IsInt(which)) + { + sprintf(num,"%i",varval->int_var); + nobo_strcpy(num); + } + else + if (IsTog(which)) + { + nobo_strcpy((varval->int_var) ? "on" : "off"); + } + else + if (IsStr(which)) + { + nobo_strcpy(nullstr(varval->str_var)); + } + } + else + { + nobo_strcpy("(unknown setting)"); + } + ec_src = src; +} + +void ec_on(char *from, char *to) +{ + nobo_strcpy(idle2str(now - current->ontime,FALSE)); +} + +void ec_server(char *from, char *to) +{ + Server *sv; + char *s; + + if ((sv = find_server(current->server))) + s = (*sv->realname) ? sv->realname : sv->name; + else + s = TEXT_NOTINSERVLIST; + nobo_strcpy(s); +} + +void ec_up(char *from, char *to) +{ + nobo_strcpy(idle2str(now - uptime,FALSE)); +} + +void ec_ver(char *from, char *to) +{ + nobo_strcpy(BOTCLASS); + nobo_strcpy(" "); + nobo_strcpy(VERSION); +} + +LS const struct +{ + void (*func)(char *, char *); + char name[12]; + char len; + +} ecmd[] = +{ + { ec_access, "$access", 7 }, + { ec_cc, "$cc", 3 }, + { ec_channels, "$channels", 9 }, + { ec_time, "$time", 5 }, + { ec_set, "$var(", 5 }, + { ec_on, "$on", 3 }, + { ec_server, "$server", 7 }, + { ec_up, "$up", 3 }, + { ec_ver, "$ver", 4 }, + { NULL, "", 0 }, +}; + +/* + * + * commands for variables + * + */ +void do_esay(COMMAND_ARGS) +{ + /* + * on_msg checks CAXS + CARGS + */ + char output[MSGLEN]; + char c,*chp; + int i,n; + + ec_end = output + MSGLEN - 20; + ec_src = rest; + rest = STREND(rest); + ec_dest = output; + c = 0; + chp = NULL; + + while(*ec_src) + { + if (*ec_src != '$') + { + *(ec_dest++) = *(ec_src++); + continue; + } + for(i=0;ecmd[i].len;i++) + { + if ((rest - ec_src) >= ecmd[i].len) + { + chp = ec_src + ecmd[i].len; + c = *chp; + *chp = 0; + } + n = Strcasecmp(ecmd[i].name,ec_src); + if (c) + { + *chp = c; + c = 0; + } + if (!n) + { + ec_src += ecmd[i].len; + ecmd[i].func(from,to); + break; + } + } + if (!ecmd[i].len) + { + *(ec_dest++) = *(ec_src++); + } + } + *ec_dest = 0; + to_user_q(from,FMT_PLAIN,output); +} + +void do_set(COMMAND_ARGS) +{ + /* + * on_msg checks: + */ + Chan *chan; + UniVar *univar,*varval; + char tmp[MSGLEN]; + char *pp,*channel,*name; + int n,which,i,sz,limit,uaccess; + + /* + * + */ + channel = get_channel2(to,&rest); + chan = find_channel_ny(channel); + name = chop(&rest); + + /* + * empty args, its "set" or "set #channel" + */ + if (!name) + { + if (!chan) + { + to_user(from,ERR_CHAN,channel); + return; + } + + if (!CurrentDCC) + return; + + i = CHANSET_SIZE; + limit = SIZE_VARS - 1; + univar = current->setting; + *tmp = 0; + if ((uaccess = get_useraccess(from,GLOBAL_CHANNEL))) + to_user(from,str_underline("Global settings")); +second_pass: + for(;isetting[i].proc_var : &univar[i]; + + sz = Strlen2(tmp,VarName[i].name); + + if (IsStr(i)) + { + sz += (varval->str_var) ? strlen(varval->str_var) : 7; + } + + if (sz > 58) + { + to_user(from,FMT_PLAIN,tmp); + *tmp = 0; + } + + if (IsInt(i)) + { + pp = tolowercat(tmp,VarName[i].name); + sprintf(pp,(IsChar(i)) ? "=`%c' " : "=%i ",varval->int_var); + } + else + if (IsStr(i)) + { + pp = tolowercat(tmp,VarName[i].name); + sprintf(pp,(varval->str_var) ? "=\"%s\" " : "=(unset) ",varval->str_var); + } + else + if (IsTog(i)) + { + pp = Strcat(tmp,(varval->int_var) ? "+" : "-"); + pp = tolowercat(pp,VarName[i].name); + pp[0] = ' '; + pp[1] = 0; + } + } + if (*tmp && tmp[1]) + to_user(from,FMT_PLAIN,tmp); + + if (limit != CHANSET_SIZE) + { + to_user(from,"\037Channel settings: %s\037",(chan) ? chan->name : rest); + i = 0; + limit = CHANSET_SIZE; + univar = chan->setting; + *tmp = 0; + uaccess = get_useraccess(from,(chan) ? chan->name : rest); + goto second_pass; + } + return; + } + + /* + * alter a setting + */ + if ((which = find_setting(name)) == -1) + { +set_usage: + usage(from); /* usage for CurrentCmd->name */ + return; + } + + if ((which < CHANSET_SIZE) && *channel != '*') + { + if (!chan) + { + to_user(from,ERR_CHAN,channel); + return; + } + /* + * its a channel setting + */ + channel = chan->name; + varval = &chan->setting[which]; + } + else + { + /* + * its a global setting + */ + channel = MATCH_ALL; + varval = ¤t->setting[which]; + } + + if (VarName[which].uaccess > get_authaccess(from,channel)) + return; + + /* + * Check each type and process `rest' if needed. + */ + n = 0; + if (IsChar(which)) + { + if (rest[1]) + goto set_usage; + } + else + if (IsNum(which)) + { + if (IsTog(which)) + { + if (!Strcasecmp(rest,"ON")) + { + n = 1; + goto num_data_ok; + } + else + if (!Strcasecmp(rest,"OFF")) + { + /* n is 0 by default */ + goto num_data_ok; + } + } + n = a2i((rest = chop(&rest))); + if (errno || n < VarName[which].min || n > VarName[which].max) + { + to_user(from,"Possible values are %i through %i",VarName[which].min,VarName[which].max); + return; + } + } +num_data_ok: + /* + * + */ + if ((which < CHANSET_SIZE) && *channel == '*') + { + for(chan=current->chanlist;chan;chan=chan->next) + { + if (IsNum(which)) + { + chan->setting[which].int_var = n; + } + else + if (IsStr(which)) + { + Free((char**)&chan->setting[which].str_var); + if (*rest) + { + set_mallocdoer(do_set); + chan->setting[which].str_var = Strdup(rest); + } + } + } + channel = "(all channels)"; + } + else + { + if (IsProc(which)) + varval = varval->proc_var; + + if (IsChar(which)) + varval->char_var = *rest; + else + if (IsNum(which)) + varval->int_var = n; + else + { + if (varval->str_var) + Free((char**)&varval->str_var); + if (*rest) + { + set_mallocdoer(do_set); + varval->str_var = Strdup(rest); + } + } + } + to_user(from,"Var: %s On: %s Set to: %s",VarName[which].name, + (which >= CHANSET_SIZE) ? "(global)" : channel,(*rest) ? rest : NULLSTR); + if (VarName[which].func) + VarName[which].func(&VarName[which]); +} diff --git a/src/web.c b/src/web.c new file mode 100644 index 0000000..6fce63a --- /dev/null +++ b/src/web.c @@ -0,0 +1,526 @@ +/* + + EnergyMech, IRC bot software + Copyright (c) 1997-2001 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define WEB_C +#include "config.h" + +#ifdef WEB + +#include "defines.h" +#include "structs.h" +#include "global.h" +#include "h.h" +#include "text.h" + +#define WEB_DEAD 0 +#define WEB_WAITURL 1 +#define WEB_WAITCR 2 + +#define WEBROOT "web/" + +LS WebSock *weblist; + +LS WebDoc docraw = { NULL, NULL, &web_raw }; + +#if 0 +{ + ":last20lines>" +}; +#endif + +LS WebDoc doclist[] = +{ +{ NULL, "/internalstatus.html", &web_botstatus }, +#ifdef DEBUG +{ NULL, "/internaldebug.html", &web_debug }, +#endif /* DEBUG */ +/**/ +{ NULL, NULL, NULL }, +}; + +char *webread(int s, char *rest, char *line) +{ + char tmp[MSGLEN]; + char *pt,*tp,*np; + int l; + + np = NULL; + pt = rest; + while(*pt) + { + if ((*pt == '\r') && (!np)) + np = pt; + if (*pt == '\n') + { + if (np) + *np = 0; + *pt = 0; + Strcpy(line,rest); + pt++; + Strcpy(rest,pt); +#ifdef DEBUG + debug("[WoR] {%i} `%s'\n",s,line); +#endif /* DEBUG */ + errno = EAGAIN; + return(line); + } + pt++; + } + l = pt - rest; + tp = pt; + memset(tmp,0,sizeof(tmp)); + switch(read(s,tmp,MSGLEN-2)) + { + case 0: + errno = EPIPE; + case -1: + return(NULL); + } + np = NULL; + pt = tmp; + while(*pt) + { + if ((*pt == '\r') && (!np)) + np = tp; + if (*pt == '\n') + { + if (np) + *np = 0; + *tp = *pt = 0; + Strcpy(line,rest); + pt++; + Strcpy(rest,pt); +#ifdef DEBUG + debug("[WoR] {%i} `%s'\n",s,line); +#endif /* DEBUG */ + errno = EAGAIN; + return(line); + } + if (l >= MSGLEN-2) + { + pt++; + continue; + } + *(tp++) = *(pt++); + l++; + } + *tp = 0; + return(NULL); +} + +#define NOBO if (dest == &mem[MSGLEN-2]) { write(s,mem,dest-mem); dest = mem; } + +void eml_fmt(WebSock *client, char *format) +{ + char mem[MSGLEN]; + char *src,*dest,*org; + int out; + int s = client->sock; + + org = NULL; + out = TRUE; + dest = mem; +restart: + while(*format) + { + if (*format != '%') + { + if (out) + { + NOBO; + *(dest++) = *format; + } + format++; + goto restart; + } + format++; + switch(*format) + { + case '%': + NOBO; + *(dest++) = *(format++); + goto restart; + case 'B': + client->ebot = botlist; + break; + case 'C': + client->echan = (client->ebot) ? client->ebot->chanlist : NULL; + break; + case 'b': + src = (client->ebot) ? client->ebot->nick : (char*)NULLSTR; + while(*src) + { + NOBO; + *(dest++) = *(src++); + } + break; + case 'O': +#ifdef DEBUG + debug("> setting org\n"); +#endif /* DEBUG */ + org = format+1; + break; + case 'R': + if (out && org) + { +#ifdef DEBUG + debug("> repeat jump\n"); +#endif /* DEBUG */ + format = org; + goto restart; + } + break; + case '?': + format++; + switch(*format) + { + case 'b': + out = (client->ebot) ? 1 : 0; +#ifdef DEBUG + debug("> ?b: out = %i\n",out); +#endif /* DEBUG */ + break; + case 'c': + out = (client->echan) ? 1 : 0; +#ifdef DEBUG + debug("> ?c: out = %i\n",out); +#endif /* DEBUG */ + break; + case '1': + out = 1; + break; + } + break; + case '+': + format++; + switch(*format) + { + case 'b': + client->ebot = (client->ebot) ? client->ebot->next : NULL; +#ifdef DEBUG + debug("> +b: ebot = %p\n",client->ebot); +#endif /* DEBUG */ + break; + case 'c': + client->echan = (client->echan) ? client->echan->next : NULL; +#ifdef DEBUG + debug("> +c: echan = %p\n",client->echan); +#endif /* DEBUG */ + break; + } + break; + } + format++; + } + if (dest-mem > 0) + write(s,mem,dest-mem); + write(s,"\r\n",2); +} + +void web_raw(WebSock *client, char *url) +{ + struct stat s; + char path[MSGLEN]; + char *src,*dest; + ino_t ino; + int fd; + int eml; + size_t sz; + + eml = (matches("*.html",url)) ? TRUE : FALSE; + + if (stat(WEBROOT "..",&s) < 0) + goto error; + ino = s.st_ino; + + dest = Strcpy(path,WEBROOT); + src = url; + if (*src == '/') + src++; + while(*src) + { + if (*src == '/') + { + *dest = 0; + if (stat(path,&s) < 0) + { +#ifdef DEBUG + debug("(web_raw) unable to stat `%s'\n",path); +#endif /* DEBUG */ + goto error; + } + if (s.st_ino == ino) + { +#ifdef DEBUG + debug("(web_raw) attempt to access docs outside WEBROOT (%s)\n",url); +#endif /* DEBUG */ + goto error; + } + } + if (dest == &path[MSGLEN-2]) + goto error; + *(dest++) = *(src++); + } + *dest = 0; + if ((fd = open(path,O_RDONLY)) < 0) + { +#ifdef DEBUG + debug("(web_raw) open failed = `%s'\n",path); +#endif /* DEBUG */ + goto error; + } + sz = lseek(fd,0,SEEK_END); + lseek(fd,0,SEEK_SET); +#ifdef DEBUG + debug("(web_raw) file open for reading `%s'\n",path); +#endif /* DEBUG */ + src = Calloc(sz+1); + read(fd,src,sz); + if (eml) + { + to_file(client->sock,"%s\r\n",src); + } + else + { + eml_fmt(client,src); + } + Free((char**)&src); + close(fd); + return; +error: + web_404(client,url); +} + +void web_botstatus(WebSock *client, char *url) +{ + Server *sp; + Mech *bot; + Chan *chan; + + to_file(client->sock,"HTTP/1.0 200 OK\r\n"); + to_file(client->sock,"Server: %s %s\r\n",BOTCLASS,VERSION); + to_file(client->sock,"Connection: close\r\n"); + to_file(client->sock,"Content-Type: text/html\r\n\r\n"); + + bot = botlist; + + to_file(client->sock, + "EnergyMech Status\r\n" + "\r\n" + "

EnergyMech Status

\r\n" + "Uptime: %s

\r\n" + "Current bots:
\r\n", + idle2str(now - uptime,FALSE)); + + for(;bot;bot=bot->next) + { + sp = find_server(bot->server); + to_file(client->sock, + "Nick: %s (want %s)
\r\n" + "On server: %s
\r\n", + bot->nick,bot->wantnick, + (sp) ? ((sp->realname[0]) ? sp->realname : sp->name) : TEXT_NOTINSERVLIST); + to_file(client->sock,"Channels: "); + for(chan=bot->chanlist;chan;chan=chan->next) + to_file(client->sock,"%s ",chan->name); + to_file(client->sock,"

\r\n"); + } + to_file(client->sock,"\r\n"); +} + +#ifdef DEBUG + +void web_debug(WebSock *client, char *url) +{ + int backup_fd,backup_dodebug; + + backup_dodebug = dodebug; + backup_fd = debug_fd; + + to_file(client->sock,"HTTP/1.0 200 OK\r\n"); + to_file(client->sock,"Server: %s %s\r\n",BOTCLASS,VERSION); + to_file(client->sock,"Connection: close\r\n"); + to_file(client->sock,"Content-Type: text/html\r\n\r\n"); + + to_file(client->sock,"
\r\n");
+
+	debug_fd = client->sock;
+	dodebug = TRUE;
+
+	run_debug();
+
+	debug_fd = backup_fd;
+	dodebug = backup_dodebug;
+}
+
+#endif /* DEBUG */
+
+void web_404(WebSock *client, char *url)
+{
+	to_file(client->sock,"HTTP/1.0 404 Not Found\r\n");
+	to_file(client->sock,"Server: %s %s\r\n",BOTCLASS,VERSION);
+	to_file(client->sock,"Connection: close\r\n");
+	to_file(client->sock,"Content-Type: text/html\r\n\r\n");
+
+	to_file(client->sock,
+		"\r\n"
+		""
+		"404 Not Found"
+		""
+		"

Not Found

" + "The requested URL %s was not found on this server.

" + "", + url); +} + +void parse(WebSock *client, char *rest) +{ + char *method,*url,*proto; + int i; + + switch(client->status) + { + case WEB_WAITURL: + method = chop(&rest); + url = chop(&rest); + proto = chop(&rest); + if (!method || !proto || Strcasecmp(method,"GET")) + { + client->status = WEB_DEAD; + return; + } + client->docptr = &docraw; + for(i=0;doclist[i].proc;i++) + { + if (!Strcasecmp(doclist[i].url,url)) + { + client->docptr = &doclist[i]; + break; + } + } + client->url = Strdup(url); + client->status = WEB_WAITCR; + break; + case WEB_WAITCR: + if (*rest == 0) + { +#ifdef DEBUG + debug("(web.c->parse) web document output\n"); +#endif /* DEBUG */ + if (client->docptr) + client->docptr->proc(client,client->url); + client->status = WEB_DEAD; + } + break; + } +} + +void select_web(void) +{ + WebSock *client; + + if ((webport > 0) && (websock == -1)) + { + websock = SockListener(webport); +#ifdef DEBUG + if (websock != -1) + debug("(web_select) websock is active (%i)\n",webport); +#endif /* DEBUG */ + } + + if (websock != -1) + { + chkhigh(websock); + FD_SET(websock,&read_fds); + } + + for(client=weblist;client;client=client->next) + { + chkhigh(client->sock); + FD_SET(client->sock,&read_fds); + } +} + +void process_web(void) +{ + WebSock *client,*new,**cptr; + char linebuf[MSGLEN]; + char *rest; + int s; + + if ((websock != -1) && (FD_ISSET(websock,&read_fds))) + { + if ((s = SockAccept(websock)) >= 0) + { + new = (WebSock*)Calloc(sizeof(WebSock)); + new->sock = s; + new->status = WEB_WAITURL; + new->when = now; + new->next = weblist; + weblist = new; + } + } + + for(client=weblist;client;client=client->next) + { + if (FD_ISSET(client->sock,&read_fds)) + { +read_again: + errno = EAGAIN; + rest = webread(client->sock,client->sockdata,linebuf); + if (rest) + { + parse(client,rest); + goto read_again; + } + switch(errno) + { + case EINTR: + case EAGAIN: + break; + default: +#ifdef DEBUG + debug("(web_process) client error = %i\n",errno); +#endif /* DEBUG */ + client->status = WEB_DEAD; + } + } + } + cptr = &weblist; + while(*cptr) + { + client = *cptr; + if (client->status == WEB_DEAD) + { +#ifdef DEBUG + debug("(web_process) {%i} dropping client\n",client->sock); +#endif /* DEBUG */ + close(client->sock); + *cptr = client->next; + if (client->url) + Free((char**)&client->url); + Free((char**)&client); + } + else + cptr = &client->next; + } +} + +#endif /* WEB */ diff --git a/trivia/mkindex.c b/trivia/mkindex.c new file mode 100644 index 0000000..ae43f2e --- /dev/null +++ b/trivia/mkindex.c @@ -0,0 +1,114 @@ +/* + + mkindex, create a binary index file of line offsets and lengths + Copyright (c) 2001 proton + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char ixname[1000]; + char *map; + char *p,*beg,*end; + off_t sz; + int fd,ifd; + int n; + + if (argc < 2) + { + exit(1); + } + + if ((fd = open(argv[1],O_RDONLY)) < 0) + { + fprintf(stderr,"open: %s:",argv[1]); + perror(""); + exit(1); + } + + strcpy(ixname,argv[1]); + if ((p = strchr(ixname,'.')) == NULL) + p = strchr(ixname,0); + + strcpy(p,".index"); + + if (!strcmp(ixname,argv[1])) + { + fprintf(stderr,"input file is output file!\n"); + exit(1); + } + + if ((ifd = open(ixname,O_WRONLY|O_TRUNC|O_CREAT,0600)) < 0) + { + fprintf(stderr,"open: %s:",ixname); + perror(""); + exit(1); + } + + sz = lseek(fd,0,SEEK_END); + + map = mmap(NULL,sz,PROT_READ,MAP_SHARED,fd,0); + if (!map) + { + perror("mmap: "); + exit(1); + } + + end = map + sz; + beg = p = map; + + n = 0; + while(p < end) + { + if ((*p == '\n') || (*p == '\r')) + { + if (p != beg) + { + struct + { + int off; + int sz; + + } entry; + + entry.off = beg - map; + entry.sz = p - beg; + + if (entry.sz < 450) + { + write(ifd,&entry,sizeof(entry)); + n++; + } + } + beg = p + 1; + } + p++; + } + + close(fd); + close(ifd); + + printf("%i entries indexed\n",n); + exit(0); +}